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