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 if (packagesDirectory.SetTo(&PackagesDirectoryRef()) == B_OK) { 336 if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists()) 337 packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL); 338 } 339 340 return B_OK; 341 } 342 343 344 status_t 345 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly) 346 { 347 for (PackageFileNameHashTable::Iterator it 348 = fLatestState->ByFileNameIterator(); it.HasNext();) { 349 Package* package = it.Next(); 350 if (activeOnly && !package->IsActive()) 351 continue; 352 353 status_t error = repository.AddPackage(package->Info()); 354 if (error != B_OK) { 355 ERROR("Volume::AddPackagesToRepository(): failed to add package %s " 356 "to repository: %s\n", package->FileName().String(), 357 strerror(error)); 358 return error; 359 } 360 } 361 362 return B_OK; 363 } 364 365 366 void 367 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume) 368 { 369 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume); 370 // create the solver 371 BSolver* solver; 372 status_t error = BSolver::Create(solver); 373 if (error != B_OK) { 374 ERROR("Volume::InitialVerify(): failed to create solver: %s\n", 375 strerror(error)); 376 return; 377 } 378 ObjectDeleter<BSolver> solverDeleter(solver); 379 380 // add a repository with all active packages 381 BSolverRepository repository; 382 error = _AddRepository(solver, repository, true, true); 383 if (error != B_OK) { 384 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 385 strerror(error)); 386 return; 387 } 388 389 // add a repository for the next volume 390 BSolverRepository nextRepository; 391 if (nextVolume != NULL) { 392 nextRepository.SetPriority(1); 393 error = nextVolume->_AddRepository(solver, nextRepository, true, false); 394 if (error != B_OK) { 395 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 396 strerror(error)); 397 return; 398 } 399 } 400 401 // add a repository for the next next volume 402 BSolverRepository nextNextRepository; 403 if (nextNextVolume != NULL) { 404 nextNextRepository.SetPriority(2); 405 error = nextNextVolume->_AddRepository(solver, nextNextRepository, true, 406 false); 407 if (error != B_OK) { 408 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 409 strerror(error)); 410 return; 411 } 412 } 413 414 // verify 415 error = solver->VerifyInstallation(); 416 if (error != B_OK) { 417 ERROR("Volume::InitialVerify(): failed to verify: %s\n", 418 strerror(error)); 419 return; 420 } 421 422 if (!solver->HasProblems()) { 423 INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n", 424 Path().String()); 425 return; 426 } 427 428 // print the problems 429 // TODO: Notify the user ... 430 INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n", 431 Path().String()); 432 433 int32 problemCount = solver->CountProblems(); 434 for (int32 i = 0; i < problemCount; i++) { 435 BSolverProblem* problem = solver->ProblemAt(i); 436 INFORM(" %" B_PRId32 ": %s\n", i + 1, problem->ToString().String()); 437 int32 solutionCount = problem->CountSolutions(); 438 for (int32 k = 0; k < solutionCount; k++) { 439 const BSolverProblemSolution* solution = problem->SolutionAt(k); 440 INFORM(" solution %" B_PRId32 ":\n", k + 1); 441 int32 elementCount = solution->CountElements(); 442 for (int32 l = 0; l < elementCount; l++) { 443 const BSolverProblemSolutionElement* element 444 = solution->ElementAt(l); 445 INFORM(" - %s\n", element->ToString().String()); 446 } 447 } 448 } 449 } 450 451 452 void 453 Volume::HandleGetLocationInfoRequest(BMessage* message) 454 { 455 AutoLocker<BLocker> locker(fLock); 456 457 // If the cached reply message is up-to-date, just send it. 458 int64 changeCount; 459 if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK 460 && changeCount == fChangeCount) { 461 locker.Unlock(); 462 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 463 kCommunicationTimeout); 464 return; 465 } 466 467 // rebuild the reply message 468 fLocationInfoReply.MakeEmpty(); 469 470 if (fLocationInfoReply.AddInt32("base directory device", 471 fRootDirectoryRef.device) != B_OK 472 || fLocationInfoReply.AddInt64("base directory node", 473 fRootDirectoryRef.node) != B_OK 474 || fLocationInfoReply.AddInt32("packages directory device", 475 PackagesDeviceID()) != B_OK 476 || fLocationInfoReply.AddInt64("packages directory node", 477 PackagesDirectoryID()) != B_OK) { 478 return; 479 } 480 481 for (PackageFileNameHashTable::Iterator it 482 = fLatestState->ByFileNameIterator(); it.HasNext();) { 483 Package* package = it.Next(); 484 const char* fieldName = package->IsActive() 485 ? "latest active packages" : "latest inactive packages"; 486 BMessage packageArchive; 487 if (package->Info().Archive(&packageArchive) != B_OK 488 || fLocationInfoReply.AddMessage(fieldName, &packageArchive) 489 != B_OK) { 490 return; 491 } 492 } 493 494 if (fActiveState != fLatestState) { 495 if (fPackagesDirectoryCount > 1) { 496 fLocationInfoReply.AddString("old state", 497 fPackagesDirectories[fPackagesDirectoryCount - 1].Name()); 498 } 499 500 for (PackageFileNameHashTable::Iterator it 501 = fActiveState->ByFileNameIterator(); it.HasNext();) { 502 Package* package = it.Next(); 503 if (!package->IsActive()) 504 continue; 505 506 BMessage packageArchive; 507 if (package->Info().Archive(&packageArchive) != B_OK 508 || fLocationInfoReply.AddMessage("currently active packages", 509 &packageArchive) != B_OK) { 510 return; 511 } 512 } 513 } 514 515 if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK) 516 return; 517 518 locker.Unlock(); 519 520 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 521 kCommunicationTimeout); 522 } 523 524 525 void 526 Volume::HandleCommitTransactionRequest(BMessage* message) 527 { 528 BCommitTransactionResult result; 529 PackageSet dummy; 530 _CommitTransaction(message, NULL, dummy, dummy, result); 531 532 BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); 533 status_t error = result.AddToMessage(reply); 534 if (error != B_OK) { 535 ERROR("Volume::HandleCommitTransactionRequest(): Failed to add " 536 "transaction result to reply: %s\n", strerror(error)); 537 return; 538 } 539 540 message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout); 541 } 542 543 544 void 545 Volume::PackageJobPending() 546 { 547 atomic_add(&fPendingPackageJobCount, 1); 548 } 549 550 551 void 552 Volume::PackageJobFinished() 553 { 554 atomic_add(&fPendingPackageJobCount, -1); 555 } 556 557 558 bool 559 Volume::IsPackageJobPending() const 560 { 561 return fPendingPackageJobCount != 0; 562 } 563 564 565 void 566 Volume::Unmounted() 567 { 568 if (fListener != NULL) { 569 stop_watching(BMessenger(this)); 570 fListener = NULL; 571 } 572 573 if (BLooper* looper = Looper()) 574 looper->RemoveHandler(this); 575 } 576 577 578 void 579 Volume::MessageReceived(BMessage* message) 580 { 581 switch (message->what) { 582 case B_NODE_MONITOR: 583 { 584 int32 opcode; 585 if (message->FindInt32("opcode", &opcode) != B_OK) 586 break; 587 588 switch (opcode) { 589 case B_ENTRY_CREATED: 590 _HandleEntryCreatedOrRemoved(message, true); 591 break; 592 case B_ENTRY_REMOVED: 593 _HandleEntryCreatedOrRemoved(message, false); 594 break; 595 case B_ENTRY_MOVED: 596 _HandleEntryMoved(message); 597 break; 598 default: 599 break; 600 } 601 break; 602 } 603 604 case kHandleNodeMonitorEvents: 605 if (fListener != NULL) { 606 if (system_time() >= fNodeMonitorEventHandleTime) 607 fListener->VolumeNodeMonitorEventOccurred(this); 608 } 609 break; 610 611 default: 612 BHandler::MessageReceived(message); 613 break; 614 } 615 } 616 617 618 BPackageInstallationLocation 619 Volume::Location() const 620 { 621 switch (fMountType) { 622 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 623 return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM; 624 case PACKAGE_FS_MOUNT_TYPE_HOME: 625 return B_PACKAGE_INSTALLATION_LOCATION_HOME; 626 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 627 default: 628 return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT; 629 } 630 } 631 632 633 const node_ref& 634 Volume::PackagesDirectoryRef() const 635 { 636 return fPackagesDirectories[0].NodeRef(); 637 } 638 639 640 PackageFileNameHashTable::Iterator 641 Volume::PackagesByFileNameIterator() const 642 { 643 return fLatestState->ByFileNameIterator(); 644 } 645 646 647 int 648 Volume::OpenRootDirectory() const 649 { 650 BDirectory directory; 651 status_t error = directory.SetTo(&fRootDirectoryRef); 652 if (error != B_OK) { 653 ERROR("Volume::OpenRootDirectory(): failed to open root directory: " 654 "%s\n", strerror(error)); 655 RETURN_ERROR(error); 656 } 657 658 return directory.Dup(); 659 } 660 661 662 void 663 Volume::ProcessPendingNodeMonitorEvents() 664 { 665 // get the events 666 NodeMonitorEventList events; 667 { 668 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 669 events.MoveFrom(&fPendingNodeMonitorEvents); 670 } 671 672 // process them 673 while (NodeMonitorEvent* event = events.RemoveHead()) { 674 ObjectDeleter<NodeMonitorEvent> eventDeleter(event); 675 if (event->WasCreated()) 676 _PackagesEntryCreated(event->EntryName()); 677 else 678 _PackagesEntryRemoved(event->EntryName()); 679 } 680 } 681 682 683 bool 684 Volume::HasPendingPackageActivationChanges() const 685 { 686 return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty(); 687 } 688 689 690 void 691 Volume::ProcessPendingPackageActivationChanges() 692 { 693 if (!HasPendingPackageActivationChanges()) 694 return; 695 696 // perform the request 697 BCommitTransactionResult result; 698 _CommitTransaction(NULL, NULL, fPackagesToBeActivated, 699 fPackagesToBeDeactivated, result); 700 701 if (result.Error() != B_TRANSACTION_OK) { 702 ERROR("Volume::ProcessPendingPackageActivationChanges(): package " 703 "activation failed: %s\n", result.FullErrorMessage().String()); 704 // TODO: Notify the user! 705 } 706 707 // clear the activation/deactivation sets in any event 708 fPackagesToBeActivated.clear(); 709 fPackagesToBeDeactivated.clear(); 710 } 711 712 713 void 714 Volume::ClearPackageActivationChanges() 715 { 716 fPackagesToBeActivated.clear(); 717 fPackagesToBeDeactivated.clear(); 718 } 719 720 721 status_t 722 Volume::CreateTransaction(BPackageInstallationLocation location, 723 BActivationTransaction& _transaction, BDirectory& _transactionDirectory) 724 { 725 // open admin directory 726 BDirectory adminDirectory; 727 status_t error = _OpenPackagesSubDirectory( 728 RelativePath(kAdminDirectoryName), true, adminDirectory); 729 if (error != B_OK) 730 return error; 731 732 // create a transaction directory 733 int uniqueId = 1; 734 BString directoryName; 735 for (;; uniqueId++) { 736 directoryName.SetToFormat("transaction-%d", uniqueId); 737 if (directoryName.IsEmpty()) 738 return B_NO_MEMORY; 739 740 error = adminDirectory.CreateDirectory(directoryName, 741 &_transactionDirectory); 742 if (error == B_OK) 743 break; 744 if (error != B_FILE_EXISTS) 745 return error; 746 } 747 748 // init the transaction 749 error = _transaction.SetTo(location, fChangeCount, directoryName); 750 if (error != B_OK) { 751 BEntry entry; 752 _transactionDirectory.GetEntry(&entry); 753 _transactionDirectory.Unset(); 754 if (entry.InitCheck() == B_OK) 755 entry.Remove(); 756 return error; 757 } 758 759 return B_OK; 760 } 761 762 763 void 764 Volume::CommitTransaction(const BActivationTransaction& transaction, 765 const PackageSet& packagesAlreadyAdded, 766 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) 767 { 768 _CommitTransaction(NULL, &transaction, packagesAlreadyAdded, 769 packagesAlreadyRemoved, _result); 770 } 771 772 773 void 774 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created) 775 { 776 // only moves to or from our packages directory are interesting 777 int32 deviceID; 778 int64 directoryID; 779 const char* name; 780 if (message->FindInt32("device", &deviceID) != B_OK 781 || message->FindInt64("directory", &directoryID) != B_OK 782 || message->FindString("name", &name) != B_OK 783 || node_ref(deviceID, directoryID) != PackagesDirectoryRef()) { 784 return; 785 } 786 787 _QueueNodeMonitorEvent(name, created); 788 } 789 790 791 void 792 Volume::_HandleEntryMoved(const BMessage* message) 793 { 794 int32 deviceID; 795 int64 fromDirectoryID; 796 int64 toDirectoryID; 797 const char* fromName; 798 const char* toName; 799 if (message->FindInt32("device", &deviceID) != B_OK 800 || message->FindInt64("from directory", &fromDirectoryID) != B_OK 801 || message->FindInt64("to directory", &toDirectoryID) != B_OK 802 || message->FindString("from name", &fromName) != B_OK 803 || message->FindString("name", &toName) != B_OK 804 || deviceID != PackagesDeviceID() 805 || (fromDirectoryID != PackagesDirectoryID() 806 && toDirectoryID != PackagesDirectoryID())) { 807 return; 808 } 809 810 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 811 // make sure for a move the two events cannot get split 812 813 if (fromDirectoryID == PackagesDirectoryID()) 814 _QueueNodeMonitorEvent(fromName, false); 815 if (toDirectoryID == PackagesDirectoryID()) 816 _QueueNodeMonitorEvent(toName, true); 817 } 818 819 820 void 821 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated) 822 { 823 if (name.IsEmpty()) { 824 ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n"); 825 return; 826 } 827 828 // ignore entries that don't have the ".hpkg" extension 829 if (!name.EndsWith(kPackageFileNameExtension)) 830 return; 831 832 NodeMonitorEvent* event 833 = new(std::nothrow) NodeMonitorEvent(name, wasCreated); 834 if (event == NULL) { 835 ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n"); 836 return; 837 } 838 839 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 840 fPendingNodeMonitorEvents.Add(event); 841 eventsLock.Unlock(); 842 843 fNodeMonitorEventHandleTime 844 = system_time() + kNodeMonitorEventHandlingDelay; 845 BMessage message(kHandleNodeMonitorEvents); 846 BMessageRunner::StartSending(this, &message, kNodeMonitorEventHandlingDelay, 847 1); 848 } 849 850 851 void 852 Volume::_PackagesEntryCreated(const char* name) 853 { 854 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name); 855 // Ignore the event, if the package is already known. 856 Package* package = fLatestState->FindPackage(name); 857 if (package != NULL) { 858 if (package->File()->EntryCreatedIgnoreLevel() > 0) { 859 package->File()->DecrementEntryCreatedIgnoreLevel(); 860 } else { 861 WARN("node monitoring created event for already known entry " 862 "\"%s\"\n", name); 863 } 864 865 // Remove the package from the packages-to-be-deactivated set, if it is in 866 // there (unlikely, unless we see a remove-create sequence). 867 PackageSet::iterator it = fPackagesToBeDeactivated.find(package); 868 if (it != fPackagesToBeDeactivated.end()) 869 fPackagesToBeDeactivated.erase(it); 870 871 return; 872 } 873 874 status_t error = fPackageFileManager->CreatePackage( 875 NotOwningEntryRef(PackagesDirectoryRef(), name), 876 package); 877 if (error != B_OK) { 878 ERROR("failed to init package for file \"%s\"\n", name); 879 return; 880 } 881 882 fLock.Lock(); 883 fLatestState->AddPackage(package); 884 fChangeCount++; 885 fLock.Unlock(); 886 887 try { 888 fPackagesToBeActivated.insert(package); 889 } catch (std::bad_alloc& exception) { 890 ERROR("out of memory\n"); 891 return; 892 } 893 } 894 895 896 void 897 Volume::_PackagesEntryRemoved(const char* name) 898 { 899 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name); 900 Package* package = fLatestState->FindPackage(name); 901 if (package == NULL) 902 return; 903 904 // Ignore the event, if we generated it ourselves. 905 if (package->File()->EntryRemovedIgnoreLevel() > 0) { 906 package->File()->DecrementEntryRemovedIgnoreLevel(); 907 return; 908 } 909 910 // Remove the package from the packages-to-be-activated set, if it is in 911 // there (unlikely, unless we see a create-remove-create sequence). 912 PackageSet::iterator it = fPackagesToBeActivated.find(package); 913 if (it != fPackagesToBeActivated.end()) 914 fPackagesToBeActivated.erase(it); 915 916 // If the package isn't active, just remove it for good. 917 if (!package->IsActive()) { 918 AutoLocker<BLocker> locker(fLock); 919 fLatestState->RemovePackage(package); 920 fChangeCount++; 921 delete package; 922 return; 923 } 924 925 // The package must be deactivated. 926 try { 927 fPackagesToBeDeactivated.insert(package); 928 } catch (std::bad_alloc& exception) { 929 ERROR("out of memory\n"); 930 return; 931 } 932 } 933 934 935 status_t 936 Volume::_ReadPackagesDirectory() 937 { 938 BDirectory directory; 939 status_t error = directory.SetTo(&PackagesDirectoryRef()); 940 if (error != B_OK) { 941 ERROR("Volume::_ReadPackagesDirectory(): failed to open packages " 942 "directory: %s\n", strerror(error)); 943 RETURN_ERROR(error); 944 } 945 946 entry_ref entry; 947 while (directory.GetNextRef(&entry) == B_OK) { 948 if (!BString(entry.name).EndsWith(kPackageFileNameExtension)) 949 continue; 950 951 Package* package; 952 status_t error = fPackageFileManager->CreatePackage(entry, package); 953 if (error == B_OK) { 954 AutoLocker<BLocker> locker(fLock); 955 fLatestState->AddPackage(package); 956 fChangeCount++; 957 } 958 } 959 960 return B_OK; 961 } 962 963 964 status_t 965 Volume::_InitLatestState() 966 { 967 if (_InitLatestStateFromActivatedPackages() == B_OK) 968 return B_OK; 969 970 INFORM("Failed to get activated packages info from activated packages file." 971 " Assuming all package files in package directory are activated.\n"); 972 973 AutoLocker<BLocker> locker(fLock); 974 975 for (PackageFileNameHashTable::Iterator it 976 = fLatestState->ByFileNameIterator(); 977 Package* package = it.Next();) { 978 fLatestState->SetPackageActive(package, true); 979 fChangeCount++; 980 } 981 982 return B_OK; 983 } 984 985 986 status_t 987 Volume::_InitLatestStateFromActivatedPackages() 988 { 989 // open admin directory 990 BDirectory adminDirectory; 991 status_t error = _OpenPackagesSubDirectory( 992 RelativePath(kAdminDirectoryName), false, adminDirectory); 993 if (error != B_OK) 994 RETURN_ERROR(error); 995 996 node_ref adminNode; 997 error = adminDirectory.GetNodeRef(&adminNode); 998 if (error != B_OK) 999 RETURN_ERROR(error); 1000 1001 // try reading the activation file 1002 NotOwningEntryRef entryRef(adminNode, kActivationFileName); 1003 BFile file; 1004 error = file.SetTo(&entryRef, B_READ_ONLY); 1005 if (error != B_OK) { 1006 INFORM("Failed to open packages activation file: %s\n", 1007 strerror(error)); 1008 RETURN_ERROR(error); 1009 } 1010 1011 // read the whole file into memory to simplify things 1012 off_t size; 1013 error = file.GetSize(&size); 1014 if (error != B_OK) { 1015 ERROR("Failed to packages activation file size: %s\n", 1016 strerror(error)); 1017 RETURN_ERROR(error); 1018 } 1019 1020 if (size > (off_t)kMaxActivationFileSize) { 1021 ERROR("The packages activation file is too big.\n"); 1022 RETURN_ERROR(B_BAD_DATA); 1023 } 1024 1025 char* fileContent = (char*)malloc(size + 1); 1026 if (fileContent == NULL) 1027 RETURN_ERROR(B_NO_MEMORY); 1028 MemoryDeleter fileContentDeleter(fileContent); 1029 1030 ssize_t bytesRead = file.Read(fileContent, size); 1031 if (bytesRead < 0) { 1032 ERROR("Failed to read packages activation file: %s\n", 1033 strerror(bytesRead)); 1034 RETURN_ERROR(errno); 1035 } 1036 1037 if (bytesRead != size) { 1038 ERROR("Failed to read whole packages activation file.\n"); 1039 RETURN_ERROR(B_ERROR); 1040 } 1041 1042 // null-terminate to simplify parsing 1043 fileContent[size] = '\0'; 1044 1045 AutoLocker<BLocker> locker(fLock); 1046 1047 // parse the file and mark the respective packages active 1048 const char* packageName = fileContent; 1049 char* const fileContentEnd = fileContent + size; 1050 while (packageName < fileContentEnd) { 1051 char* packageNameEnd = strchr(packageName, '\n'); 1052 if (packageNameEnd == NULL) 1053 packageNameEnd = fileContentEnd; 1054 1055 // skip empty lines 1056 if (packageName == packageNameEnd) { 1057 packageName++; 1058 continue; 1059 } 1060 *packageNameEnd = '\0'; 1061 1062 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) { 1063 ERROR("Invalid packages activation file content.\n"); 1064 RETURN_ERROR(B_BAD_DATA); 1065 } 1066 1067 Package* package = fLatestState->FindPackage(packageName); 1068 if (package != NULL) { 1069 fLatestState->SetPackageActive(package, true); 1070 fChangeCount++; 1071 } else { 1072 WARN("Package \"%s\" from activation file not in packages " 1073 "directory.\n", packageName); 1074 } 1075 1076 packageName = packageNameEnd + 1; 1077 } 1078 1079 return B_OK; 1080 } 1081 1082 1083 status_t 1084 Volume::_GetActivePackages(int fd) 1085 { 1086 // get the info from packagefs 1087 PackageFSGetPackageInfosRequest* request = NULL; 1088 MemoryDeleter requestDeleter; 1089 size_t bufferSize = 64 * 1024; 1090 for (;;) { 1091 request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize); 1092 if (request == NULL) 1093 RETURN_ERROR(B_NO_MEMORY); 1094 requestDeleter.SetTo(request); 1095 1096 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request, 1097 bufferSize) != 0) { 1098 ERROR("Volume::_GetActivePackages(): failed to get active package " 1099 "info from package FS: %s\n", strerror(errno)); 1100 RETURN_ERROR(errno); 1101 } 1102 1103 if (request->bufferSize <= bufferSize) 1104 break; 1105 1106 bufferSize = request->bufferSize; 1107 requestDeleter.Unset(); 1108 } 1109 1110 INFORM("latest volume state:\n"); 1111 _DumpState(fLatestState); 1112 1113 // check whether that matches the expected state 1114 if (_CheckActivePackagesMatchLatestState(request)) { 1115 INFORM("The latest volume state is also the currently active one\n"); 1116 fActiveState = fLatestState; 1117 return B_OK; 1118 } 1119 1120 // There's a mismatch. We need a new state that reflects the actual 1121 // activation situation. 1122 VolumeState* state = new(std::nothrow) VolumeState; 1123 if (state == NULL) 1124 RETURN_ERROR(B_NO_MEMORY); 1125 ObjectDeleter<VolumeState> stateDeleter(state); 1126 1127 for (uint32 i = 0; i < request->packageCount; i++) { 1128 const PackageFSPackageInfo& info = request->infos[i]; 1129 NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID, 1130 info.name); 1131 Package* package; 1132 status_t error = fPackageFileManager->CreatePackage(entryRef, package); 1133 if (error != B_OK) { 1134 WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %" 1135 B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID, 1136 info.directoryNodeID, info.name, strerror(error)); 1137 continue; 1138 } 1139 1140 state->AddPackage(package); 1141 state->SetPackageActive(package, true); 1142 } 1143 1144 INFORM("currently active volume state:\n"); 1145 _DumpState(state); 1146 1147 fActiveState = stateDeleter.Detach(); 1148 return B_OK; 1149 } 1150 1151 1152 void 1153 Volume::_RunQueuedScripts() 1154 { 1155 BDirectory adminDirectory; 1156 status_t error = _OpenPackagesSubDirectory( 1157 RelativePath(kAdminDirectoryName), false, adminDirectory); 1158 if (error != B_OK) 1159 return; 1160 1161 BDirectory scriptsDirectory; 1162 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName); 1163 if (error != B_OK) 1164 return; 1165 1166 // enumerate all the symlinks in the queued scripts directory 1167 BEntry scriptEntry; 1168 while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) { 1169 BPath scriptPath; 1170 scriptEntry.GetPath(&scriptPath); 1171 error = scriptPath.InitCheck(); 1172 if (error != B_OK) { 1173 INFORM("failed to get path of post-installation script \"%s\"\n", 1174 strerror(error)); 1175 continue; 1176 } 1177 1178 errno = 0; 1179 int result = system(scriptPath.Path()); 1180 if (result != 0) { 1181 INFORM("running post-installation script \"%s\" " 1182 "failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno)); 1183 } 1184 1185 // remove the symlink, now that we've run the post-installation script 1186 error = scriptEntry.Remove(); 1187 if (error != B_OK) { 1188 INFORM("removing queued post-install script failed \"%s\"\n", 1189 strerror(error)); 1190 } 1191 } 1192 } 1193 1194 1195 bool 1196 Volume::_CheckActivePackagesMatchLatestState( 1197 PackageFSGetPackageInfosRequest* request) 1198 { 1199 if (fPackagesDirectoryCount != 1) { 1200 INFORM("An old packages state (\"%s\") seems to be active.\n", 1201 fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String()); 1202 return false; 1203 } 1204 1205 const node_ref packagesDirRef(PackagesDirectoryRef()); 1206 1207 // mark the returned packages active 1208 for (uint32 i = 0; i < request->packageCount; i++) { 1209 const PackageFSPackageInfo& info = request->infos[i]; 1210 if (node_ref(info.directoryDeviceID, info.directoryNodeID) 1211 != packagesDirRef) { 1212 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1213 ") not in packages directory\n", info.name, 1214 info.packageDeviceID, info.packageNodeID); 1215 return false; 1216 } 1217 1218 Package* package = fLatestState->FindPackage( 1219 node_ref(info.packageDeviceID, info.packageNodeID)); 1220 if (package == NULL || !package->IsActive()) { 1221 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1222 ") not %s\n", info.name, 1223 info.packageDeviceID, info.packageNodeID, 1224 package == NULL 1225 ? "found in packages directory" : "supposed to be active"); 1226 return false; 1227 } 1228 } 1229 1230 // Check whether there are packages that aren't active but should be. 1231 uint32 count = 0; 1232 for (PackageNodeRefHashTable::Iterator it 1233 = fLatestState->ByNodeRefIterator(); it.HasNext();) { 1234 Package* package = it.Next(); 1235 if (package->IsActive()) 1236 count++; 1237 } 1238 1239 if (count != request->packageCount) { 1240 INFORM("There seem to be packages in the packages directory that " 1241 "should be active.\n"); 1242 return false; 1243 } 1244 1245 return true; 1246 } 1247 1248 1249 void 1250 Volume::_SetLatestState(VolumeState* state, bool isActive) 1251 { 1252 AutoLocker<BLocker> locker(fLock); 1253 if (isActive) { 1254 if (fLatestState != fActiveState) 1255 delete fActiveState; 1256 fActiveState = state; 1257 } 1258 1259 if (fLatestState != fActiveState) 1260 delete fLatestState; 1261 fLatestState = state; 1262 fChangeCount++; 1263 1264 locker.Unlock(); 1265 1266 // Send a notification, if this is a system root volume. 1267 if (fRoot->IsSystemRoot()) { 1268 BMessage message(B_PACKAGE_UPDATE); 1269 if (message.AddInt32("event", 1270 (int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK 1271 && message.AddInt32("location", (int32)Location()) == B_OK 1272 && message.AddInt64("change count", fChangeCount) == B_OK) { 1273 BRoster::Private().SendTo(&message, NULL, false); 1274 } 1275 } 1276 } 1277 1278 1279 void 1280 Volume::_DumpState(VolumeState* state) 1281 { 1282 uint32 inactiveCount = 0; 1283 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1284 it.HasNext();) { 1285 Package* package = it.Next(); 1286 if (package->IsActive()) { 1287 INFORM("active package: \"%s\"\n", package->FileName().String()); 1288 } else 1289 inactiveCount++; 1290 } 1291 1292 if (inactiveCount == 0) 1293 return; 1294 1295 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1296 it.HasNext();) { 1297 Package* package = it.Next(); 1298 if (!package->IsActive()) 1299 INFORM("inactive package: \"%s\"\n", package->FileName().String()); 1300 } 1301 } 1302 1303 1304 status_t 1305 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository, 1306 bool activeOnly, bool installed) 1307 { 1308 status_t error = repository.SetTo(Path()); 1309 if (error != B_OK) { 1310 ERROR("Volume::_AddRepository(): failed to init repository: %s\n", 1311 strerror(error)); 1312 return error; 1313 } 1314 1315 repository.SetInstalled(installed); 1316 1317 error = AddPackagesToRepository(repository, true); 1318 if (error != B_OK) { 1319 ERROR("Volume::_AddRepository(): failed to add packages to " 1320 "repository: %s\n", strerror(error)); 1321 return error; 1322 } 1323 1324 error = solver->AddRepository(&repository); 1325 if (error != B_OK) { 1326 ERROR("Volume::_AddRepository(): failed to add repository to solver: " 1327 "%s\n", strerror(error)); 1328 return error; 1329 } 1330 1331 return B_OK; 1332 } 1333 1334 1335 status_t 1336 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create, 1337 BDirectory& _directory) 1338 { 1339 // open the packages directory 1340 BDirectory directory; 1341 status_t error = directory.SetTo(&PackagesDirectoryRef()); 1342 if (error != B_OK) { 1343 ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages " 1344 "directory: %s\n", strerror(error)); 1345 RETURN_ERROR(error); 1346 } 1347 1348 return FSUtils::OpenSubDirectory(directory, path, create, _directory); 1349 } 1350 1351 1352 void 1353 Volume::_CommitTransaction(BMessage* message, 1354 const BActivationTransaction* transaction, 1355 const PackageSet& packagesAlreadyAdded, 1356 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) 1357 { 1358 _result.Unset(); 1359 1360 // perform the request 1361 CommitTransactionHandler handler(this, fPackageFileManager, _result); 1362 BTransactionError error = B_TRANSACTION_INTERNAL_ERROR; 1363 try { 1364 handler.Init(fLatestState, fLatestState == fActiveState, 1365 packagesAlreadyAdded, packagesAlreadyRemoved); 1366 1367 if (message != NULL) 1368 handler.HandleRequest(message); 1369 else if (transaction != NULL) 1370 handler.HandleRequest(*transaction); 1371 else 1372 handler.HandleRequest(); 1373 1374 _SetLatestState(handler.DetachVolumeState(), 1375 handler.IsActiveVolumeState()); 1376 error = B_TRANSACTION_OK; 1377 } catch (Exception& exception) { 1378 error = exception.Error(); 1379 exception.SetOnResult(_result); 1380 if (_result.ErrorPackage().IsEmpty() 1381 && handler.CurrentPackage() != NULL) { 1382 _result.SetErrorPackage(handler.CurrentPackage()->FileName()); 1383 } 1384 } catch (std::bad_alloc& exception) { 1385 error = B_TRANSACTION_NO_MEMORY; 1386 } 1387 1388 _result.SetError(error); 1389 1390 // revert on error 1391 if (error != B_TRANSACTION_OK) 1392 handler.Revert(); 1393 } 1394