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 // try reading the activation file 990 NotOwningEntryRef entryRef(PackagesDirectoryRef(), kActivationFileName); 991 BFile file; 992 status_t error = file.SetTo(&entryRef, B_READ_ONLY); 993 if (error != B_OK) { 994 INFORM("Failed to open packages activation file: %s\n", 995 strerror(error)); 996 RETURN_ERROR(error); 997 } 998 999 // read the whole file into memory to simplify things 1000 off_t size; 1001 error = file.GetSize(&size); 1002 if (error != B_OK) { 1003 ERROR("Failed to packages activation file size: %s\n", 1004 strerror(error)); 1005 RETURN_ERROR(error); 1006 } 1007 1008 if (size > (off_t)kMaxActivationFileSize) { 1009 ERROR("The packages activation file is too big.\n"); 1010 RETURN_ERROR(B_BAD_DATA); 1011 } 1012 1013 char* fileContent = (char*)malloc(size + 1); 1014 if (fileContent == NULL) 1015 RETURN_ERROR(B_NO_MEMORY); 1016 MemoryDeleter fileContentDeleter(fileContent); 1017 1018 ssize_t bytesRead = file.Read(fileContent, size); 1019 if (bytesRead < 0) { 1020 ERROR("Failed to read packages activation file: %s\n", 1021 strerror(bytesRead)); 1022 RETURN_ERROR(errno); 1023 } 1024 1025 if (bytesRead != size) { 1026 ERROR("Failed to read whole packages activation file.\n"); 1027 RETURN_ERROR(B_ERROR); 1028 } 1029 1030 // null-terminate to simplify parsing 1031 fileContent[size] = '\0'; 1032 1033 AutoLocker<BLocker> locker(fLock); 1034 1035 // parse the file and mark the respective packages active 1036 const char* packageName = fileContent; 1037 char* const fileContentEnd = fileContent + size; 1038 while (packageName < fileContentEnd) { 1039 char* packageNameEnd = strchr(packageName, '\n'); 1040 if (packageNameEnd == NULL) 1041 packageNameEnd = fileContentEnd; 1042 1043 // skip empty lines 1044 if (packageName == packageNameEnd) { 1045 packageName++; 1046 continue; 1047 } 1048 *packageNameEnd = '\0'; 1049 1050 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) { 1051 ERROR("Invalid packages activation file content.\n"); 1052 RETURN_ERROR(B_BAD_DATA); 1053 } 1054 1055 Package* package = fLatestState->FindPackage(packageName); 1056 if (package != NULL) { 1057 fLatestState->SetPackageActive(package, true); 1058 fChangeCount++; 1059 } else { 1060 WARN("Package \"%s\" from activation file not in packages " 1061 "directory.\n", packageName); 1062 } 1063 1064 packageName = packageNameEnd + 1; 1065 } 1066 1067 return B_OK; 1068 } 1069 1070 1071 status_t 1072 Volume::_GetActivePackages(int fd) 1073 { 1074 // get the info from packagefs 1075 PackageFSGetPackageInfosRequest* request = NULL; 1076 MemoryDeleter requestDeleter; 1077 size_t bufferSize = 64 * 1024; 1078 for (;;) { 1079 request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize); 1080 if (request == NULL) 1081 RETURN_ERROR(B_NO_MEMORY); 1082 requestDeleter.SetTo(request); 1083 1084 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request, 1085 bufferSize) != 0) { 1086 ERROR("Volume::_GetActivePackages(): failed to get active package " 1087 "info from package FS: %s\n", strerror(errno)); 1088 RETURN_ERROR(errno); 1089 } 1090 1091 if (request->bufferSize <= bufferSize) 1092 break; 1093 1094 bufferSize = request->bufferSize; 1095 requestDeleter.Unset(); 1096 } 1097 1098 INFORM("latest volume state:\n"); 1099 _DumpState(fLatestState); 1100 1101 // check whether that matches the expected state 1102 if (_CheckActivePackagesMatchLatestState(request)) { 1103 INFORM("The latest volume state is also the currently active one\n"); 1104 fActiveState = fLatestState; 1105 return B_OK; 1106 } 1107 1108 // There's a mismatch. We need a new state that reflects the actual 1109 // activation situation. 1110 VolumeState* state = new(std::nothrow) VolumeState; 1111 if (state == NULL) 1112 RETURN_ERROR(B_NO_MEMORY); 1113 ObjectDeleter<VolumeState> stateDeleter(state); 1114 1115 for (uint32 i = 0; i < request->packageCount; i++) { 1116 const PackageFSPackageInfo& info = request->infos[i]; 1117 NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID, 1118 info.name); 1119 Package* package; 1120 status_t error = fPackageFileManager->CreatePackage(entryRef, package); 1121 if (error != B_OK) { 1122 WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %" 1123 B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID, 1124 info.directoryNodeID, info.name, strerror(error)); 1125 continue; 1126 } 1127 1128 state->AddPackage(package); 1129 state->SetPackageActive(package, true); 1130 } 1131 1132 INFORM("currently active volume state:\n"); 1133 _DumpState(state); 1134 1135 fActiveState = stateDeleter.Detach(); 1136 return B_OK; 1137 } 1138 1139 1140 void 1141 Volume::_RunQueuedScripts() 1142 { 1143 BDirectory adminDirectory; 1144 status_t error = _OpenPackagesSubDirectory( 1145 RelativePath(kAdminDirectoryName), false, adminDirectory); 1146 if (error != B_OK) 1147 return; 1148 1149 BDirectory scriptsDirectory; 1150 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName); 1151 if (error != B_OK) 1152 return; 1153 1154 // enumerate all the symlinks in the queued scripts directory 1155 BEntry scriptEntry; 1156 while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) { 1157 BPath scriptPath; 1158 scriptEntry.GetPath(&scriptPath); 1159 error = scriptPath.InitCheck(); 1160 if (error != B_OK) { 1161 INFORM("failed to get path of post-installation script \"%s\"\n", 1162 strerror(error)); 1163 continue; 1164 } 1165 1166 errno = 0; 1167 int result = system(scriptPath.Path()); 1168 if (result != 0) { 1169 INFORM("running post-installation script \"%s\" " 1170 "failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno)); 1171 } 1172 1173 // remove the symlink, now that we've run the post-installation script 1174 error = scriptEntry.Remove(); 1175 if (error != B_OK) { 1176 INFORM("removing queued post-install script failed \"%s\"\n", 1177 strerror(error)); 1178 } 1179 } 1180 } 1181 1182 1183 bool 1184 Volume::_CheckActivePackagesMatchLatestState( 1185 PackageFSGetPackageInfosRequest* request) 1186 { 1187 if (fPackagesDirectoryCount != 1) { 1188 INFORM("An old packages state (\"%s\") seems to be active.\n", 1189 fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String()); 1190 return false; 1191 } 1192 1193 const node_ref packagesDirRef(PackagesDirectoryRef()); 1194 1195 // mark the returned packages active 1196 for (uint32 i = 0; i < request->packageCount; i++) { 1197 const PackageFSPackageInfo& info = request->infos[i]; 1198 if (node_ref(info.directoryDeviceID, info.directoryNodeID) 1199 != packagesDirRef) { 1200 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1201 ") not in packages directory\n", info.name, 1202 info.packageDeviceID, info.packageNodeID); 1203 return false; 1204 } 1205 1206 Package* package = fLatestState->FindPackage( 1207 node_ref(info.packageDeviceID, info.packageNodeID)); 1208 if (package == NULL || !package->IsActive()) { 1209 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1210 ") not %s\n", info.name, 1211 info.packageDeviceID, info.packageNodeID, 1212 package == NULL 1213 ? "found in packages directory" : "supposed to be active"); 1214 return false; 1215 } 1216 } 1217 1218 // Check whether there are packages that aren't active but should be. 1219 uint32 count = 0; 1220 for (PackageNodeRefHashTable::Iterator it 1221 = fLatestState->ByNodeRefIterator(); it.HasNext();) { 1222 Package* package = it.Next(); 1223 if (package->IsActive()) 1224 count++; 1225 } 1226 1227 if (count != request->packageCount) { 1228 INFORM("There seem to be packages in the packages directory that " 1229 "should be active.\n"); 1230 return false; 1231 } 1232 1233 return true; 1234 } 1235 1236 1237 void 1238 Volume::_SetLatestState(VolumeState* state, bool isActive) 1239 { 1240 AutoLocker<BLocker> locker(fLock); 1241 if (isActive) { 1242 if (fLatestState != fActiveState) 1243 delete fActiveState; 1244 fActiveState = state; 1245 } 1246 1247 if (fLatestState != fActiveState) 1248 delete fLatestState; 1249 fLatestState = state; 1250 fChangeCount++; 1251 1252 locker.Unlock(); 1253 1254 // Send a notification, if this is a system root volume. 1255 if (fRoot->IsSystemRoot()) { 1256 BMessage message(B_PACKAGE_UPDATE); 1257 if (message.AddInt32("event", 1258 (int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK 1259 && message.AddInt32("location", (int32)Location()) == B_OK 1260 && message.AddInt64("change count", fChangeCount) == B_OK) { 1261 BRoster::Private().SendTo(&message, NULL, false); 1262 } 1263 } 1264 } 1265 1266 1267 void 1268 Volume::_DumpState(VolumeState* state) 1269 { 1270 uint32 inactiveCount = 0; 1271 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1272 it.HasNext();) { 1273 Package* package = it.Next(); 1274 if (package->IsActive()) { 1275 INFORM("active package: \"%s\"\n", package->FileName().String()); 1276 } else 1277 inactiveCount++; 1278 } 1279 1280 if (inactiveCount == 0) 1281 return; 1282 1283 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1284 it.HasNext();) { 1285 Package* package = it.Next(); 1286 if (!package->IsActive()) 1287 INFORM("inactive package: \"%s\"\n", package->FileName().String()); 1288 } 1289 } 1290 1291 1292 status_t 1293 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository, 1294 bool activeOnly, bool installed) 1295 { 1296 status_t error = repository.SetTo(Path()); 1297 if (error != B_OK) { 1298 ERROR("Volume::_AddRepository(): failed to init repository: %s\n", 1299 strerror(error)); 1300 return error; 1301 } 1302 1303 repository.SetInstalled(installed); 1304 1305 error = AddPackagesToRepository(repository, true); 1306 if (error != B_OK) { 1307 ERROR("Volume::_AddRepository(): failed to add packages to " 1308 "repository: %s\n", strerror(error)); 1309 return error; 1310 } 1311 1312 error = solver->AddRepository(&repository); 1313 if (error != B_OK) { 1314 ERROR("Volume::_AddRepository(): failed to add repository to solver: " 1315 "%s\n", strerror(error)); 1316 return error; 1317 } 1318 1319 return B_OK; 1320 } 1321 1322 1323 status_t 1324 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create, 1325 BDirectory& _directory) 1326 { 1327 // open the packages directory 1328 BDirectory directory; 1329 status_t error = directory.SetTo(&PackagesDirectoryRef()); 1330 if (error != B_OK) { 1331 ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages " 1332 "directory: %s\n", strerror(error)); 1333 RETURN_ERROR(error); 1334 } 1335 1336 return FSUtils::OpenSubDirectory(directory, path, create, _directory); 1337 } 1338 1339 1340 void 1341 Volume::_CommitTransaction(BMessage* message, 1342 const BActivationTransaction* transaction, 1343 const PackageSet& packagesAlreadyAdded, 1344 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) 1345 { 1346 _result.Unset(); 1347 1348 // perform the request 1349 CommitTransactionHandler handler(this, fPackageFileManager, _result); 1350 BTransactionError error = B_TRANSACTION_INTERNAL_ERROR; 1351 try { 1352 handler.Init(fLatestState, fLatestState == fActiveState, 1353 packagesAlreadyAdded, packagesAlreadyRemoved); 1354 1355 if (message != NULL) 1356 handler.HandleRequest(message); 1357 else if (transaction != NULL) 1358 handler.HandleRequest(*transaction); 1359 else 1360 handler.HandleRequest(); 1361 1362 _SetLatestState(handler.DetachVolumeState(), 1363 handler.IsActiveVolumeState()); 1364 error = B_TRANSACTION_OK; 1365 } catch (Exception& exception) { 1366 error = exception.Error(); 1367 exception.SetOnResult(_result); 1368 if (_result.ErrorPackage().IsEmpty() 1369 && handler.CurrentPackage() != NULL) { 1370 _result.SetErrorPackage(handler.CurrentPackage()->FileName()); 1371 } 1372 } catch (std::bad_alloc& exception) { 1373 error = B_TRANSACTION_NO_MEMORY; 1374 } 1375 1376 _result.SetError(B_TRANSACTION_OK); 1377 1378 // revert on error 1379 if (error != B_TRANSACTION_OK) 1380 handler.Revert(); 1381 } 1382