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