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 INFORM("latest volume state:\n"); 1182 _DumpState(fLatestState); 1183 1184 // check whether that matches the expected state 1185 if (_CheckActivePackagesMatchLatestState(request)) { 1186 INFORM("The latest volume state is also the currently active one\n"); 1187 fActiveState = fLatestState; 1188 return B_OK; 1189 } 1190 1191 // There's a mismatch. We need a new state that reflects the actual 1192 // activation situation. 1193 VolumeState* state = new(std::nothrow) VolumeState; 1194 if (state == NULL) 1195 RETURN_ERROR(B_NO_MEMORY); 1196 ObjectDeleter<VolumeState> stateDeleter(state); 1197 1198 for (uint32 i = 0; i < request->packageCount; i++) { 1199 const PackageFSPackageInfo& info = request->infos[i]; 1200 NotOwningEntryRef entryRef(info.directoryDeviceID, info.directoryNodeID, 1201 info.name); 1202 Package* package; 1203 status_t error = fPackageFileManager->CreatePackage(entryRef, package); 1204 if (error != B_OK) { 1205 WARN("Failed to create package (dev: %" B_PRIdDEV ", node: %" 1206 B_PRIdINO ", \"%s\"): %s\n", info.directoryDeviceID, 1207 info.directoryNodeID, info.name, strerror(error)); 1208 continue; 1209 } 1210 1211 state->AddPackage(package); 1212 state->SetPackageActive(package, true); 1213 } 1214 1215 INFORM("currently active volume state:\n"); 1216 _DumpState(state); 1217 1218 fActiveState = stateDeleter.Detach(); 1219 return B_OK; 1220 } 1221 1222 1223 void 1224 Volume::_RunQueuedScripts() 1225 { 1226 BDirectory adminDirectory; 1227 status_t error = _OpenPackagesSubDirectory( 1228 RelativePath(kAdminDirectoryName), false, adminDirectory); 1229 if (error != B_OK) 1230 return; 1231 1232 BDirectory scriptsDirectory; 1233 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName); 1234 if (error != B_OK) 1235 return; 1236 1237 // enumerate all the symlinks in the queued scripts directory 1238 BEntry scriptEntry; 1239 while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) { 1240 BPath scriptPath; 1241 scriptEntry.GetPath(&scriptPath); 1242 error = scriptPath.InitCheck(); 1243 if (error != B_OK) { 1244 INFORM("failed to get path of post-installation script \"%s\"\n", 1245 strerror(error)); 1246 continue; 1247 } 1248 1249 errno = 0; 1250 int result = system(scriptPath.Path()); 1251 if (result != 0) { 1252 INFORM("running post-installation script \"%s\" " 1253 "failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno)); 1254 } 1255 1256 // remove the symlink, now that we've run the post-installation script 1257 error = scriptEntry.Remove(); 1258 if (error != B_OK) { 1259 INFORM("removing queued post-install script failed \"%s\"\n", 1260 strerror(error)); 1261 } 1262 } 1263 } 1264 1265 1266 bool 1267 Volume::_CheckActivePackagesMatchLatestState( 1268 PackageFSGetPackageInfosRequest* request) 1269 { 1270 if (fPackagesDirectoryCount != 1) { 1271 INFORM("An old packages state (\"%s\") seems to be active.\n", 1272 fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String()); 1273 return false; 1274 } 1275 1276 const node_ref packagesDirRef(PackagesDirectoryRef()); 1277 1278 // mark the returned packages active 1279 for (uint32 i = 0; i < request->packageCount; i++) { 1280 const PackageFSPackageInfo& info = request->infos[i]; 1281 if (node_ref(info.directoryDeviceID, info.directoryNodeID) 1282 != packagesDirRef) { 1283 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1284 ") not in packages directory\n", info.name, 1285 info.packageDeviceID, info.packageNodeID); 1286 return false; 1287 } 1288 1289 Package* package = fLatestState->FindPackage( 1290 node_ref(info.packageDeviceID, info.packageNodeID)); 1291 if (package == NULL || !package->IsActive()) { 1292 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1293 ") not %s\n", info.name, 1294 info.packageDeviceID, info.packageNodeID, 1295 package == NULL 1296 ? "found in packages directory" : "supposed to be active"); 1297 return false; 1298 } 1299 } 1300 1301 // Check whether there are packages that aren't active but should be. 1302 uint32 count = 0; 1303 for (PackageNodeRefHashTable::Iterator it 1304 = fLatestState->ByNodeRefIterator(); it.HasNext();) { 1305 Package* package = it.Next(); 1306 if (package->IsActive()) 1307 count++; 1308 } 1309 1310 if (count != request->packageCount) { 1311 INFORM("There seem to be packages in the packages directory that " 1312 "should be active.\n"); 1313 return false; 1314 } 1315 1316 return true; 1317 } 1318 1319 1320 void 1321 Volume::_SetLatestState(VolumeState* state, bool isActive) 1322 { 1323 AutoLocker<BLocker> locker(fLock); 1324 1325 bool sendNotification = fRoot->IsSystemRoot(); 1326 // Send a notification, if this is a system root volume. 1327 BStringList addedPackageNames; 1328 BStringList removedPackageNames; 1329 1330 // If a notification should be sent then assemble the latest and incoming 1331 // set of the packages' names. This can be used to figure out which 1332 // packages are added and which are removed. 1333 1334 if (sendNotification) { 1335 _CollectPackageNamesAdded(fLatestState, state, addedPackageNames); 1336 _CollectPackageNamesAdded(state, fLatestState, removedPackageNames); 1337 } 1338 1339 if (isActive) { 1340 if (fLatestState != fActiveState) 1341 delete fActiveState; 1342 fActiveState = state; 1343 } 1344 1345 if (fLatestState != fActiveState) 1346 delete fLatestState; 1347 fLatestState = state; 1348 fChangeCount++; 1349 1350 locker.Unlock(); 1351 1352 // Send a notification, if this is a system root volume. 1353 if (sendNotification) { 1354 BMessage message(B_PACKAGE_UPDATE); 1355 if (message.AddInt32("event", 1356 (int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK 1357 && message.AddStrings("added package names", 1358 addedPackageNames) == B_OK 1359 && message.AddStrings("removed package names", 1360 removedPackageNames) == B_OK 1361 && message.AddInt32("location", (int32)Location()) == B_OK 1362 && message.AddInt64("change count", fChangeCount) == B_OK) { 1363 BRoster::Private().SendTo(&message, NULL, false); 1364 } 1365 } 1366 } 1367 1368 1369 /*static*/ void 1370 Volume::_CollectPackageNamesAdded(const VolumeState* oldState, 1371 const VolumeState* newState, BStringList& addedPackageNames) 1372 { 1373 if (newState == NULL) 1374 return; 1375 1376 for (PackageFileNameHashTable::Iterator it 1377 = newState->ByFileNameIterator(); it.HasNext();) { 1378 Package* package = it.Next(); 1379 BString packageName = package->Info().Name(); 1380 if (oldState == NULL) 1381 addedPackageNames.Add(packageName); 1382 else { 1383 Package* oldStatePackage = oldState->FindPackage( 1384 package->FileName()); 1385 if (oldStatePackage == NULL) 1386 addedPackageNames.Add(packageName); 1387 } 1388 } 1389 } 1390 1391 1392 void 1393 Volume::_DumpState(VolumeState* state) 1394 { 1395 uint32 inactiveCount = 0; 1396 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1397 it.HasNext();) { 1398 Package* package = it.Next(); 1399 if (package->IsActive()) { 1400 INFORM("active package: \"%s\"\n", package->FileName().String()); 1401 } else 1402 inactiveCount++; 1403 } 1404 1405 if (inactiveCount == 0) 1406 return; 1407 1408 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1409 it.HasNext();) { 1410 Package* package = it.Next(); 1411 if (!package->IsActive()) 1412 INFORM("inactive package: \"%s\"\n", package->FileName().String()); 1413 } 1414 } 1415 1416 1417 status_t 1418 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository, 1419 bool activeOnly, bool installed) 1420 { 1421 status_t error = repository.SetTo(Path()); 1422 if (error != B_OK) { 1423 ERROR("Volume::_AddRepository(): failed to init repository: %s\n", 1424 strerror(error)); 1425 return error; 1426 } 1427 1428 repository.SetInstalled(installed); 1429 1430 error = AddPackagesToRepository(repository, true); 1431 if (error != B_OK) { 1432 ERROR("Volume::_AddRepository(): failed to add packages to " 1433 "repository: %s\n", strerror(error)); 1434 return error; 1435 } 1436 1437 error = solver->AddRepository(&repository); 1438 if (error != B_OK) { 1439 ERROR("Volume::_AddRepository(): failed to add repository to solver: " 1440 "%s\n", strerror(error)); 1441 return error; 1442 } 1443 1444 return B_OK; 1445 } 1446 1447 1448 status_t 1449 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create, 1450 BDirectory& _directory) 1451 { 1452 // open the packages directory 1453 BDirectory directory; 1454 status_t error = directory.SetTo(&PackagesDirectoryRef()); 1455 if (error != B_OK) { 1456 ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages " 1457 "directory: %s\n", strerror(error)); 1458 RETURN_ERROR(error); 1459 } 1460 1461 return FSUtils::OpenSubDirectory(directory, path, create, _directory); 1462 } 1463 1464 1465 void 1466 Volume::_CommitTransaction(BMessage* message, 1467 const BActivationTransaction* transaction, 1468 const PackageSet& packagesAlreadyAdded, 1469 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) 1470 { 1471 _result.Unset(); 1472 1473 // perform the request 1474 CommitTransactionHandler handler(this, fPackageFileManager, _result); 1475 BTransactionError error = B_TRANSACTION_INTERNAL_ERROR; 1476 try { 1477 handler.Init(fLatestState, fLatestState == fActiveState, 1478 packagesAlreadyAdded, packagesAlreadyRemoved); 1479 1480 if (message != NULL) 1481 handler.HandleRequest(message); 1482 else if (transaction != NULL) 1483 handler.HandleRequest(*transaction); 1484 else 1485 handler.HandleRequest(); 1486 1487 _SetLatestState(handler.DetachVolumeState(), 1488 handler.IsActiveVolumeState()); 1489 error = B_TRANSACTION_OK; 1490 } catch (Exception& exception) { 1491 error = exception.Error(); 1492 exception.SetOnResult(_result); 1493 if (_result.ErrorPackage().IsEmpty() 1494 && handler.CurrentPackage() != NULL) { 1495 _result.SetErrorPackage(handler.CurrentPackage()->FileName()); 1496 } 1497 } catch (std::bad_alloc& exception) { 1498 error = B_TRANSACTION_NO_MEMORY; 1499 } 1500 1501 _result.SetError(error); 1502 1503 // revert on error 1504 if (error != B_TRANSACTION_OK) 1505 handler.Revert(); 1506 } 1507