1 /* 2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 */ 8 9 10 #include "Volume.h" 11 12 #include <errno.h> 13 #include <stdlib.h> 14 #include <sys/stat.h> 15 #include <time.h> 16 #include <unistd.h> 17 18 #include <Directory.h> 19 #include <Entry.h> 20 #include <File.h> 21 #include <Looper.h> 22 #include <MessageRunner.h> 23 #include <NodeMonitor.h> 24 #include <Path.h> 25 #include <Roster.h> 26 #include <SymLink.h> 27 28 #include <package/CommitTransactionResult.h> 29 #include <package/PackageRoster.h> 30 #include <package/solver/Solver.h> 31 #include <package/solver/SolverPackage.h> 32 #include <package/solver/SolverProblem.h> 33 #include <package/solver/SolverProblemSolution.h> 34 #include <package/solver/SolverRepository.h> 35 #include <package/solver/SolverResult.h> 36 37 #include <AutoDeleter.h> 38 #include <AutoLocker.h> 39 #include <NotOwningEntryRef.h> 40 #include <package/DaemonDefs.h> 41 #include <RosterPrivate.h> 42 43 #include "CommitTransactionHandler.h" 44 #include "Constants.h" 45 #include "DebugSupport.h" 46 #include "Exception.h" 47 #include "PackageFileManager.h" 48 #include "Root.h" 49 #include "VolumeState.h" 50 51 52 using namespace BPackageKit::BPrivate; 53 54 55 // #pragma mark - Listener 56 57 58 Volume::Listener::~Listener() 59 { 60 } 61 62 63 // #pragma mark - NodeMonitorEvent 64 65 66 struct Volume::NodeMonitorEvent 67 : public DoublyLinkedListLinkImpl<NodeMonitorEvent> { 68 public: 69 NodeMonitorEvent(const BString& entryName, bool created) 70 : 71 fEntryName(entryName), 72 fCreated(created) 73 { 74 } 75 76 const BString& EntryName() const 77 { 78 return fEntryName; 79 } 80 81 bool WasCreated() const 82 { 83 return fCreated; 84 } 85 86 private: 87 BString fEntryName; 88 bool fCreated; 89 }; 90 91 92 // #pragma mark - PackagesDirectory 93 94 95 struct Volume::PackagesDirectory { 96 public: 97 PackagesDirectory() 98 : 99 fNodeRef(), 100 fName() 101 { 102 } 103 104 void Init(const node_ref& nodeRef, bool isPackagesDir) 105 { 106 fNodeRef = nodeRef; 107 108 if (isPackagesDir) 109 return; 110 111 BDirectory directory; 112 BEntry entry; 113 if (directory.SetTo(&fNodeRef) == B_OK 114 && directory.GetEntry(&entry) == B_OK) { 115 fName = entry.Name(); 116 } 117 118 if (fName.IsEmpty()) 119 fName = "unknown state"; 120 } 121 122 const node_ref& NodeRef() const 123 { 124 return fNodeRef; 125 } 126 127 const BString& Name() const 128 { 129 return fName; 130 } 131 132 private: 133 node_ref fNodeRef; 134 BString fName; 135 }; 136 137 138 // #pragma mark - Volume 139 140 141 Volume::Volume(BLooper* looper) 142 : 143 BHandler(), 144 fPath(), 145 fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM), 146 fRootDirectoryRef(), 147 fPackagesDirectories(NULL), 148 fPackagesDirectoryCount(0), 149 fRoot(NULL), 150 fListener(NULL), 151 fPackageFileManager(NULL), 152 fLatestState(NULL), 153 fActiveState(NULL), 154 fChangeCount(0), 155 fLock("volume"), 156 fPendingNodeMonitorEventsLock("pending node monitor events"), 157 fPendingNodeMonitorEvents(), 158 fNodeMonitorEventHandleTime(0), 159 fPackagesToBeActivated(), 160 fPackagesToBeDeactivated(), 161 fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY), 162 fPendingPackageJobCount(0) 163 { 164 looper->AddHandler(this); 165 } 166 167 168 Volume::~Volume() 169 { 170 Unmounted(); 171 // needed for error case in InitPackages() 172 173 _SetLatestState(NULL, true); 174 175 delete[] fPackagesDirectories; 176 delete fPackageFileManager; 177 } 178 179 180 status_t 181 Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef) 182 { 183 status_t error = fLock.InitCheck(); 184 if (error != B_OK) 185 return error; 186 187 error = fPendingNodeMonitorEventsLock.InitCheck(); 188 if (error != B_OK) 189 return error; 190 191 fLatestState = new(std::nothrow) VolumeState; 192 if (fLatestState == NULL || !fLatestState->Init()) 193 RETURN_ERROR(B_NO_MEMORY); 194 195 fPackageFileManager = new(std::nothrow) PackageFileManager(fLock); 196 if (fPackageFileManager == NULL) 197 RETURN_ERROR(B_NO_MEMORY); 198 199 error = fPackageFileManager->Init(); 200 if (error != B_OK) 201 RETURN_ERROR(error); 202 203 fRootDirectoryRef = rootDirectoryRef; 204 205 // open the root directory 206 BDirectory directory; 207 error = directory.SetTo(&fRootDirectoryRef); 208 if (error != B_OK) { 209 ERROR("Volume::Init(): failed to open root directory: %s\n", 210 strerror(error)); 211 RETURN_ERROR(error); 212 } 213 214 // get the directory path 215 BEntry entry; 216 error = directory.GetEntry(&entry); 217 218 BPath path; 219 if (error == B_OK) 220 error = entry.GetPath(&path); 221 222 if (error != B_OK) { 223 ERROR("Volume::Init(): failed to get root directory path: %s\n", 224 strerror(error)); 225 RETURN_ERROR(error); 226 } 227 228 fPath = path.Path(); 229 if (fPath.IsEmpty()) 230 RETURN_ERROR(B_NO_MEMORY); 231 232 // get a volume info from the FS 233 int fd = directory.Dup(); 234 if (fd < 0) { 235 ERROR("Volume::Init(): failed to get root directory FD: %s\n", 236 strerror(fd)); 237 RETURN_ERROR(fd); 238 } 239 FileDescriptorCloser fdCloser(fd); 240 241 // get the volume info from packagefs 242 uint32 maxPackagesDirCount = 16; 243 PackageFSVolumeInfo* info = NULL; 244 MemoryDeleter infoDeleter; 245 size_t bufferSize; 246 for (;;) { 247 bufferSize = sizeof(PackageFSVolumeInfo) 248 + (maxPackagesDirCount - 1) * sizeof(PackageFSDirectoryInfo); 249 info = (PackageFSVolumeInfo*)malloc(bufferSize); 250 if (info == NULL) 251 RETURN_ERROR(B_NO_MEMORY); 252 infoDeleter.SetTo(info); 253 254 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_VOLUME_INFO, info, 255 bufferSize) != 0) { 256 ERROR("Volume::Init(): failed to get volume info: %s\n", 257 strerror(errno)); 258 RETURN_ERROR(errno); 259 } 260 261 if (info->packagesDirectoryCount <= maxPackagesDirCount) 262 break; 263 264 maxPackagesDirCount = info->packagesDirectoryCount; 265 infoDeleter.Unset(); 266 } 267 268 if (info->packagesDirectoryCount < 1) { 269 ERROR("Volume::Init(): got invalid volume info from packagefs\n"); 270 RETURN_ERROR(B_BAD_VALUE); 271 } 272 273 fMountType = info->mountType; 274 275 fPackagesDirectories = new(std::nothrow) PackagesDirectory[ 276 info->packagesDirectoryCount]; 277 if (fPackagesDirectories == NULL) 278 RETURN_ERROR(B_NO_MEMORY); 279 280 fPackagesDirectoryCount = info->packagesDirectoryCount; 281 282 for (uint32 i = 0; i < info->packagesDirectoryCount; i++) { 283 fPackagesDirectories[i].Init( 284 node_ref(info->packagesDirectoryInfos[i].deviceID, 285 info->packagesDirectoryInfos[i].nodeID), 286 i == 0); 287 } 288 289 _packageRootRef.device = info->rootDeviceID; 290 _packageRootRef.node = info->rootDirectoryID; 291 292 return B_OK; 293 } 294 295 296 status_t 297 Volume::InitPackages(Listener* listener) 298 { 299 // node-monitor the volume's packages directory 300 status_t error = watch_node(&PackagesDirectoryRef(), B_WATCH_DIRECTORY, 301 BMessenger(this)); 302 if (error == B_OK) { 303 fListener = listener; 304 } else { 305 ERROR("Volume::InitPackages(): failed to start watching the packages " 306 "directory of the volume at \"%s\": %s\n", 307 fPath.String(), strerror(error)); 308 // Not good, but not fatal. Only the manual package operations in the 309 // packages directory won't work correctly. 310 } 311 312 // read the packages directory and get the active packages 313 int fd = OpenRootDirectory(); 314 if (fd < 0) { 315 ERROR("Volume::InitPackages(): failed to open root directory: %s\n", 316 strerror(fd)); 317 RETURN_ERROR(fd); 318 } 319 FileDescriptorCloser fdCloser(fd); 320 321 error = _ReadPackagesDirectory(); 322 if (error != B_OK) 323 RETURN_ERROR(error); 324 325 error = _InitLatestState(); 326 if (error != B_OK) 327 RETURN_ERROR(error); 328 329 error = _GetActivePackages(fd); 330 if (error != B_OK) 331 RETURN_ERROR(error); 332 333 // create the admin directory, if it doesn't exist yet 334 BDirectory packagesDirectory; 335 bool createdAdminDirectory = false; 336 if (packagesDirectory.SetTo(&PackagesDirectoryRef()) == B_OK) { 337 if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists()) { 338 packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL); 339 createdAdminDirectory = true; 340 } 341 } 342 BDirectory adminDirectory(&packagesDirectory, kAdminDirectoryName); 343 error = adminDirectory.InitCheck(); 344 if (error != B_OK) 345 RETURN_ERROR(error); 346 347 // First boot processing requested by a magic file left by the OS installer? 348 BEntry flagFileEntry(&adminDirectory, kFirstBootProcessingNeededFileName); 349 if (createdAdminDirectory || flagFileEntry.Exists()) { 350 INFORM("Volume::InitPackages Requesting delayed first boot processing " 351 "for packages dir %s.\n", BPath(&packagesDirectory).Path()); 352 if (flagFileEntry.Exists()) 353 flagFileEntry.Remove(); // Remove early on to avoid an error loop. 354 355 // Are there any packages needing processing? Don't want to create an 356 // empty transaction directory and then never have it cleaned up when 357 // the empty transaction gets rejected. 358 bool anyPackages = false; 359 for (PackageNodeRefHashTable::Iterator it = 360 fActiveState->ByNodeRefIterator(); it.HasNext();) { 361 Package* package = it.Next(); 362 if (package->IsActive()) { 363 anyPackages = true; 364 break; 365 } 366 } 367 368 if (anyPackages) { 369 // Create first boot processing special transaction for current 370 // volume, which also creates an empty transaction directory. 371 BPackageInstallationLocation location = Location(); 372 BDirectory transactionDirectory; 373 BActivationTransaction transaction; 374 error = CreateTransaction(location, transaction, 375 transactionDirectory); 376 if (error != B_OK) 377 RETURN_ERROR(error); 378 379 // Add all package files in currently active state to transaction. 380 for (PackageNodeRefHashTable::Iterator it = 381 fActiveState->ByNodeRefIterator(); it.HasNext();) { 382 Package* package = it.Next(); 383 if (package->IsActive()) { 384 if (!transaction.AddPackageToActivate( 385 package->FileName().String())) 386 RETURN_ERROR(B_NO_MEMORY); 387 } 388 } 389 transaction.SetFirstBootProcessing(true); 390 391 // Queue up the transaction as a BMessage for processing a bit 392 // later, once the package daemon has finished initialising. 393 BMessage commitMessage(B_MESSAGE_COMMIT_TRANSACTION); 394 error = transaction.Archive(&commitMessage); 395 if (error != B_OK) 396 RETURN_ERROR(error); 397 BLooper *myLooper = Looper() ; 398 if (myLooper == NULL) 399 RETURN_ERROR(B_NOT_INITIALIZED); 400 error = myLooper->PostMessage(&commitMessage); 401 if (error != B_OK) 402 RETURN_ERROR(error); 403 } 404 } 405 406 return B_OK; 407 } 408 409 410 status_t 411 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly) 412 { 413 for (PackageFileNameHashTable::Iterator it 414 = fLatestState->ByFileNameIterator(); it.HasNext();) { 415 Package* package = it.Next(); 416 if (activeOnly && !package->IsActive()) 417 continue; 418 419 status_t error = repository.AddPackage(package->Info()); 420 if (error != B_OK) { 421 ERROR("Volume::AddPackagesToRepository(): failed to add package %s " 422 "to repository: %s\n", package->FileName().String(), 423 strerror(error)); 424 return error; 425 } 426 } 427 428 return B_OK; 429 } 430 431 432 void 433 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume) 434 { 435 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume); 436 // create the solver 437 BSolver* solver; 438 status_t error = BSolver::Create(solver); 439 if (error != B_OK) { 440 ERROR("Volume::InitialVerify(): failed to create solver: %s\n", 441 strerror(error)); 442 return; 443 } 444 ObjectDeleter<BSolver> solverDeleter(solver); 445 446 // add a repository with all active packages 447 BSolverRepository repository; 448 error = _AddRepository(solver, repository, true, true); 449 if (error != B_OK) { 450 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 451 strerror(error)); 452 return; 453 } 454 455 // add a repository for the next volume 456 BSolverRepository nextRepository; 457 if (nextVolume != NULL) { 458 nextRepository.SetPriority(1); 459 error = nextVolume->_AddRepository(solver, nextRepository, true, false); 460 if (error != B_OK) { 461 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 462 strerror(error)); 463 return; 464 } 465 } 466 467 // add a repository for the next next volume 468 BSolverRepository nextNextRepository; 469 if (nextNextVolume != NULL) { 470 nextNextRepository.SetPriority(2); 471 error = nextNextVolume->_AddRepository(solver, nextNextRepository, true, 472 false); 473 if (error != B_OK) { 474 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 475 strerror(error)); 476 return; 477 } 478 } 479 480 // verify 481 error = solver->VerifyInstallation(); 482 if (error != B_OK) { 483 ERROR("Volume::InitialVerify(): failed to verify: %s\n", 484 strerror(error)); 485 return; 486 } 487 488 if (!solver->HasProblems()) { 489 INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n", 490 Path().String()); 491 return; 492 } 493 494 // print the problems 495 // TODO: Notify the user ... 496 INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n", 497 Path().String()); 498 499 int32 problemCount = solver->CountProblems(); 500 for (int32 i = 0; i < problemCount; i++) { 501 BSolverProblem* problem = solver->ProblemAt(i); 502 INFORM(" %" B_PRId32 ": %s\n", i + 1, problem->ToString().String()); 503 int32 solutionCount = problem->CountSolutions(); 504 for (int32 k = 0; k < solutionCount; k++) { 505 const BSolverProblemSolution* solution = problem->SolutionAt(k); 506 INFORM(" solution %" B_PRId32 ":\n", k + 1); 507 int32 elementCount = solution->CountElements(); 508 for (int32 l = 0; l < elementCount; l++) { 509 const BSolverProblemSolutionElement* element 510 = solution->ElementAt(l); 511 INFORM(" - %s\n", element->ToString().String()); 512 } 513 } 514 } 515 } 516 517 518 void 519 Volume::HandleGetLocationInfoRequest(BMessage* message) 520 { 521 AutoLocker<BLocker> locker(fLock); 522 523 // If the cached reply message is up-to-date, just send it. 524 int64 changeCount; 525 if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK 526 && changeCount == fChangeCount) { 527 locker.Unlock(); 528 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 529 kCommunicationTimeout); 530 return; 531 } 532 533 // rebuild the reply message 534 fLocationInfoReply.MakeEmpty(); 535 536 if (fLocationInfoReply.AddInt32("base directory device", 537 fRootDirectoryRef.device) != B_OK 538 || fLocationInfoReply.AddInt64("base directory node", 539 fRootDirectoryRef.node) != B_OK 540 || fLocationInfoReply.AddInt32("packages directory device", 541 PackagesDeviceID()) != B_OK 542 || fLocationInfoReply.AddInt64("packages directory node", 543 PackagesDirectoryID()) != B_OK) { 544 return; 545 } 546 547 for (PackageFileNameHashTable::Iterator it 548 = fLatestState->ByFileNameIterator(); it.HasNext();) { 549 Package* package = it.Next(); 550 const char* fieldName = package->IsActive() 551 ? "latest active packages" : "latest inactive packages"; 552 BMessage packageArchive; 553 if (package->Info().Archive(&packageArchive) != B_OK 554 || fLocationInfoReply.AddMessage(fieldName, &packageArchive) 555 != B_OK) { 556 return; 557 } 558 } 559 560 if (fActiveState != fLatestState) { 561 if (fPackagesDirectoryCount > 1) { 562 fLocationInfoReply.AddString("old state", 563 fPackagesDirectories[fPackagesDirectoryCount - 1].Name()); 564 } 565 566 for (PackageFileNameHashTable::Iterator it 567 = fActiveState->ByFileNameIterator(); it.HasNext();) { 568 Package* package = it.Next(); 569 if (!package->IsActive()) 570 continue; 571 572 BMessage packageArchive; 573 if (package->Info().Archive(&packageArchive) != B_OK 574 || fLocationInfoReply.AddMessage("currently active packages", 575 &packageArchive) != B_OK) { 576 return; 577 } 578 } 579 } 580 581 if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK) 582 return; 583 584 locker.Unlock(); 585 586 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 587 kCommunicationTimeout); 588 } 589 590 591 void 592 Volume::HandleCommitTransactionRequest(BMessage* message) 593 { 594 BCommitTransactionResult result; 595 PackageSet dummy; 596 _CommitTransaction(message, NULL, dummy, dummy, result); 597 598 BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); 599 status_t error = result.AddToMessage(reply); 600 if (error != B_OK) { 601 ERROR("Volume::HandleCommitTransactionRequest(): Failed to add " 602 "transaction result to reply: %s\n", strerror(error)); 603 return; 604 } 605 606 message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout); 607 } 608 609 610 void 611 Volume::PackageJobPending() 612 { 613 atomic_add(&fPendingPackageJobCount, 1); 614 } 615 616 617 void 618 Volume::PackageJobFinished() 619 { 620 atomic_add(&fPendingPackageJobCount, -1); 621 } 622 623 624 bool 625 Volume::IsPackageJobPending() const 626 { 627 return fPendingPackageJobCount != 0; 628 } 629 630 631 void 632 Volume::Unmounted() 633 { 634 if (fListener != NULL) { 635 stop_watching(BMessenger(this)); 636 fListener = NULL; 637 } 638 639 if (BLooper* looper = Looper()) 640 looper->RemoveHandler(this); 641 } 642 643 644 void 645 Volume::MessageReceived(BMessage* message) 646 { 647 switch (message->what) { 648 case B_NODE_MONITOR: 649 { 650 int32 opcode; 651 if (message->FindInt32("opcode", &opcode) != B_OK) 652 break; 653 654 switch (opcode) { 655 case B_ENTRY_CREATED: 656 _HandleEntryCreatedOrRemoved(message, true); 657 break; 658 case B_ENTRY_REMOVED: 659 _HandleEntryCreatedOrRemoved(message, false); 660 break; 661 case B_ENTRY_MOVED: 662 _HandleEntryMoved(message); 663 break; 664 default: 665 break; 666 } 667 break; 668 } 669 670 case kHandleNodeMonitorEvents: 671 if (fListener != NULL) { 672 if (system_time() >= fNodeMonitorEventHandleTime) 673 fListener->VolumeNodeMonitorEventOccurred(this); 674 } 675 break; 676 677 default: 678 BHandler::MessageReceived(message); 679 break; 680 } 681 } 682 683 684 BPackageInstallationLocation 685 Volume::Location() const 686 { 687 switch (fMountType) { 688 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 689 return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM; 690 case PACKAGE_FS_MOUNT_TYPE_HOME: 691 return B_PACKAGE_INSTALLATION_LOCATION_HOME; 692 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 693 default: 694 return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT; 695 } 696 } 697 698 699 const node_ref& 700 Volume::PackagesDirectoryRef() const 701 { 702 return fPackagesDirectories[0].NodeRef(); 703 } 704 705 706 PackageFileNameHashTable::Iterator 707 Volume::PackagesByFileNameIterator() const 708 { 709 return fLatestState->ByFileNameIterator(); 710 } 711 712 713 int 714 Volume::OpenRootDirectory() const 715 { 716 BDirectory directory; 717 status_t error = directory.SetTo(&fRootDirectoryRef); 718 if (error != B_OK) { 719 ERROR("Volume::OpenRootDirectory(): failed to open root directory: " 720 "%s\n", strerror(error)); 721 RETURN_ERROR(error); 722 } 723 724 return directory.Dup(); 725 } 726 727 728 void 729 Volume::ProcessPendingNodeMonitorEvents() 730 { 731 // get the events 732 NodeMonitorEventList events; 733 { 734 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 735 events.MoveFrom(&fPendingNodeMonitorEvents); 736 } 737 738 // process them 739 while (NodeMonitorEvent* event = events.RemoveHead()) { 740 ObjectDeleter<NodeMonitorEvent> eventDeleter(event); 741 if (event->WasCreated()) 742 _PackagesEntryCreated(event->EntryName()); 743 else 744 _PackagesEntryRemoved(event->EntryName()); 745 } 746 } 747 748 749 bool 750 Volume::HasPendingPackageActivationChanges() const 751 { 752 return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty(); 753 } 754 755 756 void 757 Volume::ProcessPendingPackageActivationChanges() 758 { 759 if (!HasPendingPackageActivationChanges()) 760 return; 761 762 // perform the request 763 BCommitTransactionResult result; 764 _CommitTransaction(NULL, NULL, fPackagesToBeActivated, 765 fPackagesToBeDeactivated, result); 766 767 if (result.Error() != B_TRANSACTION_OK) { 768 ERROR("Volume::ProcessPendingPackageActivationChanges(): package " 769 "activation failed: %s\n", result.FullErrorMessage().String()); 770 // TODO: Notify the user! 771 } 772 773 // clear the activation/deactivation sets in any event 774 fPackagesToBeActivated.clear(); 775 fPackagesToBeDeactivated.clear(); 776 } 777 778 779 void 780 Volume::ClearPackageActivationChanges() 781 { 782 fPackagesToBeActivated.clear(); 783 fPackagesToBeDeactivated.clear(); 784 } 785 786 787 status_t 788 Volume::CreateTransaction(BPackageInstallationLocation location, 789 BActivationTransaction& _transaction, BDirectory& _transactionDirectory) 790 { 791 // open admin directory 792 BDirectory adminDirectory; 793 status_t error = _OpenPackagesSubDirectory( 794 RelativePath(kAdminDirectoryName), true, adminDirectory); 795 if (error != B_OK) 796 return error; 797 798 // create a transaction directory 799 int uniqueId = 1; 800 BString directoryName; 801 for (;; uniqueId++) { 802 directoryName.SetToFormat("transaction-%d", uniqueId); 803 if (directoryName.IsEmpty()) 804 return B_NO_MEMORY; 805 806 error = adminDirectory.CreateDirectory(directoryName, 807 &_transactionDirectory); 808 if (error == B_OK) 809 break; 810 if (error != B_FILE_EXISTS) 811 return error; 812 } 813 814 // init the transaction 815 error = _transaction.SetTo(location, fChangeCount, directoryName); 816 if (error != B_OK) { 817 BEntry entry; 818 _transactionDirectory.GetEntry(&entry); 819 _transactionDirectory.Unset(); 820 if (entry.InitCheck() == B_OK) 821 entry.Remove(); 822 return error; 823 } 824 825 return B_OK; 826 } 827 828 829 void 830 Volume::CommitTransaction(const BActivationTransaction& transaction, 831 const PackageSet& packagesAlreadyAdded, 832 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) 833 { 834 _CommitTransaction(NULL, &transaction, packagesAlreadyAdded, 835 packagesAlreadyRemoved, _result); 836 } 837 838 839 void 840 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created) 841 { 842 // only moves to or from our packages directory are interesting 843 int32 deviceID; 844 int64 directoryID; 845 const char* name; 846 if (message->FindInt32("device", &deviceID) != B_OK 847 || message->FindInt64("directory", &directoryID) != B_OK 848 || message->FindString("name", &name) != B_OK 849 || node_ref(deviceID, directoryID) != PackagesDirectoryRef()) { 850 return; 851 } 852 853 _QueueNodeMonitorEvent(name, created); 854 } 855 856 857 void 858 Volume::_HandleEntryMoved(const BMessage* message) 859 { 860 int32 deviceID; 861 int64 fromDirectoryID; 862 int64 toDirectoryID; 863 const char* fromName; 864 const char* toName; 865 if (message->FindInt32("device", &deviceID) != B_OK 866 || message->FindInt64("from directory", &fromDirectoryID) != B_OK 867 || message->FindInt64("to directory", &toDirectoryID) != B_OK 868 || message->FindString("from name", &fromName) != B_OK 869 || message->FindString("name", &toName) != B_OK 870 || deviceID != PackagesDeviceID() 871 || (fromDirectoryID != PackagesDirectoryID() 872 && toDirectoryID != PackagesDirectoryID())) { 873 return; 874 } 875 876 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 877 // make sure for a move the two events cannot get split 878 879 if (fromDirectoryID == PackagesDirectoryID()) 880 _QueueNodeMonitorEvent(fromName, false); 881 if (toDirectoryID == PackagesDirectoryID()) 882 _QueueNodeMonitorEvent(toName, true); 883 } 884 885 886 void 887 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated) 888 { 889 if (name.IsEmpty()) { 890 ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n"); 891 return; 892 } 893 894 // ignore entries that don't have the ".hpkg" extension 895 if (!name.EndsWith(kPackageFileNameExtension)) 896 return; 897 898 NodeMonitorEvent* event 899 = new(std::nothrow) NodeMonitorEvent(name, wasCreated); 900 if (event == NULL) { 901 ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n"); 902 return; 903 } 904 905 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 906 fPendingNodeMonitorEvents.Add(event); 907 eventsLock.Unlock(); 908 909 fNodeMonitorEventHandleTime 910 = system_time() + kNodeMonitorEventHandlingDelay; 911 BMessage message(kHandleNodeMonitorEvents); 912 BMessageRunner::StartSending(this, &message, kNodeMonitorEventHandlingDelay, 913 1); 914 } 915 916 917 void 918 Volume::_PackagesEntryCreated(const char* name) 919 { 920 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name); 921 // Ignore the event, if the package is already known. 922 Package* package = fLatestState->FindPackage(name); 923 if (package != NULL) { 924 if (package->File()->EntryCreatedIgnoreLevel() > 0) { 925 package->File()->DecrementEntryCreatedIgnoreLevel(); 926 } else { 927 WARN("node monitoring created event for already known entry " 928 "\"%s\"\n", name); 929 } 930 931 // Remove the package from the packages-to-be-deactivated set, if it is in 932 // there (unlikely, unless we see a remove-create sequence). 933 PackageSet::iterator it = fPackagesToBeDeactivated.find(package); 934 if (it != fPackagesToBeDeactivated.end()) 935 fPackagesToBeDeactivated.erase(it); 936 937 return; 938 } 939 940 status_t error = fPackageFileManager->CreatePackage( 941 NotOwningEntryRef(PackagesDirectoryRef(), name), 942 package); 943 if (error != B_OK) { 944 ERROR("failed to init package for file \"%s\"\n", name); 945 return; 946 } 947 948 fLock.Lock(); 949 fLatestState->AddPackage(package); 950 fChangeCount++; 951 fLock.Unlock(); 952 953 try { 954 fPackagesToBeActivated.insert(package); 955 } catch (std::bad_alloc& exception) { 956 ERROR("out of memory\n"); 957 return; 958 } 959 } 960 961 962 void 963 Volume::_PackagesEntryRemoved(const char* name) 964 { 965 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name); 966 Package* package = fLatestState->FindPackage(name); 967 if (package == NULL) 968 return; 969 970 // Ignore the event, if we generated it ourselves. 971 if (package->File()->EntryRemovedIgnoreLevel() > 0) { 972 package->File()->DecrementEntryRemovedIgnoreLevel(); 973 return; 974 } 975 976 // Remove the package from the packages-to-be-activated set, if it is in 977 // there (unlikely, unless we see a create-remove-create sequence). 978 PackageSet::iterator it = fPackagesToBeActivated.find(package); 979 if (it != fPackagesToBeActivated.end()) 980 fPackagesToBeActivated.erase(it); 981 982 // If the package isn't active, just remove it for good. 983 if (!package->IsActive()) { 984 AutoLocker<BLocker> locker(fLock); 985 fLatestState->RemovePackage(package); 986 fChangeCount++; 987 delete package; 988 return; 989 } 990 991 // The package must be deactivated. 992 try { 993 fPackagesToBeDeactivated.insert(package); 994 } catch (std::bad_alloc& exception) { 995 ERROR("out of memory\n"); 996 return; 997 } 998 } 999 1000 1001 status_t 1002 Volume::_ReadPackagesDirectory() 1003 { 1004 BDirectory directory; 1005 status_t error = directory.SetTo(&PackagesDirectoryRef()); 1006 if (error != B_OK) { 1007 ERROR("Volume::_ReadPackagesDirectory(): failed to open packages " 1008 "directory: %s\n", strerror(error)); 1009 RETURN_ERROR(error); 1010 } 1011 1012 entry_ref entry; 1013 while (directory.GetNextRef(&entry) == B_OK) { 1014 if (!BString(entry.name).EndsWith(kPackageFileNameExtension)) 1015 continue; 1016 1017 Package* package; 1018 status_t error = fPackageFileManager->CreatePackage(entry, package); 1019 if (error == B_OK) { 1020 AutoLocker<BLocker> locker(fLock); 1021 fLatestState->AddPackage(package); 1022 fChangeCount++; 1023 } 1024 } 1025 1026 return B_OK; 1027 } 1028 1029 1030 status_t 1031 Volume::_InitLatestState() 1032 { 1033 if (_InitLatestStateFromActivatedPackages() == B_OK) 1034 return B_OK; 1035 1036 INFORM("Failed to get activated packages info from activated packages file." 1037 " Assuming all package files in package directory are activated.\n"); 1038 1039 AutoLocker<BLocker> locker(fLock); 1040 1041 for (PackageFileNameHashTable::Iterator it 1042 = fLatestState->ByFileNameIterator(); 1043 Package* package = it.Next();) { 1044 fLatestState->SetPackageActive(package, true); 1045 fChangeCount++; 1046 } 1047 1048 return B_OK; 1049 } 1050 1051 1052 status_t 1053 Volume::_InitLatestStateFromActivatedPackages() 1054 { 1055 // open admin directory 1056 BDirectory adminDirectory; 1057 status_t error = _OpenPackagesSubDirectory( 1058 RelativePath(kAdminDirectoryName), false, adminDirectory); 1059 if (error != B_OK) 1060 RETURN_ERROR(error); 1061 1062 node_ref adminNode; 1063 error = adminDirectory.GetNodeRef(&adminNode); 1064 if (error != B_OK) 1065 RETURN_ERROR(error); 1066 1067 // try reading the activation file 1068 NotOwningEntryRef entryRef(adminNode, kActivationFileName); 1069 BFile file; 1070 error = file.SetTo(&entryRef, B_READ_ONLY); 1071 if (error != B_OK) { 1072 BEntry activationEntry(&entryRef); 1073 BPath activationPath; 1074 const char *activationFilePathName = "Unknown due to errors"; 1075 if (activationEntry.InitCheck() == B_OK && 1076 activationEntry.GetPath(&activationPath) == B_OK) 1077 activationFilePathName = activationPath.Path(); 1078 INFORM("Failed to open packages activation file %s: %s\n", 1079 activationFilePathName, strerror(error)); 1080 RETURN_ERROR(error); 1081 } 1082 1083 // read the whole file into memory to simplify things 1084 off_t size; 1085 error = file.GetSize(&size); 1086 if (error != B_OK) { 1087 ERROR("Failed to packages activation file size: %s\n", 1088 strerror(error)); 1089 RETURN_ERROR(error); 1090 } 1091 1092 if (size > (off_t)kMaxActivationFileSize) { 1093 ERROR("The packages activation file is too big.\n"); 1094 RETURN_ERROR(B_BAD_DATA); 1095 } 1096 1097 char* fileContent = (char*)malloc(size + 1); 1098 if (fileContent == NULL) 1099 RETURN_ERROR(B_NO_MEMORY); 1100 MemoryDeleter fileContentDeleter(fileContent); 1101 1102 ssize_t bytesRead = file.Read(fileContent, size); 1103 if (bytesRead < 0) { 1104 ERROR("Failed to read packages activation file: %s\n", 1105 strerror(bytesRead)); 1106 RETURN_ERROR(errno); 1107 } 1108 1109 if (bytesRead != size) { 1110 ERROR("Failed to read whole packages activation file.\n"); 1111 RETURN_ERROR(B_ERROR); 1112 } 1113 1114 // null-terminate to simplify parsing 1115 fileContent[size] = '\0'; 1116 1117 AutoLocker<BLocker> locker(fLock); 1118 1119 // parse the file and mark the respective packages active 1120 const char* packageName = fileContent; 1121 char* const fileContentEnd = fileContent + size; 1122 while (packageName < fileContentEnd) { 1123 char* packageNameEnd = strchr(packageName, '\n'); 1124 if (packageNameEnd == NULL) 1125 packageNameEnd = fileContentEnd; 1126 1127 // skip empty lines 1128 if (packageName == packageNameEnd) { 1129 packageName++; 1130 continue; 1131 } 1132 *packageNameEnd = '\0'; 1133 1134 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) { 1135 ERROR("Invalid packages activation file content.\n"); 1136 RETURN_ERROR(B_BAD_DATA); 1137 } 1138 1139 Package* package = fLatestState->FindPackage(packageName); 1140 if (package != NULL) { 1141 fLatestState->SetPackageActive(package, true); 1142 fChangeCount++; 1143 } else { 1144 WARN("Package \"%s\" from activation file not in packages " 1145 "directory.\n", packageName); 1146 } 1147 1148 packageName = packageNameEnd + 1; 1149 } 1150 1151 return B_OK; 1152 } 1153 1154 1155 status_t 1156 Volume::_GetActivePackages(int fd) 1157 { 1158 // get the info from packagefs 1159 PackageFSGetPackageInfosRequest* request = NULL; 1160 MemoryDeleter requestDeleter; 1161 size_t bufferSize = 64 * 1024; 1162 for (;;) { 1163 request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize); 1164 if (request == NULL) 1165 RETURN_ERROR(B_NO_MEMORY); 1166 requestDeleter.SetTo(request); 1167 1168 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request, 1169 bufferSize) != 0) { 1170 ERROR("Volume::_GetActivePackages(): failed to get active package " 1171 "info from package FS: %s\n", strerror(errno)); 1172 RETURN_ERROR(errno); 1173 } 1174 1175 if (request->bufferSize <= bufferSize) 1176 break; 1177 1178 bufferSize = request->bufferSize; 1179 requestDeleter.Unset(); 1180 } 1181 1182 INFORM("latest volume state:\n"); 1183 _DumpState(fLatestState); 1184 1185 // check whether that matches the expected state 1186 if (_CheckActivePackagesMatchLatestState(request)) { 1187 INFORM("The latest volume state is also the currently active one\n"); 1188 fActiveState = fLatestState; 1189 return B_OK; 1190 } 1191 1192 // There's a mismatch. We need a new state that reflects the actual 1193 // activation situation. 1194 VolumeState* state = new(std::nothrow) VolumeState; 1195 if (state == NULL) 1196 RETURN_ERROR(B_NO_MEMORY); 1197 ObjectDeleter<VolumeState> stateDeleter(state); 1198 1199 for (uint32 i = 0; i < request->packageCount; i++) { 1200 const PackageFSPackageInfo& info = request->infos[i]; 1201 NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID, 1202 info.name); 1203 Package* package; 1204 status_t error = fPackageFileManager->CreatePackage(entryRef, package); 1205 if (error != B_OK) { 1206 WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %" 1207 B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID, 1208 info.directoryNodeID, info.name, strerror(error)); 1209 continue; 1210 } 1211 1212 state->AddPackage(package); 1213 state->SetPackageActive(package, true); 1214 } 1215 1216 INFORM("currently active volume state:\n"); 1217 _DumpState(state); 1218 1219 fActiveState = stateDeleter.Detach(); 1220 return B_OK; 1221 } 1222 1223 1224 void 1225 Volume::_RunQueuedScripts() 1226 { 1227 BDirectory adminDirectory; 1228 status_t error = _OpenPackagesSubDirectory( 1229 RelativePath(kAdminDirectoryName), false, adminDirectory); 1230 if (error != B_OK) 1231 return; 1232 1233 BDirectory scriptsDirectory; 1234 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName); 1235 if (error != B_OK) 1236 return; 1237 1238 // enumerate all the symlinks in the queued scripts directory 1239 BEntry scriptEntry; 1240 while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) { 1241 BPath scriptPath; 1242 scriptEntry.GetPath(&scriptPath); 1243 error = scriptPath.InitCheck(); 1244 if (error != B_OK) { 1245 INFORM("failed to get path of post-installation script \"%s\"\n", 1246 strerror(error)); 1247 continue; 1248 } 1249 1250 errno = 0; 1251 int result = system(scriptPath.Path()); 1252 if (result != 0) { 1253 INFORM("running post-installation script \"%s\" " 1254 "failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno)); 1255 } 1256 1257 // remove the symlink, now that we've run the post-installation script 1258 error = scriptEntry.Remove(); 1259 if (error != B_OK) { 1260 INFORM("removing queued post-install script failed \"%s\"\n", 1261 strerror(error)); 1262 } 1263 } 1264 } 1265 1266 1267 bool 1268 Volume::_CheckActivePackagesMatchLatestState( 1269 PackageFSGetPackageInfosRequest* request) 1270 { 1271 if (fPackagesDirectoryCount != 1) { 1272 INFORM("An old packages state (\"%s\") seems to be active.\n", 1273 fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String()); 1274 return false; 1275 } 1276 1277 const node_ref packagesDirRef(PackagesDirectoryRef()); 1278 1279 // mark the returned packages active 1280 for (uint32 i = 0; i < request->packageCount; i++) { 1281 const PackageFSPackageInfo& info = request->infos[i]; 1282 if (node_ref(info.directoryDeviceID, info.directoryNodeID) 1283 != packagesDirRef) { 1284 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1285 ") not in packages directory\n", info.name, 1286 info.packageDeviceID, info.packageNodeID); 1287 return false; 1288 } 1289 1290 Package* package = fLatestState->FindPackage( 1291 node_ref(info.packageDeviceID, info.packageNodeID)); 1292 if (package == NULL || !package->IsActive()) { 1293 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1294 ") not %s\n", info.name, 1295 info.packageDeviceID, info.packageNodeID, 1296 package == NULL 1297 ? "found in packages directory" : "supposed to be active"); 1298 return false; 1299 } 1300 } 1301 1302 // Check whether there are packages that aren't active but should be. 1303 uint32 count = 0; 1304 for (PackageNodeRefHashTable::Iterator it 1305 = fLatestState->ByNodeRefIterator(); it.HasNext();) { 1306 Package* package = it.Next(); 1307 if (package->IsActive()) 1308 count++; 1309 } 1310 1311 if (count != request->packageCount) { 1312 INFORM("There seem to be packages in the packages directory that " 1313 "should be active.\n"); 1314 return false; 1315 } 1316 1317 return true; 1318 } 1319 1320 1321 void 1322 Volume::_SetLatestState(VolumeState* state, bool isActive) 1323 { 1324 AutoLocker<BLocker> locker(fLock); 1325 if (isActive) { 1326 if (fLatestState != fActiveState) 1327 delete fActiveState; 1328 fActiveState = state; 1329 } 1330 1331 if (fLatestState != fActiveState) 1332 delete fLatestState; 1333 fLatestState = state; 1334 fChangeCount++; 1335 1336 locker.Unlock(); 1337 1338 // Send a notification, if this is a system root volume. 1339 if (fRoot->IsSystemRoot()) { 1340 BMessage message(B_PACKAGE_UPDATE); 1341 if (message.AddInt32("event", 1342 (int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK 1343 && message.AddInt32("location", (int32)Location()) == B_OK 1344 && message.AddInt64("change count", fChangeCount) == B_OK) { 1345 BRoster::Private().SendTo(&message, NULL, false); 1346 } 1347 } 1348 } 1349 1350 1351 void 1352 Volume::_DumpState(VolumeState* state) 1353 { 1354 uint32 inactiveCount = 0; 1355 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1356 it.HasNext();) { 1357 Package* package = it.Next(); 1358 if (package->IsActive()) { 1359 INFORM("active package: \"%s\"\n", package->FileName().String()); 1360 } else 1361 inactiveCount++; 1362 } 1363 1364 if (inactiveCount == 0) 1365 return; 1366 1367 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1368 it.HasNext();) { 1369 Package* package = it.Next(); 1370 if (!package->IsActive()) 1371 INFORM("inactive package: \"%s\"\n", package->FileName().String()); 1372 } 1373 } 1374 1375 1376 status_t 1377 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository, 1378 bool activeOnly, bool installed) 1379 { 1380 status_t error = repository.SetTo(Path()); 1381 if (error != B_OK) { 1382 ERROR("Volume::_AddRepository(): failed to init repository: %s\n", 1383 strerror(error)); 1384 return error; 1385 } 1386 1387 repository.SetInstalled(installed); 1388 1389 error = AddPackagesToRepository(repository, true); 1390 if (error != B_OK) { 1391 ERROR("Volume::_AddRepository(): failed to add packages to " 1392 "repository: %s\n", strerror(error)); 1393 return error; 1394 } 1395 1396 error = solver->AddRepository(&repository); 1397 if (error != B_OK) { 1398 ERROR("Volume::_AddRepository(): failed to add repository to solver: " 1399 "%s\n", strerror(error)); 1400 return error; 1401 } 1402 1403 return B_OK; 1404 } 1405 1406 1407 status_t 1408 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create, 1409 BDirectory& _directory) 1410 { 1411 // open the packages directory 1412 BDirectory directory; 1413 status_t error = directory.SetTo(&PackagesDirectoryRef()); 1414 if (error != B_OK) { 1415 ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages " 1416 "directory: %s\n", strerror(error)); 1417 RETURN_ERROR(error); 1418 } 1419 1420 return FSUtils::OpenSubDirectory(directory, path, create, _directory); 1421 } 1422 1423 1424 void 1425 Volume::_CommitTransaction(BMessage* message, 1426 const BActivationTransaction* transaction, 1427 const PackageSet& packagesAlreadyAdded, 1428 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) 1429 { 1430 _result.Unset(); 1431 1432 // perform the request 1433 CommitTransactionHandler handler(this, fPackageFileManager, _result); 1434 BTransactionError error = B_TRANSACTION_INTERNAL_ERROR; 1435 try { 1436 handler.Init(fLatestState, fLatestState == fActiveState, 1437 packagesAlreadyAdded, packagesAlreadyRemoved); 1438 1439 if (message != NULL) 1440 handler.HandleRequest(message); 1441 else if (transaction != NULL) 1442 handler.HandleRequest(*transaction); 1443 else 1444 handler.HandleRequest(); 1445 1446 _SetLatestState(handler.DetachVolumeState(), 1447 handler.IsActiveVolumeState()); 1448 error = B_TRANSACTION_OK; 1449 } catch (Exception& exception) { 1450 error = exception.Error(); 1451 exception.SetOnResult(_result); 1452 if (_result.ErrorPackage().IsEmpty() 1453 && handler.CurrentPackage() != NULL) { 1454 _result.SetErrorPackage(handler.CurrentPackage()->FileName()); 1455 } 1456 } catch (std::bad_alloc& exception) { 1457 error = B_TRANSACTION_NO_MEMORY; 1458 } 1459 1460 _result.SetError(error); 1461 1462 // revert on error 1463 if (error != B_TRANSACTION_OK) 1464 handler.Revert(); 1465 } 1466