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 int fd = directory.Dup(); 235 if (fd < 0) { 236 ERROR("Volume::Init(): failed to get root directory FD: %s\n", 237 strerror(fd)); 238 RETURN_ERROR(fd); 239 } 240 FileDescriptorCloser fdCloser(fd); 241 242 // get the volume info from packagefs 243 uint32 maxPackagesDirCount = 16; 244 PackageFSVolumeInfo* info = NULL; 245 MemoryDeleter infoDeleter; 246 size_t bufferSize; 247 for (;;) { 248 bufferSize = sizeof(PackageFSVolumeInfo) 249 + (maxPackagesDirCount - 1) * sizeof(PackageFSDirectoryInfo); 250 info = (PackageFSVolumeInfo*)malloc(bufferSize); 251 if (info == NULL) 252 RETURN_ERROR(B_NO_MEMORY); 253 infoDeleter.SetTo(info); 254 255 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_VOLUME_INFO, info, 256 bufferSize) != 0) { 257 ERROR("Volume::Init(): failed to get volume info: %s\n", 258 strerror(errno)); 259 RETURN_ERROR(errno); 260 } 261 262 if (info->packagesDirectoryCount <= maxPackagesDirCount) 263 break; 264 265 maxPackagesDirCount = info->packagesDirectoryCount; 266 infoDeleter.Unset(); 267 } 268 269 if (info->packagesDirectoryCount < 1) { 270 ERROR("Volume::Init(): got invalid volume info from packagefs\n"); 271 RETURN_ERROR(B_BAD_VALUE); 272 } 273 274 fMountType = info->mountType; 275 276 fPackagesDirectories = new(std::nothrow) PackagesDirectory[ 277 info->packagesDirectoryCount]; 278 if (fPackagesDirectories == NULL) 279 RETURN_ERROR(B_NO_MEMORY); 280 281 fPackagesDirectoryCount = info->packagesDirectoryCount; 282 283 for (uint32 i = 0; i < info->packagesDirectoryCount; i++) { 284 fPackagesDirectories[i].Init( 285 node_ref(info->packagesDirectoryInfos[i].deviceID, 286 info->packagesDirectoryInfos[i].nodeID), 287 i == 0); 288 } 289 290 _packageRootRef.device = info->rootDeviceID; 291 _packageRootRef.node = info->rootDirectoryID; 292 293 return B_OK; 294 } 295 296 297 status_t 298 Volume::InitPackages(Listener* listener) 299 { 300 // node-monitor the volume's packages directory 301 status_t error = watch_node(&PackagesDirectoryRef(), B_WATCH_DIRECTORY, 302 BMessenger(this)); 303 if (error == B_OK) { 304 fListener = listener; 305 } else { 306 ERROR("Volume::InitPackages(): failed to start watching the packages " 307 "directory of the volume at \"%s\": %s\n", 308 fPath.String(), strerror(error)); 309 // Not good, but not fatal. Only the manual package operations in the 310 // packages directory won't work correctly. 311 } 312 313 // read the packages directory and get the active packages 314 int fd = OpenRootDirectory(); 315 if (fd < 0) { 316 ERROR("Volume::InitPackages(): failed to open root directory: %s\n", 317 strerror(fd)); 318 RETURN_ERROR(fd); 319 } 320 FileDescriptorCloser fdCloser(fd); 321 322 error = _ReadPackagesDirectory(); 323 if (error != B_OK) 324 RETURN_ERROR(error); 325 326 error = _InitLatestState(); 327 if (error != B_OK) 328 RETURN_ERROR(error); 329 330 error = _GetActivePackages(fd); 331 if (error != B_OK) 332 RETURN_ERROR(error); 333 334 // create the admin directory, if it doesn't exist yet 335 BDirectory packagesDirectory; 336 bool createdAdminDirectory = false; 337 if (packagesDirectory.SetTo(&PackagesDirectoryRef()) == B_OK) { 338 if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists()) { 339 packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL); 340 createdAdminDirectory = true; 341 } 342 } 343 BDirectory adminDirectory(&packagesDirectory, kAdminDirectoryName); 344 error = adminDirectory.InitCheck(); 345 if (error != B_OK) 346 RETURN_ERROR(error); 347 348 // First boot processing requested by a magic file left by the OS installer? 349 BEntry flagFileEntry(&adminDirectory, kFirstBootProcessingNeededFileName); 350 if (createdAdminDirectory || flagFileEntry.Exists()) { 351 INFORM("Volume::InitPackages Requesting delayed first boot processing " 352 "for packages dir %s.\n", BPath(&packagesDirectory).Path()); 353 if (flagFileEntry.Exists()) 354 flagFileEntry.Remove(); // Remove early on to avoid an error loop. 355 356 // Are there any packages needing processing? Don't want to create an 357 // empty transaction directory and then never have it cleaned up when 358 // the empty transaction gets rejected. 359 bool anyPackages = false; 360 for (PackageNodeRefHashTable::Iterator it = 361 fActiveState->ByNodeRefIterator(); it.HasNext();) { 362 Package* package = it.Next(); 363 if (package->IsActive()) { 364 anyPackages = true; 365 break; 366 } 367 } 368 369 if (anyPackages) { 370 // Create first boot processing special transaction for current 371 // volume, which also creates an empty transaction directory. 372 BPackageInstallationLocation location = Location(); 373 BDirectory transactionDirectory; 374 BActivationTransaction transaction; 375 error = CreateTransaction(location, transaction, 376 transactionDirectory); 377 if (error != B_OK) 378 RETURN_ERROR(error); 379 380 // Add all package files in currently active state to transaction. 381 for (PackageNodeRefHashTable::Iterator it = 382 fActiveState->ByNodeRefIterator(); it.HasNext();) { 383 Package* package = it.Next(); 384 if (package->IsActive()) { 385 if (!transaction.AddPackageToActivate( 386 package->FileName().String())) 387 RETURN_ERROR(B_NO_MEMORY); 388 } 389 } 390 transaction.SetFirstBootProcessing(true); 391 392 // Queue up the transaction as a BMessage for processing a bit 393 // later, once the package daemon has finished initialising. 394 BMessage commitMessage(B_MESSAGE_COMMIT_TRANSACTION); 395 error = transaction.Archive(&commitMessage); 396 if (error != B_OK) 397 RETURN_ERROR(error); 398 BLooper *myLooper = Looper() ; 399 if (myLooper == NULL) 400 RETURN_ERROR(B_NOT_INITIALIZED); 401 error = myLooper->PostMessage(&commitMessage); 402 if (error != B_OK) 403 RETURN_ERROR(error); 404 } 405 } 406 407 return B_OK; 408 } 409 410 411 status_t 412 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly) 413 { 414 for (PackageFileNameHashTable::Iterator it 415 = fLatestState->ByFileNameIterator(); it.HasNext();) { 416 Package* package = it.Next(); 417 if (activeOnly && !package->IsActive()) 418 continue; 419 420 status_t error = repository.AddPackage(package->Info()); 421 if (error != B_OK) { 422 ERROR("Volume::AddPackagesToRepository(): failed to add package %s " 423 "to repository: %s\n", package->FileName().String(), 424 strerror(error)); 425 return error; 426 } 427 } 428 429 return B_OK; 430 } 431 432 433 void 434 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume) 435 { 436 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume); 437 // create the solver 438 BSolver* solver; 439 status_t error = BSolver::Create(solver); 440 if (error != B_OK) { 441 ERROR("Volume::InitialVerify(): failed to create solver: %s\n", 442 strerror(error)); 443 return; 444 } 445 ObjectDeleter<BSolver> solverDeleter(solver); 446 447 // add a repository with all active packages 448 BSolverRepository repository; 449 error = _AddRepository(solver, repository, true, true); 450 if (error != B_OK) { 451 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 452 strerror(error)); 453 return; 454 } 455 456 // add a repository for the next volume 457 BSolverRepository nextRepository; 458 if (nextVolume != NULL) { 459 nextRepository.SetPriority(1); 460 error = nextVolume->_AddRepository(solver, nextRepository, true, false); 461 if (error != B_OK) { 462 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 463 strerror(error)); 464 return; 465 } 466 } 467 468 // add a repository for the next next volume 469 BSolverRepository nextNextRepository; 470 if (nextNextVolume != NULL) { 471 nextNextRepository.SetPriority(2); 472 error = nextNextVolume->_AddRepository(solver, nextNextRepository, true, 473 false); 474 if (error != B_OK) { 475 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 476 strerror(error)); 477 return; 478 } 479 } 480 481 // verify 482 error = solver->VerifyInstallation(); 483 if (error != B_OK) { 484 ERROR("Volume::InitialVerify(): failed to verify: %s\n", 485 strerror(error)); 486 return; 487 } 488 489 if (!solver->HasProblems()) { 490 INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n", 491 Path().String()); 492 return; 493 } 494 495 // print the problems 496 // TODO: Notify the user ... 497 INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n", 498 Path().String()); 499 500 int32 problemCount = solver->CountProblems(); 501 for (int32 i = 0; i < problemCount; i++) { 502 BSolverProblem* problem = solver->ProblemAt(i); 503 INFORM(" %" B_PRId32 ": %s\n", i + 1, problem->ToString().String()); 504 int32 solutionCount = problem->CountSolutions(); 505 for (int32 k = 0; k < solutionCount; k++) { 506 const BSolverProblemSolution* solution = problem->SolutionAt(k); 507 INFORM(" solution %" B_PRId32 ":\n", k + 1); 508 int32 elementCount = solution->CountElements(); 509 for (int32 l = 0; l < elementCount; l++) { 510 const BSolverProblemSolutionElement* element 511 = solution->ElementAt(l); 512 INFORM(" - %s\n", element->ToString().String()); 513 } 514 } 515 } 516 } 517 518 519 void 520 Volume::HandleGetLocationInfoRequest(BMessage* message) 521 { 522 AutoLocker<BLocker> locker(fLock); 523 524 // If the cached reply message is up-to-date, just send it. 525 int64 changeCount; 526 if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK 527 && changeCount == fChangeCount) { 528 locker.Unlock(); 529 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 530 kCommunicationTimeout); 531 return; 532 } 533 534 // rebuild the reply message 535 fLocationInfoReply.MakeEmpty(); 536 537 if (fLocationInfoReply.AddInt32("base directory device", 538 fRootDirectoryRef.device) != B_OK 539 || fLocationInfoReply.AddInt64("base directory node", 540 fRootDirectoryRef.node) != B_OK 541 || fLocationInfoReply.AddInt32("packages directory device", 542 PackagesDeviceID()) != B_OK 543 || fLocationInfoReply.AddInt64("packages directory node", 544 PackagesDirectoryID()) != B_OK) { 545 return; 546 } 547 548 for (PackageFileNameHashTable::Iterator it 549 = fLatestState->ByFileNameIterator(); it.HasNext();) { 550 Package* package = it.Next(); 551 const char* fieldName = package->IsActive() 552 ? "latest active packages" : "latest inactive packages"; 553 BMessage packageArchive; 554 if (package->Info().Archive(&packageArchive) != B_OK 555 || fLocationInfoReply.AddMessage(fieldName, &packageArchive) 556 != B_OK) { 557 return; 558 } 559 } 560 561 if (fActiveState != fLatestState) { 562 if (fPackagesDirectoryCount > 1) { 563 fLocationInfoReply.AddString("old state", 564 fPackagesDirectories[fPackagesDirectoryCount - 1].Name()); 565 } 566 567 for (PackageFileNameHashTable::Iterator it 568 = fActiveState->ByFileNameIterator(); it.HasNext();) { 569 Package* package = it.Next(); 570 if (!package->IsActive()) 571 continue; 572 573 BMessage packageArchive; 574 if (package->Info().Archive(&packageArchive) != B_OK 575 || fLocationInfoReply.AddMessage("currently active packages", 576 &packageArchive) != B_OK) { 577 return; 578 } 579 } 580 } 581 582 if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK) 583 return; 584 585 locker.Unlock(); 586 587 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 588 kCommunicationTimeout); 589 } 590 591 592 void 593 Volume::HandleCommitTransactionRequest(BMessage* message) 594 { 595 BCommitTransactionResult result; 596 PackageSet dummy; 597 _CommitTransaction(message, NULL, dummy, dummy, result); 598 599 BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); 600 status_t error = result.AddToMessage(reply); 601 if (error != B_OK) { 602 ERROR("Volume::HandleCommitTransactionRequest(): Failed to add " 603 "transaction result to reply: %s\n", strerror(error)); 604 return; 605 } 606 607 message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout); 608 } 609 610 611 void 612 Volume::PackageJobPending() 613 { 614 atomic_add(&fPendingPackageJobCount, 1); 615 } 616 617 618 void 619 Volume::PackageJobFinished() 620 { 621 atomic_add(&fPendingPackageJobCount, -1); 622 } 623 624 625 bool 626 Volume::IsPackageJobPending() const 627 { 628 return fPendingPackageJobCount != 0; 629 } 630 631 632 void 633 Volume::Unmounted() 634 { 635 if (fListener != NULL) { 636 stop_watching(BMessenger(this)); 637 fListener = NULL; 638 } 639 640 if (BLooper* looper = Looper()) 641 looper->RemoveHandler(this); 642 } 643 644 645 void 646 Volume::MessageReceived(BMessage* message) 647 { 648 switch (message->what) { 649 case B_NODE_MONITOR: 650 { 651 int32 opcode; 652 if (message->FindInt32("opcode", &opcode) != B_OK) 653 break; 654 655 switch (opcode) { 656 case B_ENTRY_CREATED: 657 _HandleEntryCreatedOrRemoved(message, true); 658 break; 659 case B_ENTRY_REMOVED: 660 _HandleEntryCreatedOrRemoved(message, false); 661 break; 662 case B_ENTRY_MOVED: 663 _HandleEntryMoved(message); 664 break; 665 default: 666 break; 667 } 668 break; 669 } 670 671 case kHandleNodeMonitorEvents: 672 if (fListener != NULL) { 673 if (system_time() >= fNodeMonitorEventHandleTime) 674 fListener->VolumeNodeMonitorEventOccurred(this); 675 } 676 break; 677 678 default: 679 BHandler::MessageReceived(message); 680 break; 681 } 682 } 683 684 685 BPackageInstallationLocation 686 Volume::Location() const 687 { 688 switch (fMountType) { 689 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 690 return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM; 691 case PACKAGE_FS_MOUNT_TYPE_HOME: 692 return B_PACKAGE_INSTALLATION_LOCATION_HOME; 693 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 694 default: 695 return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT; 696 } 697 } 698 699 700 const node_ref& 701 Volume::PackagesDirectoryRef() const 702 { 703 return fPackagesDirectories[0].NodeRef(); 704 } 705 706 707 PackageFileNameHashTable::Iterator 708 Volume::PackagesByFileNameIterator() const 709 { 710 return fLatestState->ByFileNameIterator(); 711 } 712 713 714 int 715 Volume::OpenRootDirectory() const 716 { 717 BDirectory directory; 718 status_t error = directory.SetTo(&fRootDirectoryRef); 719 if (error != B_OK) { 720 ERROR("Volume::OpenRootDirectory(): failed to open root directory: " 721 "%s\n", strerror(error)); 722 RETURN_ERROR(error); 723 } 724 725 return directory.Dup(); 726 } 727 728 729 void 730 Volume::ProcessPendingNodeMonitorEvents() 731 { 732 // get the events 733 NodeMonitorEventList events; 734 { 735 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 736 events.MoveFrom(&fPendingNodeMonitorEvents); 737 } 738 739 // process them 740 while (NodeMonitorEvent* event = events.RemoveHead()) { 741 ObjectDeleter<NodeMonitorEvent> eventDeleter(event); 742 if (event->WasCreated()) 743 _PackagesEntryCreated(event->EntryName()); 744 else 745 _PackagesEntryRemoved(event->EntryName()); 746 } 747 } 748 749 750 bool 751 Volume::HasPendingPackageActivationChanges() const 752 { 753 return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty(); 754 } 755 756 757 void 758 Volume::ProcessPendingPackageActivationChanges() 759 { 760 if (!HasPendingPackageActivationChanges()) 761 return; 762 763 // perform the request 764 BCommitTransactionResult result; 765 _CommitTransaction(NULL, NULL, fPackagesToBeActivated, 766 fPackagesToBeDeactivated, result); 767 768 if (result.Error() != B_TRANSACTION_OK) { 769 ERROR("Volume::ProcessPendingPackageActivationChanges(): package " 770 "activation failed: %s\n", result.FullErrorMessage().String()); 771 // TODO: Notify the user! 772 } 773 774 // clear the activation/deactivation sets in any event 775 fPackagesToBeActivated.clear(); 776 fPackagesToBeDeactivated.clear(); 777 } 778 779 780 void 781 Volume::ClearPackageActivationChanges() 782 { 783 fPackagesToBeActivated.clear(); 784 fPackagesToBeDeactivated.clear(); 785 } 786 787 788 status_t 789 Volume::CreateTransaction(BPackageInstallationLocation location, 790 BActivationTransaction& _transaction, BDirectory& _transactionDirectory) 791 { 792 // open admin directory 793 BDirectory adminDirectory; 794 status_t error = _OpenPackagesSubDirectory( 795 RelativePath(kAdminDirectoryName), true, adminDirectory); 796 if (error != B_OK) 797 return error; 798 799 // create a transaction directory 800 int uniqueId = 1; 801 BString directoryName; 802 for (;; uniqueId++) { 803 directoryName.SetToFormat("transaction-%d", uniqueId); 804 if (directoryName.IsEmpty()) 805 return B_NO_MEMORY; 806 807 error = adminDirectory.CreateDirectory(directoryName, 808 &_transactionDirectory); 809 if (error == B_OK) 810 break; 811 if (error != B_FILE_EXISTS) 812 return error; 813 } 814 815 // init the transaction 816 error = _transaction.SetTo(location, fChangeCount, directoryName); 817 if (error != B_OK) { 818 BEntry entry; 819 _transactionDirectory.GetEntry(&entry); 820 _transactionDirectory.Unset(); 821 if (entry.InitCheck() == B_OK) 822 entry.Remove(); 823 return error; 824 } 825 826 return B_OK; 827 } 828 829 830 void 831 Volume::CommitTransaction(const BActivationTransaction& transaction, 832 const PackageSet& packagesAlreadyAdded, 833 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) 834 { 835 _CommitTransaction(NULL, &transaction, packagesAlreadyAdded, 836 packagesAlreadyRemoved, _result); 837 } 838 839 840 void 841 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created) 842 { 843 // only moves to or from our packages directory are interesting 844 int32 deviceID; 845 int64 directoryID; 846 const char* name; 847 if (message->FindInt32("device", &deviceID) != B_OK 848 || message->FindInt64("directory", &directoryID) != B_OK 849 || message->FindString("name", &name) != B_OK 850 || node_ref(deviceID, directoryID) != PackagesDirectoryRef()) { 851 return; 852 } 853 854 _QueueNodeMonitorEvent(name, created); 855 } 856 857 858 void 859 Volume::_HandleEntryMoved(const BMessage* message) 860 { 861 int32 deviceID; 862 int64 fromDirectoryID; 863 int64 toDirectoryID; 864 const char* fromName; 865 const char* toName; 866 if (message->FindInt32("device", &deviceID) != B_OK 867 || message->FindInt64("from directory", &fromDirectoryID) != B_OK 868 || message->FindInt64("to directory", &toDirectoryID) != B_OK 869 || message->FindString("from name", &fromName) != B_OK 870 || message->FindString("name", &toName) != B_OK 871 || deviceID != PackagesDeviceID() 872 || (fromDirectoryID != PackagesDirectoryID() 873 && toDirectoryID != PackagesDirectoryID())) { 874 return; 875 } 876 877 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 878 // make sure for a move the two events cannot get split 879 880 if (fromDirectoryID == PackagesDirectoryID()) 881 _QueueNodeMonitorEvent(fromName, false); 882 if (toDirectoryID == PackagesDirectoryID()) 883 _QueueNodeMonitorEvent(toName, true); 884 } 885 886 887 void 888 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated) 889 { 890 if (name.IsEmpty()) { 891 ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n"); 892 return; 893 } 894 895 // ignore entries that don't have the ".hpkg" extension 896 if (!name.EndsWith(kPackageFileNameExtension)) 897 return; 898 899 NodeMonitorEvent* event 900 = new(std::nothrow) NodeMonitorEvent(name, wasCreated); 901 if (event == NULL) { 902 ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n"); 903 return; 904 } 905 906 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 907 fPendingNodeMonitorEvents.Add(event); 908 eventsLock.Unlock(); 909 910 fNodeMonitorEventHandleTime 911 = system_time() + kNodeMonitorEventHandlingDelay; 912 BMessage message(kHandleNodeMonitorEvents); 913 BMessageRunner::StartSending(this, &message, kNodeMonitorEventHandlingDelay, 914 1); 915 } 916 917 918 void 919 Volume::_PackagesEntryCreated(const char* name) 920 { 921 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name); 922 // Ignore the event, if the package is already known. 923 Package* package = fLatestState->FindPackage(name); 924 if (package != NULL) { 925 if (package->File()->EntryCreatedIgnoreLevel() > 0) { 926 package->File()->DecrementEntryCreatedIgnoreLevel(); 927 } else { 928 WARN("node monitoring created event for already known entry " 929 "\"%s\"\n", name); 930 } 931 932 // Remove the package from the packages-to-be-deactivated set, if it is in 933 // there (unlikely, unless we see a remove-create sequence). 934 PackageSet::iterator it = fPackagesToBeDeactivated.find(package); 935 if (it != fPackagesToBeDeactivated.end()) 936 fPackagesToBeDeactivated.erase(it); 937 938 return; 939 } 940 941 status_t error = fPackageFileManager->CreatePackage( 942 NotOwningEntryRef(PackagesDirectoryRef(), name), 943 package); 944 if (error != B_OK) { 945 ERROR("failed to init package for file \"%s\"\n", name); 946 return; 947 } 948 949 fLock.Lock(); 950 fLatestState->AddPackage(package); 951 fChangeCount++; 952 fLock.Unlock(); 953 954 try { 955 fPackagesToBeActivated.insert(package); 956 } catch (std::bad_alloc& exception) { 957 ERROR("out of memory\n"); 958 return; 959 } 960 } 961 962 963 void 964 Volume::_PackagesEntryRemoved(const char* name) 965 { 966 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name); 967 Package* package = fLatestState->FindPackage(name); 968 if (package == NULL) 969 return; 970 971 // Ignore the event, if we generated it ourselves. 972 if (package->File()->EntryRemovedIgnoreLevel() > 0) { 973 package->File()->DecrementEntryRemovedIgnoreLevel(); 974 return; 975 } 976 977 // Remove the package from the packages-to-be-activated set, if it is in 978 // there (unlikely, unless we see a create-remove-create sequence). 979 PackageSet::iterator it = fPackagesToBeActivated.find(package); 980 if (it != fPackagesToBeActivated.end()) 981 fPackagesToBeActivated.erase(it); 982 983 // If the package isn't active, just remove it for good. 984 if (!package->IsActive()) { 985 AutoLocker<BLocker> locker(fLock); 986 fLatestState->RemovePackage(package); 987 fChangeCount++; 988 delete package; 989 return; 990 } 991 992 // The package must be deactivated. 993 try { 994 fPackagesToBeDeactivated.insert(package); 995 } catch (std::bad_alloc& exception) { 996 ERROR("out of memory\n"); 997 return; 998 } 999 } 1000 1001 1002 status_t 1003 Volume::_ReadPackagesDirectory() 1004 { 1005 BDirectory directory; 1006 status_t error = directory.SetTo(&PackagesDirectoryRef()); 1007 if (error != B_OK) { 1008 ERROR("Volume::_ReadPackagesDirectory(): failed to open packages " 1009 "directory: %s\n", strerror(error)); 1010 RETURN_ERROR(error); 1011 } 1012 1013 entry_ref entry; 1014 while (directory.GetNextRef(&entry) == B_OK) { 1015 if (!BString(entry.name).EndsWith(kPackageFileNameExtension)) 1016 continue; 1017 1018 Package* package; 1019 status_t error = fPackageFileManager->CreatePackage(entry, package); 1020 if (error == B_OK) { 1021 AutoLocker<BLocker> locker(fLock); 1022 fLatestState->AddPackage(package); 1023 fChangeCount++; 1024 } 1025 } 1026 1027 return B_OK; 1028 } 1029 1030 1031 status_t 1032 Volume::_InitLatestState() 1033 { 1034 if (_InitLatestStateFromActivatedPackages() == B_OK) 1035 return B_OK; 1036 1037 INFORM("Failed to get activated packages info from activated packages file." 1038 " Assuming all package files in package directory are activated.\n"); 1039 1040 AutoLocker<BLocker> locker(fLock); 1041 1042 for (PackageFileNameHashTable::Iterator it 1043 = fLatestState->ByFileNameIterator(); 1044 Package* package = it.Next();) { 1045 fLatestState->SetPackageActive(package, true); 1046 fChangeCount++; 1047 } 1048 1049 return B_OK; 1050 } 1051 1052 1053 status_t 1054 Volume::_InitLatestStateFromActivatedPackages() 1055 { 1056 // open admin directory 1057 BDirectory adminDirectory; 1058 status_t error = _OpenPackagesSubDirectory( 1059 RelativePath(kAdminDirectoryName), false, adminDirectory); 1060 if (error != B_OK) 1061 RETURN_ERROR(error); 1062 1063 node_ref adminNode; 1064 error = adminDirectory.GetNodeRef(&adminNode); 1065 if (error != B_OK) 1066 RETURN_ERROR(error); 1067 1068 // try reading the activation file 1069 NotOwningEntryRef entryRef(adminNode, kActivationFileName); 1070 BFile file; 1071 error = file.SetTo(&entryRef, B_READ_ONLY); 1072 if (error != B_OK) { 1073 BEntry activationEntry(&entryRef); 1074 BPath activationPath; 1075 const char *activationFilePathName = "Unknown due to errors"; 1076 if (activationEntry.InitCheck() == B_OK && 1077 activationEntry.GetPath(&activationPath) == B_OK) 1078 activationFilePathName = activationPath.Path(); 1079 INFORM("Failed to open packages activation file %s: %s\n", 1080 activationFilePathName, strerror(error)); 1081 RETURN_ERROR(error); 1082 } 1083 1084 // read the whole file into memory to simplify things 1085 off_t size; 1086 error = file.GetSize(&size); 1087 if (error != B_OK) { 1088 ERROR("Failed to packages activation file size: %s\n", 1089 strerror(error)); 1090 RETURN_ERROR(error); 1091 } 1092 1093 if (size > (off_t)kMaxActivationFileSize) { 1094 ERROR("The packages activation file is too big.\n"); 1095 RETURN_ERROR(B_BAD_DATA); 1096 } 1097 1098 char* fileContent = (char*)malloc(size + 1); 1099 if (fileContent == NULL) 1100 RETURN_ERROR(B_NO_MEMORY); 1101 MemoryDeleter fileContentDeleter(fileContent); 1102 1103 ssize_t bytesRead = file.Read(fileContent, size); 1104 if (bytesRead < 0) { 1105 ERROR("Failed to read packages activation file: %s\n", 1106 strerror(bytesRead)); 1107 RETURN_ERROR(errno); 1108 } 1109 1110 if (bytesRead != size) { 1111 ERROR("Failed to read whole packages activation file.\n"); 1112 RETURN_ERROR(B_ERROR); 1113 } 1114 1115 // null-terminate to simplify parsing 1116 fileContent[size] = '\0'; 1117 1118 AutoLocker<BLocker> locker(fLock); 1119 1120 // parse the file and mark the respective packages active 1121 const char* packageName = fileContent; 1122 char* const fileContentEnd = fileContent + size; 1123 while (packageName < fileContentEnd) { 1124 char* packageNameEnd = strchr(packageName, '\n'); 1125 if (packageNameEnd == NULL) 1126 packageNameEnd = fileContentEnd; 1127 1128 // skip empty lines 1129 if (packageName == packageNameEnd) { 1130 packageName++; 1131 continue; 1132 } 1133 *packageNameEnd = '\0'; 1134 1135 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) { 1136 ERROR("Invalid packages activation file content.\n"); 1137 RETURN_ERROR(B_BAD_DATA); 1138 } 1139 1140 Package* package = fLatestState->FindPackage(packageName); 1141 if (package != NULL) { 1142 fLatestState->SetPackageActive(package, true); 1143 fChangeCount++; 1144 } else { 1145 WARN("Package \"%s\" from activation file not in packages " 1146 "directory.\n", packageName); 1147 } 1148 1149 packageName = packageNameEnd + 1; 1150 } 1151 1152 return B_OK; 1153 } 1154 1155 1156 status_t 1157 Volume::_GetActivePackages(int fd) 1158 { 1159 // get the info from packagefs 1160 PackageFSGetPackageInfosRequest* request = NULL; 1161 MemoryDeleter requestDeleter; 1162 size_t bufferSize = 64 * 1024; 1163 for (;;) { 1164 request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize); 1165 if (request == NULL) 1166 RETURN_ERROR(B_NO_MEMORY); 1167 requestDeleter.SetTo(request); 1168 1169 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request, 1170 bufferSize) != 0) { 1171 ERROR("Volume::_GetActivePackages(): failed to get active package " 1172 "info from package FS: %s\n", strerror(errno)); 1173 RETURN_ERROR(errno); 1174 } 1175 1176 if (request->bufferSize <= bufferSize) 1177 break; 1178 1179 bufferSize = request->bufferSize; 1180 requestDeleter.Unset(); 1181 } 1182 1183 INFORM("latest volume state:\n"); 1184 _DumpState(fLatestState); 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 INFORM("currently active volume state:\n"); 1218 _DumpState(state); 1219 1220 fActiveState = stateDeleter.Detach(); 1221 return B_OK; 1222 } 1223 1224 1225 void 1226 Volume::_RunQueuedScripts() 1227 { 1228 BDirectory adminDirectory; 1229 status_t error = _OpenPackagesSubDirectory( 1230 RelativePath(kAdminDirectoryName), false, adminDirectory); 1231 if (error != B_OK) 1232 return; 1233 1234 BDirectory scriptsDirectory; 1235 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName); 1236 if (error != B_OK) 1237 return; 1238 1239 // enumerate all the symlinks in the queued scripts directory 1240 BEntry scriptEntry; 1241 while (scriptsDirectory.GetNextEntry(&scriptEntry, false) == B_OK) { 1242 BPath scriptPath; 1243 scriptEntry.GetPath(&scriptPath); 1244 error = scriptPath.InitCheck(); 1245 if (error != B_OK) { 1246 INFORM("failed to get path of post-installation script \"%s\"\n", 1247 strerror(error)); 1248 continue; 1249 } 1250 1251 errno = 0; 1252 int result = system(scriptPath.Path()); 1253 if (result != 0) { 1254 INFORM("running post-installation script \"%s\" " 1255 "failed: %d (errno: %s)\n", scriptPath.Leaf(), errno, strerror(errno)); 1256 } 1257 1258 // remove the symlink, now that we've run the post-installation script 1259 error = scriptEntry.Remove(); 1260 if (error != B_OK) { 1261 INFORM("removing queued post-install script failed \"%s\"\n", 1262 strerror(error)); 1263 } 1264 } 1265 } 1266 1267 1268 bool 1269 Volume::_CheckActivePackagesMatchLatestState( 1270 PackageFSGetPackageInfosRequest* request) 1271 { 1272 if (fPackagesDirectoryCount != 1) { 1273 INFORM("An old packages state (\"%s\") seems to be active.\n", 1274 fPackagesDirectories[fPackagesDirectoryCount - 1].Name().String()); 1275 return false; 1276 } 1277 1278 const node_ref packagesDirRef(PackagesDirectoryRef()); 1279 1280 // mark the returned packages active 1281 for (uint32 i = 0; i < request->packageCount; i++) { 1282 const PackageFSPackageInfo& info = request->infos[i]; 1283 if (node_ref(info.directoryDeviceID, info.directoryNodeID) 1284 != packagesDirRef) { 1285 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1286 ") not in packages directory\n", info.name, 1287 info.packageDeviceID, info.packageNodeID); 1288 return false; 1289 } 1290 1291 Package* package = fLatestState->FindPackage( 1292 node_ref(info.packageDeviceID, info.packageNodeID)); 1293 if (package == NULL || !package->IsActive()) { 1294 WARN("active package \"%s\" (dev: %" B_PRIdDEV ", node: %" B_PRIdINO 1295 ") not %s\n", info.name, 1296 info.packageDeviceID, info.packageNodeID, 1297 package == NULL 1298 ? "found in packages directory" : "supposed to be active"); 1299 return false; 1300 } 1301 } 1302 1303 // Check whether there are packages that aren't active but should be. 1304 uint32 count = 0; 1305 for (PackageNodeRefHashTable::Iterator it 1306 = fLatestState->ByNodeRefIterator(); it.HasNext();) { 1307 Package* package = it.Next(); 1308 if (package->IsActive()) 1309 count++; 1310 } 1311 1312 if (count != request->packageCount) { 1313 INFORM("There seem to be packages in the packages directory that " 1314 "should be active.\n"); 1315 return false; 1316 } 1317 1318 return true; 1319 } 1320 1321 1322 void 1323 Volume::_SetLatestState(VolumeState* state, bool isActive) 1324 { 1325 AutoLocker<BLocker> locker(fLock); 1326 1327 bool sendNotification = fRoot->IsSystemRoot(); 1328 // Send a notification, if this is a system root volume. 1329 BStringList addedPackageNames; 1330 BStringList removedPackageNames; 1331 1332 // If a notification should be sent then assemble the latest and incoming 1333 // set of the packages' names. This can be used to figure out which 1334 // packages are added and which are removed. 1335 1336 if (sendNotification) { 1337 _CollectPackageNamesAdded(fLatestState, state, addedPackageNames); 1338 _CollectPackageNamesAdded(state, fLatestState, removedPackageNames); 1339 } 1340 1341 if (isActive) { 1342 if (fLatestState != fActiveState) 1343 delete fActiveState; 1344 fActiveState = state; 1345 } 1346 1347 if (fLatestState != fActiveState) 1348 delete fLatestState; 1349 fLatestState = state; 1350 fChangeCount++; 1351 1352 locker.Unlock(); 1353 1354 // Send a notification, if this is a system root volume. 1355 if (sendNotification) { 1356 BMessage message(B_PACKAGE_UPDATE); 1357 if (message.AddInt32("event", 1358 (int32)B_INSTALLATION_LOCATION_PACKAGES_CHANGED) == B_OK 1359 && message.AddStrings("added package names", 1360 addedPackageNames) == B_OK 1361 && message.AddStrings("removed package names", 1362 removedPackageNames) == B_OK 1363 && message.AddInt32("location", (int32)Location()) == B_OK 1364 && message.AddInt64("change count", fChangeCount) == B_OK) { 1365 BRoster::Private().SendTo(&message, NULL, false); 1366 } 1367 } 1368 } 1369 1370 1371 /*static*/ void 1372 Volume::_CollectPackageNamesAdded(const VolumeState* oldState, 1373 const VolumeState* newState, BStringList& addedPackageNames) 1374 { 1375 if (newState == NULL) 1376 return; 1377 1378 for (PackageFileNameHashTable::Iterator it 1379 = newState->ByFileNameIterator(); it.HasNext();) { 1380 Package* package = it.Next(); 1381 BString packageName = package->Info().Name(); 1382 if (oldState == NULL) 1383 addedPackageNames.Add(packageName); 1384 else { 1385 Package* oldStatePackage = oldState->FindPackage( 1386 package->FileName()); 1387 if (oldStatePackage == NULL) 1388 addedPackageNames.Add(packageName); 1389 } 1390 } 1391 } 1392 1393 1394 void 1395 Volume::_DumpState(VolumeState* state) 1396 { 1397 uint32 inactiveCount = 0; 1398 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1399 it.HasNext();) { 1400 Package* package = it.Next(); 1401 if (package->IsActive()) { 1402 INFORM("active package: \"%s\"\n", package->FileName().String()); 1403 } else 1404 inactiveCount++; 1405 } 1406 1407 if (inactiveCount == 0) 1408 return; 1409 1410 for (PackageNodeRefHashTable::Iterator it = state->ByNodeRefIterator(); 1411 it.HasNext();) { 1412 Package* package = it.Next(); 1413 if (!package->IsActive()) 1414 INFORM("inactive package: \"%s\"\n", package->FileName().String()); 1415 } 1416 } 1417 1418 1419 status_t 1420 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository, 1421 bool activeOnly, bool installed) 1422 { 1423 status_t error = repository.SetTo(Path()); 1424 if (error != B_OK) { 1425 ERROR("Volume::_AddRepository(): failed to init repository: %s\n", 1426 strerror(error)); 1427 return error; 1428 } 1429 1430 repository.SetInstalled(installed); 1431 1432 error = AddPackagesToRepository(repository, true); 1433 if (error != B_OK) { 1434 ERROR("Volume::_AddRepository(): failed to add packages to " 1435 "repository: %s\n", strerror(error)); 1436 return error; 1437 } 1438 1439 error = solver->AddRepository(&repository); 1440 if (error != B_OK) { 1441 ERROR("Volume::_AddRepository(): failed to add repository to solver: " 1442 "%s\n", strerror(error)); 1443 return error; 1444 } 1445 1446 return B_OK; 1447 } 1448 1449 1450 status_t 1451 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create, 1452 BDirectory& _directory) 1453 { 1454 // open the packages directory 1455 BDirectory directory; 1456 status_t error = directory.SetTo(&PackagesDirectoryRef()); 1457 if (error != B_OK) { 1458 ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages " 1459 "directory: %s\n", strerror(error)); 1460 RETURN_ERROR(error); 1461 } 1462 1463 return FSUtils::OpenSubDirectory(directory, path, create, _directory); 1464 } 1465 1466 1467 void 1468 Volume::_CommitTransaction(BMessage* message, 1469 const BActivationTransaction* transaction, 1470 const PackageSet& packagesAlreadyAdded, 1471 const PackageSet& packagesAlreadyRemoved, BCommitTransactionResult& _result) 1472 { 1473 _result.Unset(); 1474 1475 // perform the request 1476 CommitTransactionHandler handler(this, fPackageFileManager, _result); 1477 BTransactionError error = B_TRANSACTION_INTERNAL_ERROR; 1478 try { 1479 handler.Init(fLatestState, fLatestState == fActiveState, 1480 packagesAlreadyAdded, packagesAlreadyRemoved); 1481 1482 if (message != NULL) 1483 handler.HandleRequest(message); 1484 else if (transaction != NULL) 1485 handler.HandleRequest(*transaction); 1486 else 1487 handler.HandleRequest(); 1488 1489 _SetLatestState(handler.DetachVolumeState(), 1490 handler.IsActiveVolumeState()); 1491 error = B_TRANSACTION_OK; 1492 } catch (Exception& exception) { 1493 error = exception.Error(); 1494 exception.SetOnResult(_result); 1495 if (_result.ErrorPackage().IsEmpty() 1496 && handler.CurrentPackage() != NULL) { 1497 _result.SetErrorPackage(handler.CurrentPackage()->FileName()); 1498 } 1499 } catch (std::bad_alloc& exception) { 1500 error = B_TRANSACTION_NO_MEMORY; 1501 } 1502 1503 _result.SetError(error); 1504 1505 // revert on error 1506 if (error != B_TRANSACTION_OK) 1507 handler.Revert(); 1508 } 1509