1 /* 2 * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 */ 8 9 10 #include "CommitTransactionHandler.h" 11 12 #include <errno.h> 13 #include <grp.h> 14 #include <pwd.h> 15 16 #include <File.h> 17 #include <Path.h> 18 #include <SymLink.h> 19 20 #include <AutoDeleter.h> 21 #include <CopyEngine.h> 22 #include <NotOwningEntryRef.h> 23 #include <package/CommitTransactionResult.h> 24 #include <package/DaemonDefs.h> 25 #include <RemoveEngine.h> 26 27 #include "Constants.h" 28 #include "DebugSupport.h" 29 #include "Exception.h" 30 #include "PackageFileManager.h" 31 #include "VolumeState.h" 32 33 34 using namespace BPackageKit::BPrivate; 35 36 using BPackageKit::BTransactionIssue; 37 38 39 // #pragma mark - TransactionIssueBuilder 40 41 42 struct CommitTransactionHandler::TransactionIssueBuilder { 43 TransactionIssueBuilder(BTransactionIssue::BType type, 44 Package* package = NULL) 45 : 46 fType(type), 47 fPackageName(package != NULL ? package->FileName() : BString()), 48 fPath1(), 49 fPath2(), 50 fSystemError(B_OK), 51 fExitCode(0) 52 { 53 } 54 55 TransactionIssueBuilder& SetPath1(const BString& path) 56 { 57 fPath1 = path; 58 return *this; 59 } 60 61 TransactionIssueBuilder& SetPath1(const FSUtils::Entry& entry) 62 { 63 return SetPath1(entry.Path()); 64 } 65 66 TransactionIssueBuilder& SetPath2(const BString& path) 67 { 68 fPath2 = path; 69 return *this; 70 } 71 72 TransactionIssueBuilder& SetPath2(const FSUtils::Entry& entry) 73 { 74 return SetPath2(entry.Path()); 75 } 76 77 TransactionIssueBuilder& SetSystemError(status_t error) 78 { 79 fSystemError = error; 80 return *this; 81 } 82 83 TransactionIssueBuilder& SetExitCode(int exitCode) 84 { 85 fExitCode = exitCode; 86 return *this; 87 } 88 89 BTransactionIssue BuildIssue(Package* package) const 90 { 91 BString packageName(fPackageName); 92 if (packageName.IsEmpty() && package != NULL) 93 packageName = package->FileName(); 94 95 return BTransactionIssue(fType, packageName, fPath1, fPath2, 96 fSystemError, fExitCode); 97 } 98 99 private: 100 BTransactionIssue::BType fType; 101 BString fPackageName; 102 BString fPath1; 103 BString fPath2; 104 status_t fSystemError; 105 int fExitCode; 106 }; 107 108 109 // #pragma mark - CommitTransactionHandler 110 111 112 CommitTransactionHandler::CommitTransactionHandler(Volume* volume, 113 PackageFileManager* packageFileManager, BCommitTransactionResult& result) 114 : 115 fVolume(volume), 116 fPackageFileManager(packageFileManager), 117 fVolumeState(NULL), 118 fVolumeStateIsActive(false), 119 fPackagesToActivate(), 120 fPackagesToDeactivate(), 121 fAddedPackages(), 122 fRemovedPackages(), 123 fPackagesAlreadyAdded(), 124 fPackagesAlreadyRemoved(), 125 fOldStateDirectory(), 126 fOldStateDirectoryRef(), 127 fOldStateDirectoryName(), 128 fTransactionDirectoryRef(), 129 fFirstBootProcessing(false), 130 fWritableFilesDirectory(), 131 fAddedGroups(), 132 fAddedUsers(), 133 fFSTransaction(), 134 fResult(result), 135 fCurrentPackage(NULL) 136 { 137 } 138 139 140 CommitTransactionHandler::~CommitTransactionHandler() 141 { 142 // Delete Package objects we created in case of error (on success 143 // fPackagesToActivate will be empty). 144 int32 count = fPackagesToActivate.CountItems(); 145 for (int32 i = 0; i < count; i++) { 146 Package* package = fPackagesToActivate.ItemAt(i); 147 if (fPackagesAlreadyAdded.find(package) 148 == fPackagesAlreadyAdded.end()) { 149 delete package; 150 } 151 } 152 153 delete fVolumeState; 154 } 155 156 157 void 158 CommitTransactionHandler::Init(VolumeState* volumeState, 159 bool isActiveVolumeState, const PackageSet& packagesAlreadyAdded, 160 const PackageSet& packagesAlreadyRemoved) 161 { 162 fVolumeState = volumeState->Clone(); 163 if (fVolumeState == NULL) 164 throw std::bad_alloc(); 165 166 fVolumeStateIsActive = isActiveVolumeState; 167 168 for (PackageSet::const_iterator it = packagesAlreadyAdded.begin(); 169 it != packagesAlreadyAdded.end(); ++it) { 170 Package* package = fVolumeState->FindPackage((*it)->FileName()); 171 fPackagesAlreadyAdded.insert(package); 172 } 173 174 for (PackageSet::const_iterator it = packagesAlreadyRemoved.begin(); 175 it != packagesAlreadyRemoved.end(); ++it) { 176 Package* package = fVolumeState->FindPackage((*it)->FileName()); 177 fPackagesAlreadyRemoved.insert(package); 178 } 179 } 180 181 182 void 183 CommitTransactionHandler::HandleRequest(BMessage* request) 184 { 185 status_t error; 186 187 BActivationTransaction transaction(request, &error); 188 if (error == B_OK) 189 error = transaction.InitCheck(); 190 if (error != B_OK) { 191 if (error == B_NO_MEMORY) 192 throw Exception(B_TRANSACTION_NO_MEMORY); 193 throw Exception(B_TRANSACTION_BAD_REQUEST); 194 } 195 196 HandleRequest(transaction); 197 } 198 199 200 void 201 CommitTransactionHandler::HandleRequest( 202 const BActivationTransaction& transaction) 203 { 204 // check the change count 205 if (transaction.ChangeCount() != fVolume->ChangeCount()) 206 throw Exception(B_TRANSACTION_CHANGE_COUNT_MISMATCH); 207 208 fFirstBootProcessing = transaction.FirstBootProcessing(); 209 210 // collect the packages to deactivate 211 _GetPackagesToDeactivate(transaction); 212 213 // read the packages to activate 214 _ReadPackagesToActivate(transaction); 215 216 // anything to do at all? 217 if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) { 218 WARN("Bad package activation request: no packages to activate or" 219 " deactivate\n"); 220 throw Exception(B_TRANSACTION_BAD_REQUEST); 221 } 222 223 _ApplyChanges(); 224 225 // Clean up the unused empty transaction directory for first boot 226 // processing, since it's usually an internal to package_daemon 227 // operation and there is no external client to clean it up. 228 if (fFirstBootProcessing) { 229 RelativePath directoryPath(kAdminDirectoryName, 230 transaction.TransactionDirectoryName().String()); 231 BDirectory transactionDir; 232 status_t error = _OpenPackagesSubDirectory(directoryPath, false, 233 transactionDir); 234 if (error == B_OK) { 235 BEntry transactionDirEntry; 236 error = transactionDir.GetEntry(&transactionDirEntry); 237 if (error == B_OK) 238 transactionDirEntry.Remove(); // Okay to fail when non-empty. 239 } 240 } 241 } 242 243 244 void 245 CommitTransactionHandler::HandleRequest() 246 { 247 for (PackageSet::const_iterator it = fPackagesAlreadyAdded.begin(); 248 it != fPackagesAlreadyAdded.end(); ++it) { 249 if (!fPackagesToActivate.AddItem(*it)) 250 throw std::bad_alloc(); 251 } 252 253 fPackagesToDeactivate = fPackagesAlreadyRemoved; 254 255 _ApplyChanges(); 256 } 257 258 259 void 260 CommitTransactionHandler::Revert() 261 { 262 // move packages to activate back to transaction directory 263 _RevertAddPackagesToActivate(); 264 265 // move packages to deactivate back to packages directory 266 _RevertRemovePackagesToDeactivate(); 267 268 // revert user and group changes 269 _RevertUserGroupChanges(); 270 271 // Revert all other FS operations, i.e. the writable files changes as 272 // well as the creation of the old state directory. 273 fFSTransaction.RollBack(); 274 } 275 276 277 VolumeState* 278 CommitTransactionHandler::DetachVolumeState() 279 { 280 VolumeState* result = fVolumeState; 281 fVolumeState = NULL; 282 return result; 283 } 284 285 286 void 287 CommitTransactionHandler::_GetPackagesToDeactivate( 288 const BActivationTransaction& transaction) 289 { 290 // get the number of packages to deactivate 291 const BStringList& packagesToDeactivate 292 = transaction.PackagesToDeactivate(); 293 int32 packagesToDeactivateCount = packagesToDeactivate.CountStrings(); 294 if (packagesToDeactivateCount == 0) 295 return; 296 297 for (int32 i = 0; i < packagesToDeactivateCount; i++) { 298 BString packageName = packagesToDeactivate.StringAt(i); 299 Package* package = fVolumeState->FindPackage(packageName); 300 if (package == NULL) { 301 throw Exception(B_TRANSACTION_NO_SUCH_PACKAGE) 302 .SetPackageName(packageName); 303 } 304 305 fPackagesToDeactivate.insert(package); 306 } 307 } 308 309 310 void 311 CommitTransactionHandler::_ReadPackagesToActivate( 312 const BActivationTransaction& transaction) 313 { 314 // get the number of packages to activate 315 const BStringList& packagesToActivate 316 = transaction.PackagesToActivate(); 317 int32 packagesToActivateCount = packagesToActivate.CountStrings(); 318 if (packagesToActivateCount == 0) 319 return; 320 321 // check the transaction directory name -- we only allow a simple 322 // subdirectory of the admin directory 323 const BString& transactionDirectoryName 324 = transaction.TransactionDirectoryName(); 325 if (transactionDirectoryName.IsEmpty() 326 || transactionDirectoryName.FindFirst('/') >= 0 327 || transactionDirectoryName == "." 328 || transactionDirectoryName == "..") { 329 WARN("Bad package activation request: malformed transaction" 330 " directory name: \"%s\"\n", transactionDirectoryName.String()); 331 throw Exception(B_TRANSACTION_BAD_REQUEST); 332 } 333 334 // open the directory 335 RelativePath directoryPath(kAdminDirectoryName, 336 transactionDirectoryName); 337 BDirectory directory; 338 status_t error = _OpenPackagesSubDirectory(directoryPath, false, directory); 339 if (error == B_OK) { 340 error = directory.GetNodeRef(&fTransactionDirectoryRef); 341 if (error != B_OK) { 342 ERROR("Failed to get transaction directory node ref: %s\n", 343 strerror(error)); 344 } 345 } else 346 ERROR("Failed to open transaction directory: %s\n", strerror(error)); 347 348 if (error != B_OK) { 349 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 350 .SetPath1(_GetPath( 351 FSUtils::Entry(fVolume->PackagesDirectoryRef(), 352 directoryPath.ToString()), 353 directoryPath.ToString())) 354 .SetSystemError(error); 355 } 356 357 // read the packages 358 for (int32 i = 0; i < packagesToActivateCount; i++) { 359 BString packageName = packagesToActivate.StringAt(i); 360 // make sure it doesn't clash with an already existing package, 361 // except in first boot mode where it should always clash. 362 Package* package = fVolumeState->FindPackage(packageName); 363 if (fFirstBootProcessing) { 364 if (package == NULL) { 365 throw Exception(B_TRANSACTION_NO_SUCH_PACKAGE) 366 .SetPackageName(packageName); 367 } 368 if (!fPackagesToActivate.AddItem(package)) 369 throw Exception(B_TRANSACTION_NO_MEMORY); 370 continue; 371 } else { 372 if (package != NULL) { 373 if (fPackagesAlreadyAdded.find(package) 374 != fPackagesAlreadyAdded.end()) { 375 if (!fPackagesToActivate.AddItem(package)) 376 throw Exception(B_TRANSACTION_NO_MEMORY); 377 continue; 378 } 379 380 if (fPackagesToDeactivate.find(package) 381 == fPackagesToDeactivate.end()) { 382 throw Exception(B_TRANSACTION_PACKAGE_ALREADY_EXISTS) 383 .SetPackageName(packageName); 384 } 385 } 386 } 387 388 // read the package 389 error = fPackageFileManager->CreatePackage( 390 NotOwningEntryRef(fTransactionDirectoryRef, packageName), 391 package); 392 if (error != B_OK) { 393 if (error == B_NO_MEMORY) 394 throw Exception(B_TRANSACTION_NO_MEMORY); 395 throw Exception(B_TRANSACTION_FAILED_TO_READ_PACKAGE_FILE) 396 .SetPackageName(packageName) 397 .SetPath1(_GetPath( 398 FSUtils::Entry( 399 NotOwningEntryRef(fTransactionDirectoryRef, 400 packageName)), 401 packageName)) 402 .SetSystemError(error); 403 } 404 405 if (!fPackagesToActivate.AddItem(package)) { 406 delete package; 407 throw Exception(B_TRANSACTION_NO_MEMORY); 408 } 409 } 410 } 411 412 413 void 414 CommitTransactionHandler::_ApplyChanges() 415 { 416 if (!fFirstBootProcessing) 417 { 418 // create an old state directory 419 _CreateOldStateDirectory(); 420 421 // move packages to deactivate to old state directory 422 _RemovePackagesToDeactivate(); 423 424 // move packages to activate to packages directory 425 _AddPackagesToActivate(); 426 427 // run pre-uninstall scripts, before their packages vanish. 428 _RunPreUninstallScripts(); 429 430 // activate/deactivate packages and create users, groups, settings files. 431 _ChangePackageActivation(fAddedPackages, fRemovedPackages); 432 } else // FirstBootProcessing, skip several steps and just do package setup. 433 _PrepareFirstBootPackages(); 434 435 // run post-install scripts now that the new packages are visible in the 436 // package file system. 437 if (fVolumeStateIsActive || fFirstBootProcessing) { 438 _RunPostInstallScripts(); 439 } else { 440 // Do post-install scripts later after a reboot, for Haiku OS packages. 441 _QueuePostInstallScripts(); 442 } 443 444 // removed packages have been deleted, new packages shall not be deleted 445 fAddedPackages.clear(); 446 fRemovedPackages.clear(); 447 fPackagesToActivate.MakeEmpty(false); 448 fPackagesToDeactivate.clear(); 449 } 450 451 452 void 453 CommitTransactionHandler::_CreateOldStateDirectory() 454 { 455 // construct a nice name from the current date and time 456 time_t nowSeconds = time(NULL); 457 struct tm now; 458 BString baseName; 459 if (localtime_r(&nowSeconds, &now) != NULL) { 460 baseName.SetToFormat("state_%d-%02d-%02d_%02d:%02d:%02d", 461 1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour, 462 now.tm_min, now.tm_sec); 463 } else 464 baseName = "state"; 465 466 if (baseName.IsEmpty()) 467 throw Exception(B_TRANSACTION_NO_MEMORY); 468 469 // make sure the directory doesn't exist yet 470 BDirectory adminDirectory; 471 status_t error = _OpenPackagesSubDirectory( 472 RelativePath(kAdminDirectoryName), true, adminDirectory); 473 if (error != B_OK) { 474 ERROR("Failed to open administrative directory: %s\n", strerror(error)); 475 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 476 .SetPath1(_GetPath( 477 FSUtils::Entry(fVolume->PackagesDirectoryRef(), 478 kAdminDirectoryName), 479 kAdminDirectoryName)) 480 .SetSystemError(error); 481 } 482 483 int uniqueId = 1; 484 BString directoryName = baseName; 485 while (BEntry(&adminDirectory, directoryName).Exists()) { 486 directoryName.SetToFormat("%s-%d", baseName.String(), uniqueId++); 487 if (directoryName.IsEmpty()) 488 throw Exception(B_TRANSACTION_NO_MEMORY); 489 } 490 491 // create the directory 492 FSTransaction::CreateOperation createOldStateDirectoryOperation( 493 &fFSTransaction, FSUtils::Entry(adminDirectory, directoryName)); 494 495 error = adminDirectory.CreateDirectory(directoryName, 496 &fOldStateDirectory); 497 if (error == B_OK) { 498 createOldStateDirectoryOperation.Finished(); 499 500 fOldStateDirectoryName = directoryName; 501 502 error = fOldStateDirectory.GetNodeRef(&fOldStateDirectoryRef); 503 if (error != B_OK) 504 ERROR("Failed get old state directory ref: %s\n", strerror(error)); 505 } else 506 ERROR("Failed to create old state directory: %s\n", strerror(error)); 507 508 if (error != B_OK) { 509 throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY) 510 .SetPath1(_GetPath( 511 FSUtils::Entry(adminDirectory, directoryName), 512 directoryName)) 513 .SetSystemError(error); 514 } 515 516 // write the old activation file 517 BEntry activationFile; 518 _WriteActivationFile(RelativePath(kAdminDirectoryName, directoryName), 519 kActivationFileName, PackageSet(), PackageSet(), activationFile); 520 521 fResult.SetOldStateDirectory(fOldStateDirectoryName); 522 } 523 524 525 void 526 CommitTransactionHandler::_RemovePackagesToDeactivate() 527 { 528 if (fPackagesToDeactivate.empty()) 529 return; 530 531 for (PackageSet::const_iterator it = fPackagesToDeactivate.begin(); 532 it != fPackagesToDeactivate.end(); ++it) { 533 Package* package = *it; 534 535 // When deactivating (or updating) a system package, don't do that live. 536 if (_IsSystemPackage(package)) 537 fVolumeStateIsActive = false; 538 539 if (fPackagesAlreadyRemoved.find(package) 540 != fPackagesAlreadyRemoved.end()) { 541 fRemovedPackages.insert(package); 542 continue; 543 } 544 545 // get a BEntry for the package 546 NotOwningEntryRef entryRef(package->EntryRef()); 547 548 BEntry entry; 549 status_t error = entry.SetTo(&entryRef); 550 if (error != B_OK) { 551 ERROR("Failed to get package entry for %s: %s\n", 552 package->FileName().String(), strerror(error)); 553 throw Exception(B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH) 554 .SetPath1(package->FileName()) 555 .SetPackageName(package->FileName()) 556 .SetSystemError(error); 557 } 558 559 // move entry 560 fRemovedPackages.insert(package); 561 562 error = entry.MoveTo(&fOldStateDirectory); 563 if (error != B_OK) { 564 fRemovedPackages.erase(package); 565 ERROR("Failed to move old package %s from packages directory: %s\n", 566 package->FileName().String(), strerror(error)); 567 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) 568 .SetPath1( 569 _GetPath(FSUtils::Entry(entryRef), package->FileName())) 570 .SetPath2(_GetPath( 571 FSUtils::Entry(fOldStateDirectory), 572 fOldStateDirectoryName)) 573 .SetSystemError(error); 574 } 575 576 fPackageFileManager->PackageFileMoved(package->File(), 577 fOldStateDirectoryRef); 578 package->File()->IncrementEntryRemovedIgnoreLevel(); 579 } 580 } 581 582 583 void 584 CommitTransactionHandler::_AddPackagesToActivate() 585 { 586 if (fPackagesToActivate.IsEmpty()) 587 return; 588 589 // open packages directory 590 BDirectory packagesDirectory; 591 status_t error 592 = packagesDirectory.SetTo(&fVolume->PackagesDirectoryRef()); 593 if (error != B_OK) { 594 ERROR("Failed to open packages directory: %s\n", strerror(error)); 595 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 596 .SetPath1("<packages>") 597 .SetSystemError(error); 598 } 599 600 int32 count = fPackagesToActivate.CountItems(); 601 for (int32 i = 0; i < count; i++) { 602 Package* package = fPackagesToActivate.ItemAt(i); 603 if (fPackagesAlreadyAdded.find(package) 604 != fPackagesAlreadyAdded.end()) { 605 fAddedPackages.insert(package); 606 _PreparePackageToActivate(package); 607 continue; 608 } 609 610 // get a BEntry for the package 611 NotOwningEntryRef entryRef(fTransactionDirectoryRef, 612 package->FileName()); 613 BEntry entry; 614 error = entry.SetTo(&entryRef); 615 if (error != B_OK) { 616 ERROR("Failed to get package entry for %s: %s\n", 617 package->FileName().String(), strerror(error)); 618 throw Exception(B_TRANSACTION_FAILED_TO_GET_ENTRY_PATH) 619 .SetPath1(package->FileName()) 620 .SetPackageName(package->FileName()) 621 .SetSystemError(error); 622 } 623 624 // move entry 625 fAddedPackages.insert(package); 626 627 error = entry.MoveTo(&packagesDirectory); 628 if (error == B_FILE_EXISTS) { 629 error = _AssertEntriesAreEqual(entry, &packagesDirectory); 630 if (error == B_OK) { 631 // Packages are identical, no need to move. 632 // If the entry is not removed however, it will prevent 633 // the transaction directory from being removed later. 634 // We ignore failure to Remove() here, though. 635 entry.Remove(); 636 } else if (error != B_FILE_EXISTS) { 637 ERROR("Failed to compare new package %s to existing file in " 638 "packages directory: %s\n", package->FileName().String(), 639 strerror(error)); 640 // Restore original error to avoid confusion 641 error = B_FILE_EXISTS; 642 } 643 } 644 if (error != B_OK) { 645 fAddedPackages.erase(package); 646 ERROR("Failed to move new package %s to packages directory: %s\n", 647 package->FileName().String(), strerror(error)); 648 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) 649 .SetPath1( 650 _GetPath(FSUtils::Entry(entryRef), package->FileName())) 651 .SetPath2(_GetPath( 652 FSUtils::Entry(packagesDirectory), 653 "packages")) 654 .SetSystemError(error); 655 } 656 657 fPackageFileManager->PackageFileMoved(package->File(), 658 fVolume->PackagesDirectoryRef()); 659 package->File()->IncrementEntryCreatedIgnoreLevel(); 660 661 // also add the package to the volume 662 fVolumeState->AddPackage(package); 663 664 _PreparePackageToActivate(package); 665 } 666 } 667 668 669 void 670 CommitTransactionHandler::_PrepareFirstBootPackages() 671 { 672 int32 count = fPackagesToActivate.CountItems(); 673 674 BDirectory transactionDir(&fTransactionDirectoryRef); 675 BEntry transactionEntry; 676 BPath transactionPath; 677 if (transactionDir.InitCheck() == B_OK && 678 transactionDir.GetEntry(&transactionEntry) == B_OK && 679 transactionEntry.GetPath(&transactionPath) == B_OK) { 680 INFORM("Starting First Boot Processing for %d packages in %s.\n", 681 (int) count, transactionPath.Path()); 682 } 683 684 for (int32 i = 0; i < count; i++) { 685 Package* package = fPackagesToActivate.ItemAt(i); 686 fAddedPackages.insert(package); 687 INFORM("Doing first boot processing #%d for package %s.\n", 688 (int) i, package->FileName().String()); 689 _PreparePackageToActivate(package); 690 } 691 } 692 693 694 void 695 CommitTransactionHandler::_PreparePackageToActivate(Package* package) 696 { 697 fCurrentPackage = package; 698 699 // add groups 700 const BStringList& groups = package->Info().Groups(); 701 int32 count = groups.CountStrings(); 702 for (int32 i = 0; i < count; i++) 703 _AddGroup(package, groups.StringAt(i)); 704 705 // add users 706 const BObjectList<BUser>& users = package->Info().Users(); 707 for (int32 i = 0; const BUser* user = users.ItemAt(i); i++) 708 _AddUser(package, *user); 709 710 // handle global writable files 711 _AddGlobalWritableFiles(package); 712 713 fCurrentPackage = NULL; 714 } 715 716 717 void 718 CommitTransactionHandler::_AddGroup(Package* package, const BString& groupName) 719 { 720 // Check whether the group already exists. 721 char buffer[256]; 722 struct group groupBuffer; 723 struct group* groupFound; 724 int error = getgrnam_r(groupName, &groupBuffer, buffer, sizeof(buffer), 725 &groupFound); 726 if ((error == 0 && groupFound != NULL) || error == ERANGE) 727 return; 728 729 // add it 730 fAddedGroups.insert(groupName.String()); 731 732 std::string commandLine("groupadd "); 733 commandLine += FSUtils::ShellEscapeString(groupName).String(); 734 735 if (system(commandLine.c_str()) != 0) { 736 fAddedGroups.erase(groupName.String()); 737 ERROR("Failed to add group \"%s\".\n", groupName.String()); 738 throw Exception(B_TRANSACTION_FAILED_TO_ADD_GROUP) 739 .SetPackageName(package->FileName()) 740 .SetString1(groupName); 741 } 742 } 743 744 745 void 746 CommitTransactionHandler::_AddUser(Package* package, const BUser& user) 747 { 748 // Check whether the user already exists. 749 char buffer[256]; 750 struct passwd passwdBuffer; 751 struct passwd* passwdFound; 752 int error = getpwnam_r(user.Name(), &passwdBuffer, buffer, 753 sizeof(buffer), &passwdFound); 754 if ((error == 0 && passwdFound != NULL) || error == ERANGE) 755 return; 756 757 // add it 758 fAddedUsers.insert(user.Name().String()); 759 760 std::string commandLine("useradd "); 761 762 if (!user.RealName().IsEmpty()) { 763 commandLine += std::string("-n ") 764 + FSUtils::ShellEscapeString(user.RealName()).String() + " "; 765 } 766 767 if (!user.Home().IsEmpty()) { 768 commandLine += std::string("-d ") 769 + FSUtils::ShellEscapeString(user.Home()).String() + " "; 770 } 771 772 if (!user.Shell().IsEmpty()) { 773 commandLine += std::string("-s ") 774 + FSUtils::ShellEscapeString(user.Shell()).String() + " "; 775 } 776 777 if (!user.Groups().IsEmpty()) { 778 commandLine += std::string("-g ") 779 + FSUtils::ShellEscapeString(user.Groups().First()).String() 780 + " "; 781 } 782 783 commandLine += FSUtils::ShellEscapeString(user.Name()).String(); 784 785 if (system(commandLine.c_str()) != 0) { 786 fAddedUsers.erase(user.Name().String()); 787 ERROR("Failed to add user \"%s\".\n", user.Name().String()); 788 throw Exception(B_TRANSACTION_FAILED_TO_ADD_USER) 789 .SetPackageName(package->FileName()) 790 .SetString1(user.Name()); 791 792 } 793 794 // add the supplementary groups 795 int32 groupCount = user.Groups().CountStrings(); 796 for (int32 i = 1; i < groupCount; i++) { 797 commandLine = std::string("groupmod -A ") 798 + FSUtils::ShellEscapeString(user.Name()).String() 799 + " " 800 + FSUtils::ShellEscapeString(user.Groups().StringAt(i)) 801 .String(); 802 if (system(commandLine.c_str()) != 0) { 803 fAddedUsers.erase(user.Name().String()); 804 ERROR("Failed to add user \"%s\" to group \"%s\".\n", 805 user.Name().String(), user.Groups().StringAt(i).String()); 806 throw Exception(B_TRANSACTION_FAILED_TO_ADD_USER_TO_GROUP) 807 .SetPackageName(package->FileName()) 808 .SetString1(user.Name()) 809 .SetString2(user.Groups().StringAt(i)); 810 } 811 } 812 } 813 814 815 void 816 CommitTransactionHandler::_AddGlobalWritableFiles(Package* package) 817 { 818 // get the list of included files 819 const BObjectList<BGlobalWritableFileInfo>& files 820 = package->Info().GlobalWritableFileInfos(); 821 BStringList contentPaths; 822 for (int32 i = 0; const BGlobalWritableFileInfo* file = files.ItemAt(i); 823 i++) { 824 if (file->IsIncluded() && !contentPaths.Add(file->Path())) 825 throw std::bad_alloc(); 826 } 827 828 if (contentPaths.IsEmpty()) 829 return; 830 831 // Open the root directory of the installation location where we will 832 // extract the files -- that's the volume's root directory. 833 BDirectory rootDirectory; 834 status_t error = rootDirectory.SetTo(&fVolume->RootDirectoryRef()); 835 if (error != B_OK) { 836 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 837 .SetPath1(_GetPath( 838 FSUtils::Entry(fVolume->RootDirectoryRef()), 839 "<packagefs root>")) 840 .SetSystemError(error); 841 } 842 843 // Open writable-files directory in the administrative directory. 844 if (fWritableFilesDirectory.InitCheck() != B_OK) { 845 RelativePath directoryPath(kAdminDirectoryName, 846 kWritableFilesDirectoryName); 847 error = _OpenPackagesSubDirectory(directoryPath, true, 848 fWritableFilesDirectory); 849 850 if (error != B_OK) { 851 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 852 .SetPath1(_GetPath( 853 FSUtils::Entry(fVolume->PackagesDirectoryRef(), 854 directoryPath.ToString()), 855 directoryPath.ToString())) 856 .SetPackageName(package->FileName()) 857 .SetSystemError(error); 858 } 859 } 860 861 // extract files into a subdir of the writable-files directory 862 BDirectory extractedFilesDirectory; 863 _ExtractPackageContent(package, contentPaths, 864 fWritableFilesDirectory, extractedFilesDirectory); 865 866 for (int32 i = 0; const BGlobalWritableFileInfo* file = files.ItemAt(i); 867 i++) { 868 if (file->IsIncluded()) { 869 _AddGlobalWritableFile(package, *file, rootDirectory, 870 extractedFilesDirectory); 871 } 872 } 873 } 874 875 876 void 877 CommitTransactionHandler::_AddGlobalWritableFile(Package* package, 878 const BGlobalWritableFileInfo& file, const BDirectory& rootDirectory, 879 const BDirectory& extractedFilesDirectory) 880 { 881 // Map the path name to the actual target location. Currently this only 882 // concerns "settings/", which is mapped to "settings/global/". 883 BString targetPath(file.Path()); 884 if (fVolume->MountType() == PACKAGE_FS_MOUNT_TYPE_HOME) { 885 if (targetPath == "settings" 886 || targetPath.StartsWith("settings/")) { 887 targetPath.Insert("/global", 8); 888 if (targetPath.Length() == file.Path().Length()) 889 throw std::bad_alloc(); 890 } 891 } 892 893 // open parent directory of the source entry 894 const char* lastSlash = strrchr(file.Path(), '/'); 895 const BDirectory* sourceDirectory; 896 BDirectory stackSourceDirectory; 897 if (lastSlash != NULL) { 898 sourceDirectory = &stackSourceDirectory; 899 BString sourceParentPath(file.Path(), 900 lastSlash - file.Path().String()); 901 if (sourceParentPath.Length() == 0) 902 throw std::bad_alloc(); 903 904 status_t error = stackSourceDirectory.SetTo( 905 &extractedFilesDirectory, sourceParentPath); 906 if (error != B_OK) { 907 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 908 .SetPath1(_GetPath( 909 FSUtils::Entry(extractedFilesDirectory, sourceParentPath), 910 sourceParentPath)) 911 .SetPackageName(package->FileName()) 912 .SetSystemError(error); 913 } 914 } else { 915 sourceDirectory = &extractedFilesDirectory; 916 } 917 918 // open parent directory of the target entry -- create, if necessary 919 FSUtils::Path relativeSourcePath(file.Path()); 920 lastSlash = strrchr(targetPath, '/'); 921 if (lastSlash != NULL) { 922 BString targetParentPath(targetPath, 923 lastSlash - targetPath.String()); 924 if (targetParentPath.Length() == 0) 925 throw std::bad_alloc(); 926 927 BDirectory targetDirectory; 928 status_t error = FSUtils::OpenSubDirectory(rootDirectory, 929 RelativePath(targetParentPath), true, targetDirectory); 930 if (error != B_OK) { 931 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 932 .SetPath1(_GetPath( 933 FSUtils::Entry(rootDirectory, targetParentPath), 934 targetParentPath)) 935 .SetPackageName(package->FileName()) 936 .SetSystemError(error); 937 } 938 _AddGlobalWritableFileRecurse(package, *sourceDirectory, 939 relativeSourcePath, targetDirectory, lastSlash + 1, 940 file.UpdateType()); 941 } else { 942 _AddGlobalWritableFileRecurse(package, *sourceDirectory, 943 relativeSourcePath, rootDirectory, targetPath, 944 file.UpdateType()); 945 } 946 } 947 948 949 void 950 CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, 951 const BDirectory& sourceDirectory, FSUtils::Path& relativeSourcePath, 952 const BDirectory& targetDirectory, const char* targetName, 953 BWritableFileUpdateType updateType) 954 { 955 // * If the file doesn't exist, just copy the extracted one. 956 // * If the file does exist, compare with the previous original version: 957 // * If unchanged, just overwrite it. 958 // * If changed, leave it to the user for now. When we support merging 959 // first back the file up, then try the merge. 960 961 // Check whether the target location exists and what type the entry at 962 // both locations are. 963 struct stat targetStat; 964 if (targetDirectory.GetStatFor(targetName, &targetStat) != B_OK) { 965 // target doesn't exist -- just copy 966 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 967 "couldn't get stat for writable file \"%s\", copying...\n", 968 targetName); 969 FSTransaction::CreateOperation copyOperation(&fFSTransaction, 970 FSUtils::Entry(targetDirectory, targetName)); 971 status_t error = BCopyEngine(BCopyEngine::COPY_RECURSIVELY) 972 .CopyEntry( 973 FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()), 974 FSUtils::Entry(targetDirectory, targetName)); 975 if (error != B_OK) { 976 if (targetDirectory.GetStatFor(targetName, &targetStat) == B_OK) 977 copyOperation.Finished(); 978 979 throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE) 980 .SetPath1(_GetPath( 981 FSUtils::Entry(sourceDirectory, 982 relativeSourcePath.Leaf()), 983 relativeSourcePath)) 984 .SetPath2(_GetPath( 985 FSUtils::Entry(targetDirectory, targetName), 986 targetName)) 987 .SetSystemError(error); 988 } 989 copyOperation.Finished(); 990 return; 991 } 992 993 struct stat sourceStat; 994 status_t error = sourceDirectory.GetStatFor(relativeSourcePath.Leaf(), 995 &sourceStat); 996 if (error != B_OK) { 997 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) 998 .SetPath1(_GetPath( 999 FSUtils::Entry(sourceDirectory, 1000 relativeSourcePath.Leaf()), 1001 relativeSourcePath)) 1002 .SetSystemError(error); 1003 } 1004 1005 if ((sourceStat.st_mode & S_IFMT) != (targetStat.st_mode & S_IFMT) 1006 || (!S_ISDIR(sourceStat.st_mode) && !S_ISREG(sourceStat.st_mode) 1007 && !S_ISLNK(sourceStat.st_mode))) { 1008 // Source and target entry types don't match or this is an entry 1009 // we cannot handle. The user must handle this manually. 1010 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1011 "writable file \"%s\" exists, but type doesn't match previous " 1012 "type\n", targetName); 1013 _AddIssue(TransactionIssueBuilder( 1014 BTransactionIssue::B_WRITABLE_FILE_TYPE_MISMATCH) 1015 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1016 .SetPath2(FSUtils::Entry(sourceDirectory, 1017 relativeSourcePath.Leaf()))); 1018 return; 1019 } 1020 1021 if (S_ISDIR(sourceStat.st_mode)) { 1022 // entry is a directory -- recurse 1023 BDirectory sourceSubDirectory; 1024 error = sourceSubDirectory.SetTo(&sourceDirectory, 1025 relativeSourcePath.Leaf()); 1026 if (error != B_OK) { 1027 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1028 .SetPath1(_GetPath( 1029 FSUtils::Entry(sourceDirectory, 1030 relativeSourcePath.Leaf()), 1031 relativeSourcePath)) 1032 .SetPackageName(package->FileName()) 1033 .SetSystemError(error); 1034 } 1035 1036 BDirectory targetSubDirectory; 1037 error = targetSubDirectory.SetTo(&targetDirectory, targetName); 1038 if (error != B_OK) { 1039 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1040 .SetPath1(_GetPath( 1041 FSUtils::Entry(targetDirectory, targetName), 1042 targetName)) 1043 .SetPackageName(package->FileName()) 1044 .SetSystemError(error); 1045 } 1046 1047 entry_ref entry; 1048 while (sourceSubDirectory.GetNextRef(&entry) == B_OK) { 1049 relativeSourcePath.AppendComponent(entry.name); 1050 _AddGlobalWritableFileRecurse(package, sourceSubDirectory, 1051 relativeSourcePath, targetSubDirectory, entry.name, 1052 updateType); 1053 relativeSourcePath.RemoveLastComponent(); 1054 } 1055 1056 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1057 "writable directory, recursion done\n"); 1058 return; 1059 } 1060 1061 // get the package the target file originated from 1062 BString originalPackage; 1063 if (BNode(&targetDirectory, targetName).ReadAttrString( 1064 kPackageFileAttribute, &originalPackage) != B_OK) { 1065 // Can't determine the original package. The user must handle this 1066 // manually. 1067 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1068 "failed to get SYS:PACKAGE attribute for \"%s\", can't tell if " 1069 "file needs to be updated\n", 1070 targetName); 1071 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { 1072 _AddIssue(TransactionIssueBuilder( 1073 BTransactionIssue::B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE) 1074 .SetPath1(FSUtils::Entry(targetDirectory, targetName))); 1075 } 1076 return; 1077 } 1078 1079 // If that's our package, we're happy. 1080 if (originalPackage == package->RevisionedNameThrows()) { 1081 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1082 "file \"%s\" tagged with same package version we're activating\n", 1083 targetName); 1084 return; 1085 } 1086 1087 // Check, whether the writable-files directory for the original package 1088 // exists. 1089 BString originalRelativeSourcePath = BString().SetToFormat("%s/%s", 1090 originalPackage.String(), relativeSourcePath.ToCString()); 1091 if (originalRelativeSourcePath.IsEmpty()) 1092 throw std::bad_alloc(); 1093 1094 struct stat originalPackageStat; 1095 error = fWritableFilesDirectory.GetStatFor(originalRelativeSourcePath, 1096 &originalPackageStat); 1097 if (error != B_OK 1098 || (sourceStat.st_mode & S_IFMT) 1099 != (originalPackageStat.st_mode & S_IFMT)) { 1100 // Original entry doesn't exist (either we don't have the data from 1101 // the original package or the entry really didn't exist) or its 1102 // type differs from the expected one. The user must handle this 1103 // manually. 1104 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1105 "original \"%s\" doesn't exist or has other type\n", 1106 _GetPath(FSUtils::Entry(fWritableFilesDirectory, 1107 originalRelativeSourcePath), 1108 originalRelativeSourcePath).String()); 1109 if (error != B_OK) { 1110 _AddIssue(TransactionIssueBuilder( 1111 BTransactionIssue 1112 ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_MISSING) 1113 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1114 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1115 originalRelativeSourcePath))); 1116 } else { 1117 _AddIssue(TransactionIssueBuilder( 1118 BTransactionIssue 1119 ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_TYPE_MISMATCH) 1120 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1121 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1122 originalRelativeSourcePath))); 1123 } 1124 return; 1125 } 1126 1127 if (S_ISREG(sourceStat.st_mode)) { 1128 // compare file content 1129 bool equal; 1130 error = FSUtils::CompareFileContent( 1131 FSUtils::Entry(fWritableFilesDirectory, 1132 originalRelativeSourcePath), 1133 FSUtils::Entry(targetDirectory, targetName), 1134 equal); 1135 // TODO: Merge support! 1136 if (error != B_OK || !equal) { 1137 // The comparison failed or the files differ. The user must 1138 // handle this manually. 1139 PRINT("Volume::CommitTransactionHandler::" 1140 "_AddGlobalWritableFile(): " 1141 "file comparison \"%s\" failed (%s) or files aren't equal\n", 1142 targetName, strerror(error)); 1143 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { 1144 if (error != B_OK) { 1145 _AddIssue(TransactionIssueBuilder( 1146 BTransactionIssue 1147 ::B_WRITABLE_FILE_COMPARISON_FAILED) 1148 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1149 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1150 originalRelativeSourcePath)) 1151 .SetSystemError(error)); 1152 } else { 1153 _AddIssue(TransactionIssueBuilder( 1154 BTransactionIssue 1155 ::B_WRITABLE_FILE_NOT_EQUAL) 1156 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1157 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1158 originalRelativeSourcePath))); 1159 } 1160 } 1161 return; 1162 } 1163 } else { 1164 // compare symlinks 1165 bool equal; 1166 error = FSUtils::CompareSymLinks( 1167 FSUtils::Entry(fWritableFilesDirectory, 1168 originalRelativeSourcePath), 1169 FSUtils::Entry(targetDirectory, targetName), 1170 equal); 1171 if (error != B_OK || !equal) { 1172 // The comparison failed or the symlinks differ. The user must 1173 // handle this manually. 1174 PRINT("Volume::CommitTransactionHandler::" 1175 "_AddGlobalWritableFile(): " 1176 "symlink comparison \"%s\" failed (%s) or symlinks aren't " 1177 "equal\n", targetName, strerror(error)); 1178 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { 1179 if (error != B_OK) { 1180 _AddIssue(TransactionIssueBuilder( 1181 BTransactionIssue 1182 ::B_WRITABLE_SYMLINK_COMPARISON_FAILED) 1183 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1184 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1185 originalRelativeSourcePath)) 1186 .SetSystemError(error)); 1187 } else { 1188 _AddIssue(TransactionIssueBuilder( 1189 BTransactionIssue 1190 ::B_WRITABLE_SYMLINK_NOT_EQUAL) 1191 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1192 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1193 originalRelativeSourcePath))); 1194 } 1195 } 1196 return; 1197 } 1198 } 1199 1200 // Replace the existing file/symlink. We do that in two steps: First 1201 // copy the new file to a neighoring location, then move-replace the 1202 // old file. 1203 BString tempTargetName; 1204 tempTargetName.SetToFormat("%s.%s", targetName, 1205 package->RevisionedNameThrows().String()); 1206 if (tempTargetName.IsEmpty()) 1207 throw std::bad_alloc(); 1208 1209 // copy 1210 FSTransaction::CreateOperation copyOperation(&fFSTransaction, 1211 FSUtils::Entry(targetDirectory, tempTargetName)); 1212 1213 error = BCopyEngine(BCopyEngine::UNLINK_DESTINATION).CopyEntry( 1214 FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()), 1215 FSUtils::Entry(targetDirectory, tempTargetName)); 1216 if (error != B_OK) { 1217 throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE) 1218 .SetPath1(_GetPath( 1219 FSUtils::Entry(sourceDirectory, 1220 relativeSourcePath.Leaf()), 1221 relativeSourcePath)) 1222 .SetPath2(_GetPath( 1223 FSUtils::Entry(targetDirectory, tempTargetName), 1224 tempTargetName)) 1225 .SetSystemError(error); 1226 } 1227 1228 copyOperation.Finished(); 1229 1230 // rename 1231 FSTransaction::RemoveOperation renameOperation(&fFSTransaction, 1232 FSUtils::Entry(targetDirectory, targetName), 1233 FSUtils::Entry(fWritableFilesDirectory, 1234 originalRelativeSourcePath)); 1235 1236 BEntry targetEntry; 1237 error = targetEntry.SetTo(&targetDirectory, tempTargetName); 1238 if (error == B_OK) 1239 error = targetEntry.Rename(targetName, true); 1240 if (error != B_OK) { 1241 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) 1242 .SetPath1(_GetPath( 1243 FSUtils::Entry(targetDirectory, tempTargetName), 1244 tempTargetName)) 1245 .SetPath2(targetName) 1246 .SetSystemError(error); 1247 } 1248 1249 renameOperation.Finished(); 1250 copyOperation.Unregister(); 1251 } 1252 1253 1254 void 1255 CommitTransactionHandler::_RevertAddPackagesToActivate() 1256 { 1257 if (fAddedPackages.empty() || fFirstBootProcessing) 1258 return; 1259 1260 // open transaction directory 1261 BDirectory transactionDirectory; 1262 status_t error = transactionDirectory.SetTo(&fTransactionDirectoryRef); 1263 if (error != B_OK) { 1264 ERROR("failed to open transaction directory: %s\n", 1265 strerror(error)); 1266 } 1267 1268 for (PackageSet::iterator it = fAddedPackages.begin(); 1269 it != fAddedPackages.end(); ++it) { 1270 // remove package from the volume 1271 Package* package = *it; 1272 1273 if (fPackagesAlreadyAdded.find(package) 1274 != fPackagesAlreadyAdded.end()) { 1275 continue; 1276 } 1277 1278 fVolumeState->RemovePackage(package); 1279 1280 if (transactionDirectory.InitCheck() != B_OK) 1281 continue; 1282 1283 // get BEntry for the package 1284 NotOwningEntryRef entryRef(package->EntryRef()); 1285 BEntry entry; 1286 error = entry.SetTo(&entryRef); 1287 if (error != B_OK) { 1288 ERROR("failed to get entry for package \"%s\": %s\n", 1289 package->FileName().String(), strerror(error)); 1290 continue; 1291 } 1292 1293 // move entry 1294 error = entry.MoveTo(&transactionDirectory); 1295 if (error != B_OK) { 1296 ERROR("failed to move new package \"%s\" back to transaction " 1297 "directory: %s\n", package->FileName().String(), 1298 strerror(error)); 1299 continue; 1300 } 1301 1302 fPackageFileManager->PackageFileMoved(package->File(), 1303 fTransactionDirectoryRef); 1304 package->File()->IncrementEntryRemovedIgnoreLevel(); 1305 } 1306 } 1307 1308 1309 void 1310 CommitTransactionHandler::_RevertRemovePackagesToDeactivate() 1311 { 1312 if (fRemovedPackages.empty() || fFirstBootProcessing) 1313 return; 1314 1315 // open packages directory 1316 BDirectory packagesDirectory; 1317 status_t error 1318 = packagesDirectory.SetTo(&fVolume->PackagesDirectoryRef()); 1319 if (error != B_OK) { 1320 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1321 .SetPath1("<packages>") 1322 .SetSystemError(error); 1323 } 1324 1325 for (PackageSet::iterator it = fRemovedPackages.begin(); 1326 it != fRemovedPackages.end(); ++it) { 1327 Package* package = *it; 1328 if (fPackagesAlreadyRemoved.find(package) 1329 != fPackagesAlreadyRemoved.end()) { 1330 continue; 1331 } 1332 1333 // get a BEntry for the package 1334 BEntry entry; 1335 status_t error = entry.SetTo(&fOldStateDirectory, 1336 package->FileName()); 1337 if (error != B_OK) { 1338 ERROR("failed to get entry for package \"%s\": %s\n", 1339 package->FileName().String(), strerror(error)); 1340 continue; 1341 } 1342 1343 // move entry 1344 error = entry.MoveTo(&packagesDirectory); 1345 if (error != B_OK) { 1346 ERROR("failed to move old package \"%s\" back to packages " 1347 "directory: %s\n", package->FileName().String(), 1348 strerror(error)); 1349 continue; 1350 } 1351 1352 fPackageFileManager->PackageFileMoved(package->File(), 1353 fVolume->PackagesDirectoryRef()); 1354 package->File()->IncrementEntryCreatedIgnoreLevel(); 1355 } 1356 } 1357 1358 1359 void 1360 CommitTransactionHandler::_RevertUserGroupChanges() 1361 { 1362 // delete users 1363 for (StringSet::const_iterator it = fAddedUsers.begin(); 1364 it != fAddedUsers.end(); ++it) { 1365 std::string commandLine("userdel "); 1366 commandLine += FSUtils::ShellEscapeString(it->c_str()).String(); 1367 if (system(commandLine.c_str()) != 0) 1368 ERROR("failed to remove user \"%s\"\n", it->c_str()); 1369 } 1370 1371 // delete groups 1372 for (StringSet::const_iterator it = fAddedGroups.begin(); 1373 it != fAddedGroups.end(); ++it) { 1374 std::string commandLine("groupdel "); 1375 commandLine += FSUtils::ShellEscapeString(it->c_str()).String(); 1376 if (system(commandLine.c_str()) != 0) 1377 ERROR("failed to remove group \"%s\"\n", it->c_str()); 1378 } 1379 } 1380 1381 1382 void 1383 CommitTransactionHandler::_RunPostInstallScripts() 1384 { 1385 for (PackageSet::iterator it = fAddedPackages.begin(); 1386 it != fAddedPackages.end(); ++it) { 1387 Package* package = *it; 1388 fCurrentPackage = package; 1389 const BStringList& scripts = package->Info().PostInstallScripts(); 1390 int32 count = scripts.CountStrings(); 1391 for (int32 i = 0; i < count; i++) 1392 _RunPostOrPreScript(package, scripts.StringAt(i), true); 1393 } 1394 1395 fCurrentPackage = NULL; 1396 } 1397 1398 1399 void 1400 CommitTransactionHandler::_RunPreUninstallScripts() 1401 { 1402 // Note this runs in the correct order, so dependents get uninstalled before 1403 // the packages they depend on. No need for a reversed loop. 1404 for (PackageSet::iterator it = fPackagesToDeactivate.begin(); 1405 it != fPackagesToDeactivate.end(); ++it) { 1406 Package* package = *it; 1407 fCurrentPackage = package; 1408 const BStringList& scripts = package->Info().PreUninstallScripts(); 1409 int32 count = scripts.CountStrings(); 1410 for (int32 i = 0; i < count; i++) 1411 _RunPostOrPreScript(package, scripts.StringAt(i), false); 1412 } 1413 1414 fCurrentPackage = NULL; 1415 } 1416 1417 1418 void 1419 CommitTransactionHandler::_RunPostOrPreScript(Package* package, 1420 const BString& script, bool postNotPre) 1421 { 1422 const char *postOrPreInstallWording = postNotPre 1423 ? "post-installation" : "pre-uninstall"; 1424 BDirectory rootDir(&fVolume->RootDirectoryRef()); 1425 BPath scriptPath(&rootDir, script); 1426 status_t error = scriptPath.InitCheck(); 1427 if (error != B_OK) { 1428 ERROR("Volume::CommitTransactionHandler::_RunPostOrPreScript(): " 1429 "failed get path of %s script \"%s\" of package " 1430 "%s: %s\n", 1431 postOrPreInstallWording, script.String(), 1432 package->FileName().String(), strerror(error)); 1433 _AddIssue(TransactionIssueBuilder(postNotPre 1434 ? BTransactionIssue::B_POST_INSTALL_SCRIPT_NOT_FOUND 1435 : BTransactionIssue::B_PRE_UNINSTALL_SCRIPT_NOT_FOUND) 1436 .SetPath1(script) 1437 .SetSystemError(error)); 1438 return; 1439 } 1440 1441 errno = 0; 1442 int result = system(scriptPath.Path()); 1443 if (result != 0) { 1444 ERROR("Volume::CommitTransactionHandler::_RunPostOrPreScript(): " 1445 "running %s script \"%s\" of package %s " 1446 "failed: %d (errno: %s)\n", 1447 postOrPreInstallWording, script.String(), 1448 package->FileName().String(), result, strerror(errno)); 1449 if (result < 0 || result == 127) { // bash shell returns 127 on failure. 1450 _AddIssue(TransactionIssueBuilder(postNotPre 1451 ? BTransactionIssue::B_STARTING_POST_INSTALL_SCRIPT_FAILED 1452 : BTransactionIssue::B_STARTING_PRE_UNINSTALL_SCRIPT_FAILED) 1453 .SetPath1(BString(scriptPath.Path())) 1454 .SetSystemError(errno)); 1455 } else { // positive is an exit code from the script itself. 1456 _AddIssue(TransactionIssueBuilder(postNotPre 1457 ? BTransactionIssue::B_POST_INSTALL_SCRIPT_FAILED 1458 : BTransactionIssue::B_PRE_UNINSTALL_SCRIPT_FAILED) 1459 .SetPath1(BString(scriptPath.Path())) 1460 .SetExitCode(result)); 1461 } 1462 } 1463 } 1464 1465 1466 void 1467 CommitTransactionHandler::_QueuePostInstallScripts() 1468 { 1469 BDirectory adminDirectory; 1470 status_t error = _OpenPackagesSubDirectory( 1471 RelativePath(kAdminDirectoryName), true, adminDirectory); 1472 if (error != B_OK) { 1473 ERROR("Failed to open administrative directory: %s\n", strerror(error)); 1474 return; 1475 } 1476 1477 BDirectory scriptsDirectory; 1478 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName); 1479 if (error == B_ENTRY_NOT_FOUND) 1480 error = adminDirectory.CreateDirectory(kQueuedScriptsDirectoryName, &scriptsDirectory); 1481 if (error != B_OK) { 1482 ERROR("Failed to open queued scripts directory: %s\n", strerror(error)); 1483 return; 1484 } 1485 1486 BDirectory rootDir(&fVolume->RootDirectoryRef()); 1487 for (PackageSet::iterator it = fAddedPackages.begin(); 1488 it != fAddedPackages.end(); ++it) { 1489 Package* package = *it; 1490 const BStringList& scripts = package->Info().PostInstallScripts(); 1491 for (int32 i = 0; i < scripts.CountStrings(); ++i) { 1492 BPath scriptPath(&rootDir, scripts.StringAt(i)); 1493 status_t error = scriptPath.InitCheck(); 1494 if (error != B_OK) { 1495 ERROR("Can't find script: %s\n", scripts.StringAt(i).String()); 1496 continue; 1497 } 1498 1499 // symlink to the script 1500 BSymLink scriptLink; 1501 scriptsDirectory.CreateSymLink(scriptPath.Leaf(), 1502 scriptPath.Path(), &scriptLink); 1503 if (scriptLink.InitCheck() != B_OK) { 1504 ERROR("Creating symlink failed: %s\n", strerror(scriptLink.InitCheck())); 1505 continue; 1506 } 1507 } 1508 } 1509 } 1510 1511 1512 void 1513 CommitTransactionHandler::_ExtractPackageContent(Package* package, 1514 const BStringList& contentPaths, BDirectory& targetDirectory, 1515 BDirectory& _extractedFilesDirectory) 1516 { 1517 // check whether the subdirectory already exists 1518 BString targetName(package->RevisionedNameThrows()); 1519 1520 BEntry targetEntry; 1521 status_t error = targetEntry.SetTo(&targetDirectory, targetName); 1522 if (error != B_OK) { 1523 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) 1524 .SetPath1(_GetPath( 1525 FSUtils::Entry(targetDirectory, targetName), 1526 targetName)) 1527 .SetPackageName(package->FileName()) 1528 .SetSystemError(error); 1529 } 1530 if (targetEntry.Exists()) { 1531 // nothing to do -- the very same version of the package has already 1532 // been extracted 1533 error = _extractedFilesDirectory.SetTo(&targetDirectory, 1534 targetName); 1535 if (error != B_OK) { 1536 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1537 .SetPath1(_GetPath( 1538 FSUtils::Entry(targetDirectory, targetName), 1539 targetName)) 1540 .SetPackageName(package->FileName()) 1541 .SetSystemError(error); 1542 } 1543 return; 1544 } 1545 1546 // create the subdirectory with a temporary name (remove, if it already 1547 // exists) 1548 BString temporaryTargetName = BString().SetToFormat("%s.tmp", 1549 targetName.String()); 1550 if (temporaryTargetName.IsEmpty()) 1551 throw std::bad_alloc(); 1552 1553 error = targetEntry.SetTo(&targetDirectory, temporaryTargetName); 1554 if (error != B_OK) { 1555 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) 1556 .SetPath1(_GetPath( 1557 FSUtils::Entry(targetDirectory, temporaryTargetName), 1558 temporaryTargetName)) 1559 .SetPackageName(package->FileName()) 1560 .SetSystemError(error); 1561 } 1562 1563 if (targetEntry.Exists()) { 1564 // remove pre-existing 1565 error = BRemoveEngine().RemoveEntry(FSUtils::Entry(targetEntry)); 1566 if (error != B_OK) { 1567 throw Exception(B_TRANSACTION_FAILED_TO_REMOVE_DIRECTORY) 1568 .SetPath1(_GetPath( 1569 FSUtils::Entry(targetDirectory, temporaryTargetName), 1570 temporaryTargetName)) 1571 .SetPackageName(package->FileName()) 1572 .SetSystemError(error); 1573 } 1574 } 1575 1576 BDirectory& subDirectory = _extractedFilesDirectory; 1577 FSTransaction::CreateOperation createSubDirectoryOperation( 1578 &fFSTransaction, 1579 FSUtils::Entry(targetDirectory, temporaryTargetName)); 1580 error = targetDirectory.CreateDirectory(temporaryTargetName, 1581 &subDirectory); 1582 if (error != B_OK) { 1583 throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY) 1584 .SetPath1(_GetPath( 1585 FSUtils::Entry(targetDirectory, temporaryTargetName), 1586 temporaryTargetName)) 1587 .SetPackageName(package->FileName()) 1588 .SetSystemError(error); 1589 } 1590 1591 createSubDirectoryOperation.Finished(); 1592 1593 // extract 1594 NotOwningEntryRef packageRef(package->EntryRef()); 1595 1596 int32 contentPathCount = contentPaths.CountStrings(); 1597 for (int32 i = 0; i < contentPathCount; i++) { 1598 const char* contentPath = contentPaths.StringAt(i); 1599 1600 error = FSUtils::ExtractPackageContent(FSUtils::Entry(packageRef), 1601 contentPath, FSUtils::Entry(subDirectory)); 1602 if (error != B_OK) { 1603 throw Exception(B_TRANSACTION_FAILED_TO_EXTRACT_PACKAGE_FILE) 1604 .SetPath1(contentPath) 1605 .SetPackageName(package->FileName()) 1606 .SetSystemError(error); 1607 } 1608 } 1609 1610 // tag all entries with the package attribute 1611 _TagPackageEntriesRecursively(subDirectory, targetName, true); 1612 1613 // rename the subdirectory 1614 error = targetEntry.Rename(targetName); 1615 if (error != B_OK) { 1616 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) 1617 .SetPath1(_GetPath( 1618 FSUtils::Entry(targetDirectory, temporaryTargetName), 1619 temporaryTargetName)) 1620 .SetPath2(targetName) 1621 .SetPackageName(package->FileName()) 1622 .SetSystemError(error); 1623 } 1624 1625 // keep the directory, regardless of whether the transaction is rolled 1626 // back 1627 createSubDirectoryOperation.Unregister(); 1628 } 1629 1630 1631 status_t 1632 CommitTransactionHandler::_OpenPackagesSubDirectory(const RelativePath& path, 1633 bool create, BDirectory& _directory) 1634 { 1635 // open the packages directory 1636 BDirectory directory; 1637 status_t error = directory.SetTo(&fVolume->PackagesDirectoryRef()); 1638 if (error != B_OK) { 1639 ERROR("CommitTransactionHandler::_OpenPackagesSubDirectory(): failed " 1640 "to open packages directory: %s\n", strerror(error)); 1641 RETURN_ERROR(error); 1642 } 1643 1644 return FSUtils::OpenSubDirectory(directory, path, create, _directory); 1645 } 1646 1647 1648 status_t 1649 CommitTransactionHandler::_OpenPackagesFile( 1650 const RelativePath& subDirectoryPath, const char* fileName, uint32 openMode, 1651 BFile& _file, BEntry* _entry) 1652 { 1653 BDirectory directory; 1654 if (!subDirectoryPath.IsEmpty()) { 1655 status_t error = _OpenPackagesSubDirectory(subDirectoryPath, 1656 (openMode & B_CREATE_FILE) != 0, directory); 1657 if (error != B_OK) { 1658 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to " 1659 "open packages subdirectory \"%s\": %s\n", 1660 subDirectoryPath.ToString().String(), strerror(error)); 1661 RETURN_ERROR(error); 1662 } 1663 } else { 1664 status_t error = directory.SetTo(&fVolume->PackagesDirectoryRef()); 1665 if (error != B_OK) { 1666 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to " 1667 "open packages directory: %s\n", strerror(error)); 1668 RETURN_ERROR(error); 1669 } 1670 } 1671 1672 BEntry stackEntry; 1673 BEntry& entry = _entry != NULL ? *_entry : stackEntry; 1674 status_t error = entry.SetTo(&directory, fileName); 1675 if (error != B_OK) { 1676 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to get " 1677 "entry for file: %s", strerror(error)); 1678 RETURN_ERROR(error); 1679 } 1680 1681 return _file.SetTo(&entry, openMode); 1682 } 1683 1684 1685 void 1686 CommitTransactionHandler::_WriteActivationFile( 1687 const RelativePath& directoryPath, const char* fileName, 1688 const PackageSet& toActivate, const PackageSet& toDeactivate, 1689 BEntry& _entry) 1690 { 1691 // create the content 1692 BString activationFileContent; 1693 _CreateActivationFileContent(toActivate, toDeactivate, 1694 activationFileContent); 1695 1696 // write the file 1697 status_t error = _WriteTextFile(directoryPath, fileName, 1698 activationFileContent, _entry); 1699 if (error != B_OK) { 1700 BString filePath = directoryPath.ToString() << '/' << fileName; 1701 throw Exception(B_TRANSACTION_FAILED_TO_WRITE_ACTIVATION_FILE) 1702 .SetPath1(_GetPath( 1703 FSUtils::Entry(fVolume->PackagesDirectoryRef(), filePath), 1704 filePath)) 1705 .SetSystemError(error); 1706 } 1707 } 1708 1709 1710 void 1711 CommitTransactionHandler::_CreateActivationFileContent( 1712 const PackageSet& toActivate, const PackageSet& toDeactivate, 1713 BString& _content) 1714 { 1715 BString activationFileContent; 1716 for (PackageFileNameHashTable::Iterator it 1717 = fVolumeState->ByFileNameIterator(); 1718 Package* package = it.Next();) { 1719 if (package->IsActive() 1720 && toDeactivate.find(package) == toDeactivate.end()) { 1721 int32 length = activationFileContent.Length(); 1722 activationFileContent << package->FileName() << '\n'; 1723 if (activationFileContent.Length() 1724 < length + package->FileName().Length() + 1) { 1725 throw Exception(B_TRANSACTION_NO_MEMORY); 1726 } 1727 } 1728 } 1729 1730 for (PackageSet::const_iterator it = toActivate.begin(); 1731 it != toActivate.end(); ++it) { 1732 Package* package = *it; 1733 int32 length = activationFileContent.Length(); 1734 activationFileContent << package->FileName() << '\n'; 1735 if (activationFileContent.Length() 1736 < length + package->FileName().Length() + 1) { 1737 throw Exception(B_TRANSACTION_NO_MEMORY); 1738 } 1739 } 1740 1741 _content = activationFileContent; 1742 } 1743 1744 1745 status_t 1746 CommitTransactionHandler::_WriteTextFile(const RelativePath& directoryPath, 1747 const char* fileName, const BString& content, BEntry& _entry) 1748 { 1749 BFile file; 1750 status_t error = _OpenPackagesFile(directoryPath, 1751 fileName, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE, file, &_entry); 1752 if (error != B_OK) { 1753 ERROR("CommitTransactionHandler::_WriteTextFile(): failed to create " 1754 "file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName, 1755 strerror(error)); 1756 return error; 1757 } 1758 1759 ssize_t bytesWritten = file.Write(content.String(), 1760 content.Length()); 1761 if (bytesWritten < 0) { 1762 ERROR("CommitTransactionHandler::_WriteTextFile(): failed to write " 1763 "file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName, 1764 strerror(bytesWritten)); 1765 return bytesWritten; 1766 } 1767 1768 return B_OK; 1769 } 1770 1771 1772 void 1773 CommitTransactionHandler::_ChangePackageActivation( 1774 const PackageSet& packagesToActivate, 1775 const PackageSet& packagesToDeactivate) 1776 { 1777 INFORM("CommitTransactionHandler::_ChangePackageActivation(): activating " 1778 "%zu, deactivating %zu packages\n", packagesToActivate.size(), 1779 packagesToDeactivate.size()); 1780 1781 // write the temporary package activation file 1782 BEntry activationFileEntry; 1783 _WriteActivationFile(RelativePath(kAdminDirectoryName), 1784 kTemporaryActivationFileName, packagesToActivate, packagesToDeactivate, 1785 activationFileEntry); 1786 1787 // notify packagefs 1788 if (fVolumeStateIsActive) { 1789 _ChangePackageActivationIOCtl(packagesToActivate, packagesToDeactivate); 1790 } else { 1791 // TODO: Notify packagefs that active packages have been moved or do 1792 // node monitoring in packagefs! 1793 } 1794 1795 // rename the temporary activation file to the final file 1796 status_t error = activationFileEntry.Rename(kActivationFileName, true); 1797 if (error != B_OK) { 1798 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) 1799 .SetPath1(_GetPath( 1800 FSUtils::Entry(activationFileEntry), 1801 activationFileEntry.Name())) 1802 .SetPath2(kActivationFileName) 1803 .SetSystemError(error); 1804 1805 // TODO: We should probably try to revert the activation changes, though that 1806 // will fail, if this method has been called in response to node monitoring 1807 // events. Alternatively moving the package activation file could be made part 1808 // of the ioctl(), since packagefs should be able to undo package changes until 1809 // the very end, unless running out of memory. In the end the situation would be 1810 // bad anyway, though, since the activation file may refer to removed packages 1811 // and things would be in an inconsistent state after rebooting. 1812 } 1813 1814 // Update our state, i.e. remove deactivated packages and mark activated 1815 // packages accordingly. 1816 fVolumeState->ActivationChanged(packagesToActivate, packagesToDeactivate); 1817 } 1818 1819 1820 void 1821 CommitTransactionHandler::_ChangePackageActivationIOCtl( 1822 const PackageSet& packagesToActivate, 1823 const PackageSet& packagesToDeactivate) 1824 { 1825 // compute the size of the allocation we need for the activation change 1826 // request 1827 int32 itemCount = packagesToActivate.size() + packagesToDeactivate.size(); 1828 size_t requestSize = sizeof(PackageFSActivationChangeRequest) 1829 + itemCount * sizeof(PackageFSActivationChangeItem); 1830 1831 for (PackageSet::iterator it = packagesToActivate.begin(); 1832 it != packagesToActivate.end(); ++it) { 1833 requestSize += (*it)->FileName().Length() + 1; 1834 } 1835 1836 for (PackageSet::iterator it = packagesToDeactivate.begin(); 1837 it != packagesToDeactivate.end(); ++it) { 1838 requestSize += (*it)->FileName().Length() + 1; 1839 } 1840 1841 // allocate and prepare the request 1842 PackageFSActivationChangeRequest* request 1843 = (PackageFSActivationChangeRequest*)malloc(requestSize); 1844 if (request == NULL) 1845 throw Exception(B_TRANSACTION_NO_MEMORY); 1846 MemoryDeleter requestDeleter(request); 1847 1848 request->itemCount = itemCount; 1849 1850 PackageFSActivationChangeItem* item = &request->items[0]; 1851 char* nameBuffer = (char*)(item + itemCount); 1852 1853 for (PackageSet::iterator it = packagesToActivate.begin(); 1854 it != packagesToActivate.end(); ++it, item++) { 1855 _FillInActivationChangeItem(item, PACKAGE_FS_ACTIVATE_PACKAGE, *it, 1856 nameBuffer); 1857 } 1858 1859 for (PackageSet::iterator it = packagesToDeactivate.begin(); 1860 it != packagesToDeactivate.end(); ++it, item++) { 1861 _FillInActivationChangeItem(item, PACKAGE_FS_DEACTIVATE_PACKAGE, *it, 1862 nameBuffer); 1863 } 1864 1865 // issue the request 1866 FileDescriptorCloser fd(fVolume->OpenRootDirectory()); 1867 if (!fd.IsSet()) { 1868 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1869 .SetPath1(_GetPath( 1870 FSUtils::Entry(fVolume->RootDirectoryRef()), 1871 "<packagefs root>")) 1872 .SetSystemError(fd.Get()); 1873 } 1874 1875 if (ioctl(fd.Get(), PACKAGE_FS_OPERATION_CHANGE_ACTIVATION, request, 1876 requestSize) != 0) { 1877 // TODO: We need more error information and error handling! 1878 throw Exception(B_TRANSACTION_FAILED_TO_CHANGE_PACKAGE_ACTIVATION) 1879 .SetSystemError(errno); 1880 } 1881 } 1882 1883 1884 void 1885 CommitTransactionHandler::_FillInActivationChangeItem( 1886 PackageFSActivationChangeItem* item, PackageFSActivationChangeType type, 1887 Package* package, char*& nameBuffer) 1888 { 1889 item->type = type; 1890 item->packageDeviceID = package->NodeRef().device; 1891 item->packageNodeID = package->NodeRef().node; 1892 item->nameLength = package->FileName().Length(); 1893 item->parentDeviceID = fVolume->PackagesDeviceID(); 1894 item->parentDirectoryID = fVolume->PackagesDirectoryID(); 1895 item->name = nameBuffer; 1896 strcpy(nameBuffer, package->FileName()); 1897 nameBuffer += package->FileName().Length() + 1; 1898 } 1899 1900 1901 bool 1902 CommitTransactionHandler::_IsSystemPackage(Package* package) 1903 { 1904 // package name should be "haiku[_<arch>]" 1905 const BString& name = package->Info().Name(); 1906 if (!name.StartsWith("haiku")) 1907 return false; 1908 if (name.Length() == 5) 1909 return true; 1910 if (name[5] != '_') 1911 return false; 1912 1913 BPackageArchitecture architecture; 1914 return BPackageInfo::GetArchitectureByName(name.String() + 6, architecture) 1915 == B_OK; 1916 } 1917 1918 1919 void 1920 CommitTransactionHandler::_AddIssue(const TransactionIssueBuilder& builder) 1921 { 1922 fResult.AddIssue(builder.BuildIssue(fCurrentPackage)); 1923 } 1924 1925 1926 /*static*/ BString 1927 CommitTransactionHandler::_GetPath(const FSUtils::Entry& entry, 1928 const BString& fallback) 1929 { 1930 BString path = entry.Path(); 1931 return path.IsEmpty() ? fallback : path; 1932 } 1933 1934 1935 /*static*/ void 1936 CommitTransactionHandler::_TagPackageEntriesRecursively(BDirectory& directory, 1937 const BString& value, bool nonDirectoriesOnly) 1938 { 1939 char buffer[offsetof(struct dirent, d_name) + B_FILE_NAME_LENGTH]; 1940 dirent *entry = (dirent*)buffer; 1941 while (directory.GetNextDirents(entry, sizeof(buffer), 1) == 1) { 1942 if (strcmp(entry->d_name, ".") == 0 1943 || strcmp(entry->d_name, "..") == 0) { 1944 continue; 1945 } 1946 1947 // determine type 1948 struct stat st; 1949 status_t error = directory.GetStatFor(entry->d_name, &st); 1950 if (error != B_OK) { 1951 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) 1952 .SetPath1(_GetPath( 1953 FSUtils::Entry(directory, entry->d_name), 1954 entry->d_name)) 1955 .SetSystemError(error); 1956 } 1957 bool isDirectory = S_ISDIR(st.st_mode); 1958 1959 // open the node and set the attribute 1960 BNode stackNode; 1961 BDirectory stackDirectory; 1962 BNode* node; 1963 if (isDirectory) { 1964 node = &stackDirectory; 1965 error = stackDirectory.SetTo(&directory, entry->d_name); 1966 } else { 1967 node = &stackNode; 1968 error = stackNode.SetTo(&directory, entry->d_name); 1969 } 1970 1971 if (error != B_OK) { 1972 throw Exception(isDirectory 1973 ? B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY 1974 : B_TRANSACTION_FAILED_TO_OPEN_FILE) 1975 .SetPath1(_GetPath( 1976 FSUtils::Entry(directory, entry->d_name), 1977 entry->d_name)) 1978 .SetSystemError(error); 1979 } 1980 1981 if (!isDirectory || !nonDirectoriesOnly) { 1982 error = node->WriteAttrString(kPackageFileAttribute, &value); 1983 if (error != B_OK) { 1984 throw Exception(B_TRANSACTION_FAILED_TO_WRITE_FILE_ATTRIBUTE) 1985 .SetPath1(_GetPath( 1986 FSUtils::Entry(directory, entry->d_name), 1987 entry->d_name)) 1988 .SetSystemError(error); 1989 } 1990 } 1991 1992 // recurse 1993 if (isDirectory) { 1994 _TagPackageEntriesRecursively(stackDirectory, value, 1995 nonDirectoriesOnly); 1996 } 1997 } 1998 } 1999 2000 2001 /*static*/ status_t 2002 CommitTransactionHandler::_AssertEntriesAreEqual(const BEntry& entry, 2003 const BDirectory* directory) 2004 { 2005 BFile a; 2006 status_t status = a.SetTo(&entry, B_READ_ONLY); 2007 if (status != B_OK) 2008 return status; 2009 2010 BFile b; 2011 status = b.SetTo(directory, entry.Name(), B_READ_ONLY); 2012 if (status != B_OK) 2013 return status; 2014 2015 off_t aSize; 2016 status = a.GetSize(&aSize); 2017 if (status != B_OK) 2018 return status; 2019 2020 off_t bSize; 2021 status = b.GetSize(&bSize); 2022 if (status != B_OK) 2023 return status; 2024 2025 if (aSize != bSize) 2026 return B_FILE_EXISTS; 2027 2028 const size_t bufferSize = 4096; 2029 uint8 aBuffer[bufferSize]; 2030 uint8 bBuffer[bufferSize]; 2031 2032 while (aSize > 0) { 2033 ssize_t aRead = a.Read(aBuffer, bufferSize); 2034 ssize_t bRead = b.Read(bBuffer, bufferSize); 2035 if (aRead < 0 || aRead != bRead) 2036 return B_FILE_EXISTS; 2037 if (memcmp(aBuffer, bBuffer, aRead) != 0) 2038 return B_FILE_EXISTS; 2039 aSize -= aRead; 2040 } 2041 2042 INFORM("CommitTransactionHandler::_AssertEntriesAreEqual(): " 2043 "Package file '%s' already exists in target folder " 2044 "with equal contents\n", entry.Name()); 2045 return B_OK; 2046 } 2047