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