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 // open parent directory of the source entry 882 const char* lastSlash = strrchr(file.Path(), '/'); 883 const BDirectory* sourceDirectory; 884 BDirectory stackSourceDirectory; 885 if (lastSlash != NULL) { 886 sourceDirectory = &stackSourceDirectory; 887 BString sourceParentPath(file.Path(), 888 lastSlash - file.Path().String()); 889 if (sourceParentPath.Length() == 0) 890 throw std::bad_alloc(); 891 892 status_t error = stackSourceDirectory.SetTo( 893 &extractedFilesDirectory, sourceParentPath); 894 if (error != B_OK) { 895 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 896 .SetPath1(_GetPath( 897 FSUtils::Entry(extractedFilesDirectory, sourceParentPath), 898 sourceParentPath)) 899 .SetPackageName(package->FileName()) 900 .SetSystemError(error); 901 } 902 } else { 903 sourceDirectory = &extractedFilesDirectory; 904 } 905 906 // open parent directory of the target entry -- create, if necessary 907 BString targetPath(file.Path()); 908 FSUtils::Path relativeSourcePath(file.Path()); 909 lastSlash = strrchr(targetPath, '/'); 910 if (lastSlash != NULL) { 911 BString targetParentPath(targetPath, 912 lastSlash - targetPath.String()); 913 if (targetParentPath.Length() == 0) 914 throw std::bad_alloc(); 915 916 BDirectory targetDirectory; 917 status_t error = FSUtils::OpenSubDirectory(rootDirectory, 918 RelativePath(targetParentPath), true, targetDirectory); 919 if (error != B_OK) { 920 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 921 .SetPath1(_GetPath( 922 FSUtils::Entry(rootDirectory, targetParentPath), 923 targetParentPath)) 924 .SetPackageName(package->FileName()) 925 .SetSystemError(error); 926 } 927 _AddGlobalWritableFileRecurse(package, *sourceDirectory, 928 relativeSourcePath, targetDirectory, lastSlash + 1, 929 file.UpdateType()); 930 } else { 931 _AddGlobalWritableFileRecurse(package, *sourceDirectory, 932 relativeSourcePath, rootDirectory, targetPath, 933 file.UpdateType()); 934 } 935 } 936 937 938 void 939 CommitTransactionHandler::_AddGlobalWritableFileRecurse(Package* package, 940 const BDirectory& sourceDirectory, FSUtils::Path& relativeSourcePath, 941 const BDirectory& targetDirectory, const char* targetName, 942 BWritableFileUpdateType updateType) 943 { 944 // * If the file doesn't exist, just copy the extracted one. 945 // * If the file does exist, compare with the previous original version: 946 // * If unchanged, just overwrite it. 947 // * If changed, leave it to the user for now. When we support merging 948 // first back the file up, then try the merge. 949 950 // Check whether the target location exists and what type the entry at 951 // both locations are. 952 struct stat targetStat; 953 if (targetDirectory.GetStatFor(targetName, &targetStat) != B_OK) { 954 // target doesn't exist -- just copy 955 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 956 "couldn't get stat for writable file \"%s\", copying...\n", 957 targetName); 958 FSTransaction::CreateOperation copyOperation(&fFSTransaction, 959 FSUtils::Entry(targetDirectory, targetName)); 960 status_t error = BCopyEngine(BCopyEngine::COPY_RECURSIVELY) 961 .CopyEntry( 962 FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()), 963 FSUtils::Entry(targetDirectory, targetName)); 964 if (error != B_OK) { 965 if (targetDirectory.GetStatFor(targetName, &targetStat) == B_OK) 966 copyOperation.Finished(); 967 968 throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE) 969 .SetPath1(_GetPath( 970 FSUtils::Entry(sourceDirectory, 971 relativeSourcePath.Leaf()), 972 relativeSourcePath)) 973 .SetPath2(_GetPath( 974 FSUtils::Entry(targetDirectory, targetName), 975 targetName)) 976 .SetSystemError(error); 977 } 978 copyOperation.Finished(); 979 return; 980 } 981 982 struct stat sourceStat; 983 status_t error = sourceDirectory.GetStatFor(relativeSourcePath.Leaf(), 984 &sourceStat); 985 if (error != B_OK) { 986 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) 987 .SetPath1(_GetPath( 988 FSUtils::Entry(sourceDirectory, 989 relativeSourcePath.Leaf()), 990 relativeSourcePath)) 991 .SetSystemError(error); 992 } 993 994 if ((sourceStat.st_mode & S_IFMT) != (targetStat.st_mode & S_IFMT) 995 || (!S_ISDIR(sourceStat.st_mode) && !S_ISREG(sourceStat.st_mode) 996 && !S_ISLNK(sourceStat.st_mode))) { 997 // Source and target entry types don't match or this is an entry 998 // we cannot handle. The user must handle this manually. 999 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1000 "writable file \"%s\" exists, but type doesn't match previous " 1001 "type\n", targetName); 1002 _AddIssue(TransactionIssueBuilder( 1003 BTransactionIssue::B_WRITABLE_FILE_TYPE_MISMATCH) 1004 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1005 .SetPath2(FSUtils::Entry(sourceDirectory, 1006 relativeSourcePath.Leaf()))); 1007 return; 1008 } 1009 1010 if (S_ISDIR(sourceStat.st_mode)) { 1011 // entry is a directory -- recurse 1012 BDirectory sourceSubDirectory; 1013 error = sourceSubDirectory.SetTo(&sourceDirectory, 1014 relativeSourcePath.Leaf()); 1015 if (error != B_OK) { 1016 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1017 .SetPath1(_GetPath( 1018 FSUtils::Entry(sourceDirectory, 1019 relativeSourcePath.Leaf()), 1020 relativeSourcePath)) 1021 .SetPackageName(package->FileName()) 1022 .SetSystemError(error); 1023 } 1024 1025 BDirectory targetSubDirectory; 1026 error = targetSubDirectory.SetTo(&targetDirectory, targetName); 1027 if (error != B_OK) { 1028 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1029 .SetPath1(_GetPath( 1030 FSUtils::Entry(targetDirectory, targetName), 1031 targetName)) 1032 .SetPackageName(package->FileName()) 1033 .SetSystemError(error); 1034 } 1035 1036 entry_ref entry; 1037 while (sourceSubDirectory.GetNextRef(&entry) == B_OK) { 1038 relativeSourcePath.AppendComponent(entry.name); 1039 _AddGlobalWritableFileRecurse(package, sourceSubDirectory, 1040 relativeSourcePath, targetSubDirectory, entry.name, 1041 updateType); 1042 relativeSourcePath.RemoveLastComponent(); 1043 } 1044 1045 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1046 "writable directory, recursion done\n"); 1047 return; 1048 } 1049 1050 // get the package the target file originated from 1051 BString originalPackage; 1052 if (BNode(&targetDirectory, targetName).ReadAttrString( 1053 kPackageFileAttribute, &originalPackage) != B_OK) { 1054 // Can't determine the original package. The user must handle this 1055 // manually. 1056 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1057 "failed to get SYS:PACKAGE attribute for \"%s\", can't tell if " 1058 "file needs to be updated\n", 1059 targetName); 1060 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { 1061 _AddIssue(TransactionIssueBuilder( 1062 BTransactionIssue::B_WRITABLE_FILE_NO_PACKAGE_ATTRIBUTE) 1063 .SetPath1(FSUtils::Entry(targetDirectory, targetName))); 1064 } 1065 return; 1066 } 1067 1068 // If that's our package, we're happy. 1069 if (originalPackage == package->RevisionedNameThrows()) { 1070 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1071 "file \"%s\" tagged with same package version we're activating\n", 1072 targetName); 1073 return; 1074 } 1075 1076 // Check, whether the writable-files directory for the original package 1077 // exists. 1078 BString originalRelativeSourcePath = BString().SetToFormat("%s/%s", 1079 originalPackage.String(), relativeSourcePath.ToCString()); 1080 if (originalRelativeSourcePath.IsEmpty()) 1081 throw std::bad_alloc(); 1082 1083 struct stat originalPackageStat; 1084 error = fWritableFilesDirectory.GetStatFor(originalRelativeSourcePath, 1085 &originalPackageStat); 1086 if (error != B_OK 1087 || (sourceStat.st_mode & S_IFMT) 1088 != (originalPackageStat.st_mode & S_IFMT)) { 1089 // Original entry doesn't exist (either we don't have the data from 1090 // the original package or the entry really didn't exist) or its 1091 // type differs from the expected one. The user must handle this 1092 // manually. 1093 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 1094 "original \"%s\" doesn't exist or has other type\n", 1095 _GetPath(FSUtils::Entry(fWritableFilesDirectory, 1096 originalRelativeSourcePath), 1097 originalRelativeSourcePath).String()); 1098 if (error != B_OK) { 1099 _AddIssue(TransactionIssueBuilder( 1100 BTransactionIssue 1101 ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_MISSING) 1102 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1103 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1104 originalRelativeSourcePath))); 1105 } else { 1106 _AddIssue(TransactionIssueBuilder( 1107 BTransactionIssue 1108 ::B_WRITABLE_FILE_OLD_ORIGINAL_FILE_TYPE_MISMATCH) 1109 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1110 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1111 originalRelativeSourcePath))); 1112 } 1113 return; 1114 } 1115 1116 if (S_ISREG(sourceStat.st_mode)) { 1117 // compare file content 1118 bool equal; 1119 error = FSUtils::CompareFileContent( 1120 FSUtils::Entry(fWritableFilesDirectory, 1121 originalRelativeSourcePath), 1122 FSUtils::Entry(targetDirectory, targetName), 1123 equal); 1124 // TODO: Merge support! 1125 if (error != B_OK || !equal) { 1126 // The comparison failed or the files differ. The user must 1127 // handle this manually. 1128 PRINT("Volume::CommitTransactionHandler::" 1129 "_AddGlobalWritableFile(): " 1130 "file comparison \"%s\" failed (%s) or files aren't equal\n", 1131 targetName, strerror(error)); 1132 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { 1133 if (error != B_OK) { 1134 _AddIssue(TransactionIssueBuilder( 1135 BTransactionIssue 1136 ::B_WRITABLE_FILE_COMPARISON_FAILED) 1137 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1138 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1139 originalRelativeSourcePath)) 1140 .SetSystemError(error)); 1141 } else { 1142 _AddIssue(TransactionIssueBuilder( 1143 BTransactionIssue 1144 ::B_WRITABLE_FILE_NOT_EQUAL) 1145 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1146 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1147 originalRelativeSourcePath))); 1148 } 1149 } 1150 return; 1151 } 1152 } else { 1153 // compare symlinks 1154 bool equal; 1155 error = FSUtils::CompareSymLinks( 1156 FSUtils::Entry(fWritableFilesDirectory, 1157 originalRelativeSourcePath), 1158 FSUtils::Entry(targetDirectory, targetName), 1159 equal); 1160 if (error != B_OK || !equal) { 1161 // The comparison failed or the symlinks differ. The user must 1162 // handle this manually. 1163 PRINT("Volume::CommitTransactionHandler::" 1164 "_AddGlobalWritableFile(): " 1165 "symlink comparison \"%s\" failed (%s) or symlinks aren't " 1166 "equal\n", targetName, strerror(error)); 1167 if (updateType != B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD) { 1168 if (error != B_OK) { 1169 _AddIssue(TransactionIssueBuilder( 1170 BTransactionIssue 1171 ::B_WRITABLE_SYMLINK_COMPARISON_FAILED) 1172 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1173 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1174 originalRelativeSourcePath)) 1175 .SetSystemError(error)); 1176 } else { 1177 _AddIssue(TransactionIssueBuilder( 1178 BTransactionIssue 1179 ::B_WRITABLE_SYMLINK_NOT_EQUAL) 1180 .SetPath1(FSUtils::Entry(targetDirectory, targetName)) 1181 .SetPath2(FSUtils::Entry(fWritableFilesDirectory, 1182 originalRelativeSourcePath))); 1183 } 1184 } 1185 return; 1186 } 1187 } 1188 1189 // Replace the existing file/symlink. We do that in two steps: First 1190 // copy the new file to a neighoring location, then move-replace the 1191 // old file. 1192 BString tempTargetName; 1193 tempTargetName.SetToFormat("%s.%s", targetName, 1194 package->RevisionedNameThrows().String()); 1195 if (tempTargetName.IsEmpty()) 1196 throw std::bad_alloc(); 1197 1198 // copy 1199 FSTransaction::CreateOperation copyOperation(&fFSTransaction, 1200 FSUtils::Entry(targetDirectory, tempTargetName)); 1201 1202 error = BCopyEngine(BCopyEngine::UNLINK_DESTINATION).CopyEntry( 1203 FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()), 1204 FSUtils::Entry(targetDirectory, tempTargetName)); 1205 if (error != B_OK) { 1206 throw Exception(B_TRANSACTION_FAILED_TO_COPY_FILE) 1207 .SetPath1(_GetPath( 1208 FSUtils::Entry(sourceDirectory, 1209 relativeSourcePath.Leaf()), 1210 relativeSourcePath)) 1211 .SetPath2(_GetPath( 1212 FSUtils::Entry(targetDirectory, tempTargetName), 1213 tempTargetName)) 1214 .SetSystemError(error); 1215 } 1216 1217 copyOperation.Finished(); 1218 1219 // rename 1220 FSTransaction::RemoveOperation renameOperation(&fFSTransaction, 1221 FSUtils::Entry(targetDirectory, targetName), 1222 FSUtils::Entry(fWritableFilesDirectory, 1223 originalRelativeSourcePath)); 1224 1225 BEntry targetEntry; 1226 error = targetEntry.SetTo(&targetDirectory, tempTargetName); 1227 if (error == B_OK) 1228 error = targetEntry.Rename(targetName, true); 1229 if (error != B_OK) { 1230 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) 1231 .SetPath1(_GetPath( 1232 FSUtils::Entry(targetDirectory, tempTargetName), 1233 tempTargetName)) 1234 .SetPath2(targetName) 1235 .SetSystemError(error); 1236 } 1237 1238 renameOperation.Finished(); 1239 copyOperation.Unregister(); 1240 } 1241 1242 1243 void 1244 CommitTransactionHandler::_RevertAddPackagesToActivate() 1245 { 1246 if (fAddedPackages.empty() || fFirstBootProcessing) 1247 return; 1248 1249 // open transaction directory 1250 BDirectory transactionDirectory; 1251 status_t error = transactionDirectory.SetTo(&fTransactionDirectoryRef); 1252 if (error != B_OK) { 1253 ERROR("failed to open transaction directory: %s\n", 1254 strerror(error)); 1255 } 1256 1257 for (PackageSet::iterator it = fAddedPackages.begin(); 1258 it != fAddedPackages.end(); ++it) { 1259 // remove package from the volume 1260 Package* package = *it; 1261 1262 if (fPackagesAlreadyAdded.find(package) 1263 != fPackagesAlreadyAdded.end()) { 1264 continue; 1265 } 1266 1267 fVolumeState->RemovePackage(package); 1268 1269 if (transactionDirectory.InitCheck() != B_OK) 1270 continue; 1271 1272 // get BEntry for the package 1273 NotOwningEntryRef entryRef(package->EntryRef()); 1274 BEntry entry; 1275 error = entry.SetTo(&entryRef); 1276 if (error != B_OK) { 1277 ERROR("failed to get entry for package \"%s\": %s\n", 1278 package->FileName().String(), strerror(error)); 1279 continue; 1280 } 1281 1282 // move entry 1283 error = entry.MoveTo(&transactionDirectory); 1284 if (error != B_OK) { 1285 ERROR("failed to move new package \"%s\" back to transaction " 1286 "directory: %s\n", package->FileName().String(), 1287 strerror(error)); 1288 continue; 1289 } 1290 1291 fPackageFileManager->PackageFileMoved(package->File(), 1292 fTransactionDirectoryRef); 1293 package->File()->IncrementEntryRemovedIgnoreLevel(); 1294 } 1295 } 1296 1297 1298 void 1299 CommitTransactionHandler::_RevertRemovePackagesToDeactivate() 1300 { 1301 if (fRemovedPackages.empty() || fFirstBootProcessing) 1302 return; 1303 1304 // open packages directory 1305 BDirectory packagesDirectory; 1306 status_t error 1307 = packagesDirectory.SetTo(&fVolume->PackagesDirectoryRef()); 1308 if (error != B_OK) { 1309 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1310 .SetPath1("<packages>") 1311 .SetSystemError(error); 1312 } 1313 1314 for (PackageSet::iterator it = fRemovedPackages.begin(); 1315 it != fRemovedPackages.end(); ++it) { 1316 Package* package = *it; 1317 if (fPackagesAlreadyRemoved.find(package) 1318 != fPackagesAlreadyRemoved.end()) { 1319 continue; 1320 } 1321 1322 // get a BEntry for the package 1323 BEntry entry; 1324 status_t error = entry.SetTo(&fOldStateDirectory, 1325 package->FileName()); 1326 if (error != B_OK) { 1327 ERROR("failed to get entry for package \"%s\": %s\n", 1328 package->FileName().String(), strerror(error)); 1329 continue; 1330 } 1331 1332 // move entry 1333 error = entry.MoveTo(&packagesDirectory); 1334 if (error != B_OK) { 1335 ERROR("failed to move old package \"%s\" back to packages " 1336 "directory: %s\n", package->FileName().String(), 1337 strerror(error)); 1338 continue; 1339 } 1340 1341 fPackageFileManager->PackageFileMoved(package->File(), 1342 fVolume->PackagesDirectoryRef()); 1343 package->File()->IncrementEntryCreatedIgnoreLevel(); 1344 } 1345 } 1346 1347 1348 void 1349 CommitTransactionHandler::_RevertUserGroupChanges() 1350 { 1351 // delete users 1352 for (StringSet::const_iterator it = fAddedUsers.begin(); 1353 it != fAddedUsers.end(); ++it) { 1354 std::string commandLine("userdel "); 1355 commandLine += FSUtils::ShellEscapeString(it->c_str()).String(); 1356 if (system(commandLine.c_str()) != 0) 1357 ERROR("failed to remove user \"%s\"\n", it->c_str()); 1358 } 1359 1360 // delete groups 1361 for (StringSet::const_iterator it = fAddedGroups.begin(); 1362 it != fAddedGroups.end(); ++it) { 1363 std::string commandLine("groupdel "); 1364 commandLine += FSUtils::ShellEscapeString(it->c_str()).String(); 1365 if (system(commandLine.c_str()) != 0) 1366 ERROR("failed to remove group \"%s\"\n", it->c_str()); 1367 } 1368 } 1369 1370 1371 void 1372 CommitTransactionHandler::_RunPostInstallScripts() 1373 { 1374 for (PackageSet::iterator it = fAddedPackages.begin(); 1375 it != fAddedPackages.end(); ++it) { 1376 Package* package = *it; 1377 fCurrentPackage = package; 1378 const BStringList& scripts = package->Info().PostInstallScripts(); 1379 int32 count = scripts.CountStrings(); 1380 for (int32 i = 0; i < count; i++) 1381 _RunPostOrPreScript(package, scripts.StringAt(i), true); 1382 } 1383 1384 fCurrentPackage = NULL; 1385 } 1386 1387 1388 void 1389 CommitTransactionHandler::_RunPreUninstallScripts() 1390 { 1391 // Note this runs in the correct order, so dependents get uninstalled before 1392 // the packages they depend on. No need for a reversed loop. 1393 for (PackageSet::iterator it = fPackagesToDeactivate.begin(); 1394 it != fPackagesToDeactivate.end(); ++it) { 1395 Package* package = *it; 1396 fCurrentPackage = package; 1397 const BStringList& scripts = package->Info().PreUninstallScripts(); 1398 int32 count = scripts.CountStrings(); 1399 for (int32 i = 0; i < count; i++) 1400 _RunPostOrPreScript(package, scripts.StringAt(i), false); 1401 } 1402 1403 fCurrentPackage = NULL; 1404 } 1405 1406 1407 void 1408 CommitTransactionHandler::_RunPostOrPreScript(Package* package, 1409 const BString& script, bool postNotPre) 1410 { 1411 const char *postOrPreInstallWording = postNotPre 1412 ? "post-installation" : "pre-uninstall"; 1413 BDirectory rootDir(&fVolume->RootDirectoryRef()); 1414 BPath scriptPath(&rootDir, script); 1415 status_t error = scriptPath.InitCheck(); 1416 if (error != B_OK) { 1417 ERROR("Volume::CommitTransactionHandler::_RunPostOrPreScript(): " 1418 "failed get path of %s script \"%s\" of package " 1419 "%s: %s\n", 1420 postOrPreInstallWording, script.String(), 1421 package->FileName().String(), strerror(error)); 1422 _AddIssue(TransactionIssueBuilder(postNotPre 1423 ? BTransactionIssue::B_POST_INSTALL_SCRIPT_NOT_FOUND 1424 : BTransactionIssue::B_PRE_UNINSTALL_SCRIPT_NOT_FOUND) 1425 .SetPath1(script) 1426 .SetSystemError(error)); 1427 return; 1428 } 1429 1430 errno = 0; 1431 int result = system(scriptPath.Path()); 1432 if (result != 0) { 1433 ERROR("Volume::CommitTransactionHandler::_RunPostOrPreScript(): " 1434 "running %s script \"%s\" of package %s " 1435 "failed: %d (errno: %s)\n", 1436 postOrPreInstallWording, script.String(), 1437 package->FileName().String(), result, strerror(errno)); 1438 if (result < 0 || result == 127) { // bash shell returns 127 on failure. 1439 _AddIssue(TransactionIssueBuilder(postNotPre 1440 ? BTransactionIssue::B_STARTING_POST_INSTALL_SCRIPT_FAILED 1441 : BTransactionIssue::B_STARTING_PRE_UNINSTALL_SCRIPT_FAILED) 1442 .SetPath1(BString(scriptPath.Path())) 1443 .SetSystemError(errno)); 1444 } else { // positive is an exit code from the script itself. 1445 _AddIssue(TransactionIssueBuilder(postNotPre 1446 ? BTransactionIssue::B_POST_INSTALL_SCRIPT_FAILED 1447 : BTransactionIssue::B_PRE_UNINSTALL_SCRIPT_FAILED) 1448 .SetPath1(BString(scriptPath.Path())) 1449 .SetExitCode(result)); 1450 } 1451 } 1452 } 1453 1454 1455 void 1456 CommitTransactionHandler::_QueuePostInstallScripts() 1457 { 1458 BDirectory adminDirectory; 1459 status_t error = _OpenPackagesSubDirectory( 1460 RelativePath(kAdminDirectoryName), true, adminDirectory); 1461 if (error != B_OK) { 1462 ERROR("Failed to open administrative directory: %s\n", strerror(error)); 1463 return; 1464 } 1465 1466 BDirectory scriptsDirectory; 1467 error = scriptsDirectory.SetTo(&adminDirectory, kQueuedScriptsDirectoryName); 1468 if (error == B_ENTRY_NOT_FOUND) 1469 error = adminDirectory.CreateDirectory(kQueuedScriptsDirectoryName, &scriptsDirectory); 1470 if (error != B_OK) { 1471 ERROR("Failed to open queued scripts directory: %s\n", strerror(error)); 1472 return; 1473 } 1474 1475 BDirectory rootDir(&fVolume->RootDirectoryRef()); 1476 for (PackageSet::iterator it = fAddedPackages.begin(); 1477 it != fAddedPackages.end(); ++it) { 1478 Package* package = *it; 1479 const BStringList& scripts = package->Info().PostInstallScripts(); 1480 for (int32 i = 0; i < scripts.CountStrings(); ++i) { 1481 BPath scriptPath(&rootDir, scripts.StringAt(i)); 1482 status_t error = scriptPath.InitCheck(); 1483 if (error != B_OK) { 1484 ERROR("Can't find script: %s\n", scripts.StringAt(i).String()); 1485 continue; 1486 } 1487 1488 // symlink to the script 1489 BSymLink scriptLink; 1490 scriptsDirectory.CreateSymLink(scriptPath.Leaf(), 1491 scriptPath.Path(), &scriptLink); 1492 if (scriptLink.InitCheck() != B_OK) { 1493 ERROR("Creating symlink failed: %s\n", strerror(scriptLink.InitCheck())); 1494 continue; 1495 } 1496 } 1497 } 1498 } 1499 1500 1501 void 1502 CommitTransactionHandler::_ExtractPackageContent(Package* package, 1503 const BStringList& contentPaths, BDirectory& targetDirectory, 1504 BDirectory& _extractedFilesDirectory) 1505 { 1506 // check whether the subdirectory already exists 1507 BString targetName(package->RevisionedNameThrows()); 1508 1509 BEntry targetEntry; 1510 status_t error = targetEntry.SetTo(&targetDirectory, targetName); 1511 if (error != B_OK) { 1512 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) 1513 .SetPath1(_GetPath( 1514 FSUtils::Entry(targetDirectory, targetName), 1515 targetName)) 1516 .SetPackageName(package->FileName()) 1517 .SetSystemError(error); 1518 } 1519 if (targetEntry.Exists()) { 1520 // nothing to do -- the very same version of the package has already 1521 // been extracted 1522 error = _extractedFilesDirectory.SetTo(&targetDirectory, 1523 targetName); 1524 if (error != B_OK) { 1525 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1526 .SetPath1(_GetPath( 1527 FSUtils::Entry(targetDirectory, targetName), 1528 targetName)) 1529 .SetPackageName(package->FileName()) 1530 .SetSystemError(error); 1531 } 1532 return; 1533 } 1534 1535 // create the subdirectory with a temporary name (remove, if it already 1536 // exists) 1537 BString temporaryTargetName = BString().SetToFormat("%s.tmp", 1538 targetName.String()); 1539 if (temporaryTargetName.IsEmpty()) 1540 throw std::bad_alloc(); 1541 1542 error = targetEntry.SetTo(&targetDirectory, temporaryTargetName); 1543 if (error != B_OK) { 1544 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) 1545 .SetPath1(_GetPath( 1546 FSUtils::Entry(targetDirectory, temporaryTargetName), 1547 temporaryTargetName)) 1548 .SetPackageName(package->FileName()) 1549 .SetSystemError(error); 1550 } 1551 1552 if (targetEntry.Exists()) { 1553 // remove pre-existing 1554 error = BRemoveEngine().RemoveEntry(FSUtils::Entry(targetEntry)); 1555 if (error != B_OK) { 1556 throw Exception(B_TRANSACTION_FAILED_TO_REMOVE_DIRECTORY) 1557 .SetPath1(_GetPath( 1558 FSUtils::Entry(targetDirectory, temporaryTargetName), 1559 temporaryTargetName)) 1560 .SetPackageName(package->FileName()) 1561 .SetSystemError(error); 1562 } 1563 } 1564 1565 BDirectory& subDirectory = _extractedFilesDirectory; 1566 FSTransaction::CreateOperation createSubDirectoryOperation( 1567 &fFSTransaction, 1568 FSUtils::Entry(targetDirectory, temporaryTargetName)); 1569 error = targetDirectory.CreateDirectory(temporaryTargetName, 1570 &subDirectory); 1571 if (error != B_OK) { 1572 throw Exception(B_TRANSACTION_FAILED_TO_CREATE_DIRECTORY) 1573 .SetPath1(_GetPath( 1574 FSUtils::Entry(targetDirectory, temporaryTargetName), 1575 temporaryTargetName)) 1576 .SetPackageName(package->FileName()) 1577 .SetSystemError(error); 1578 } 1579 1580 createSubDirectoryOperation.Finished(); 1581 1582 // extract 1583 NotOwningEntryRef packageRef(package->EntryRef()); 1584 1585 int32 contentPathCount = contentPaths.CountStrings(); 1586 for (int32 i = 0; i < contentPathCount; i++) { 1587 const char* contentPath = contentPaths.StringAt(i); 1588 1589 error = FSUtils::ExtractPackageContent(FSUtils::Entry(packageRef), 1590 contentPath, FSUtils::Entry(subDirectory)); 1591 if (error != B_OK) { 1592 throw Exception(B_TRANSACTION_FAILED_TO_EXTRACT_PACKAGE_FILE) 1593 .SetPath1(contentPath) 1594 .SetPackageName(package->FileName()) 1595 .SetSystemError(error); 1596 } 1597 } 1598 1599 // tag all entries with the package attribute 1600 _TagPackageEntriesRecursively(subDirectory, targetName, true); 1601 1602 // rename the subdirectory 1603 error = targetEntry.Rename(targetName); 1604 if (error != B_OK) { 1605 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) 1606 .SetPath1(_GetPath( 1607 FSUtils::Entry(targetDirectory, temporaryTargetName), 1608 temporaryTargetName)) 1609 .SetPath2(targetName) 1610 .SetPackageName(package->FileName()) 1611 .SetSystemError(error); 1612 } 1613 1614 // keep the directory, regardless of whether the transaction is rolled 1615 // back 1616 createSubDirectoryOperation.Unregister(); 1617 } 1618 1619 1620 status_t 1621 CommitTransactionHandler::_OpenPackagesSubDirectory(const RelativePath& path, 1622 bool create, BDirectory& _directory) 1623 { 1624 // open the packages directory 1625 BDirectory directory; 1626 status_t error = directory.SetTo(&fVolume->PackagesDirectoryRef()); 1627 if (error != B_OK) { 1628 ERROR("CommitTransactionHandler::_OpenPackagesSubDirectory(): failed " 1629 "to open packages directory: %s\n", strerror(error)); 1630 RETURN_ERROR(error); 1631 } 1632 1633 return FSUtils::OpenSubDirectory(directory, path, create, _directory); 1634 } 1635 1636 1637 status_t 1638 CommitTransactionHandler::_OpenPackagesFile( 1639 const RelativePath& subDirectoryPath, const char* fileName, uint32 openMode, 1640 BFile& _file, BEntry* _entry) 1641 { 1642 BDirectory directory; 1643 if (!subDirectoryPath.IsEmpty()) { 1644 status_t error = _OpenPackagesSubDirectory(subDirectoryPath, 1645 (openMode & B_CREATE_FILE) != 0, directory); 1646 if (error != B_OK) { 1647 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to " 1648 "open packages subdirectory \"%s\": %s\n", 1649 subDirectoryPath.ToString().String(), strerror(error)); 1650 RETURN_ERROR(error); 1651 } 1652 } else { 1653 status_t error = directory.SetTo(&fVolume->PackagesDirectoryRef()); 1654 if (error != B_OK) { 1655 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to " 1656 "open packages directory: %s\n", strerror(error)); 1657 RETURN_ERROR(error); 1658 } 1659 } 1660 1661 BEntry stackEntry; 1662 BEntry& entry = _entry != NULL ? *_entry : stackEntry; 1663 status_t error = entry.SetTo(&directory, fileName); 1664 if (error != B_OK) { 1665 ERROR("CommitTransactionHandler::_OpenPackagesFile(): failed to get " 1666 "entry for file: %s", strerror(error)); 1667 RETURN_ERROR(error); 1668 } 1669 1670 return _file.SetTo(&entry, openMode); 1671 } 1672 1673 1674 void 1675 CommitTransactionHandler::_WriteActivationFile( 1676 const RelativePath& directoryPath, const char* fileName, 1677 const PackageSet& toActivate, const PackageSet& toDeactivate, 1678 BEntry& _entry) 1679 { 1680 // create the content 1681 BString activationFileContent; 1682 _CreateActivationFileContent(toActivate, toDeactivate, 1683 activationFileContent); 1684 1685 // write the file 1686 status_t error = _WriteTextFile(directoryPath, fileName, 1687 activationFileContent, _entry); 1688 if (error != B_OK) { 1689 BString filePath = directoryPath.ToString() << '/' << fileName; 1690 throw Exception(B_TRANSACTION_FAILED_TO_WRITE_ACTIVATION_FILE) 1691 .SetPath1(_GetPath( 1692 FSUtils::Entry(fVolume->PackagesDirectoryRef(), filePath), 1693 filePath)) 1694 .SetSystemError(error); 1695 } 1696 } 1697 1698 1699 void 1700 CommitTransactionHandler::_CreateActivationFileContent( 1701 const PackageSet& toActivate, const PackageSet& toDeactivate, 1702 BString& _content) 1703 { 1704 BString activationFileContent; 1705 for (PackageFileNameHashTable::Iterator it 1706 = fVolumeState->ByFileNameIterator(); 1707 Package* package = it.Next();) { 1708 if (package->IsActive() 1709 && toDeactivate.find(package) == toDeactivate.end()) { 1710 int32 length = activationFileContent.Length(); 1711 activationFileContent << package->FileName() << '\n'; 1712 if (activationFileContent.Length() 1713 < length + package->FileName().Length() + 1) { 1714 throw Exception(B_TRANSACTION_NO_MEMORY); 1715 } 1716 } 1717 } 1718 1719 for (PackageSet::const_iterator it = toActivate.begin(); 1720 it != toActivate.end(); ++it) { 1721 Package* package = *it; 1722 int32 length = activationFileContent.Length(); 1723 activationFileContent << package->FileName() << '\n'; 1724 if (activationFileContent.Length() 1725 < length + package->FileName().Length() + 1) { 1726 throw Exception(B_TRANSACTION_NO_MEMORY); 1727 } 1728 } 1729 1730 _content = activationFileContent; 1731 } 1732 1733 1734 status_t 1735 CommitTransactionHandler::_WriteTextFile(const RelativePath& directoryPath, 1736 const char* fileName, const BString& content, BEntry& _entry) 1737 { 1738 BFile file; 1739 status_t error = _OpenPackagesFile(directoryPath, 1740 fileName, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE, file, &_entry); 1741 if (error != B_OK) { 1742 ERROR("CommitTransactionHandler::_WriteTextFile(): failed to create " 1743 "file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName, 1744 strerror(error)); 1745 return error; 1746 } 1747 1748 ssize_t bytesWritten = file.Write(content.String(), 1749 content.Length()); 1750 if (bytesWritten < 0) { 1751 ERROR("CommitTransactionHandler::_WriteTextFile(): failed to write " 1752 "file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName, 1753 strerror(bytesWritten)); 1754 return bytesWritten; 1755 } 1756 1757 return B_OK; 1758 } 1759 1760 1761 void 1762 CommitTransactionHandler::_ChangePackageActivation( 1763 const PackageSet& packagesToActivate, 1764 const PackageSet& packagesToDeactivate) 1765 { 1766 INFORM("CommitTransactionHandler::_ChangePackageActivation(): activating " 1767 "%zu, deactivating %zu packages\n", packagesToActivate.size(), 1768 packagesToDeactivate.size()); 1769 1770 // write the temporary package activation file 1771 BEntry activationFileEntry; 1772 _WriteActivationFile(RelativePath(kAdminDirectoryName), 1773 kTemporaryActivationFileName, packagesToActivate, packagesToDeactivate, 1774 activationFileEntry); 1775 1776 // notify packagefs 1777 if (fVolumeStateIsActive) { 1778 _ChangePackageActivationIOCtl(packagesToActivate, packagesToDeactivate); 1779 } else { 1780 // TODO: Notify packagefs that active packages have been moved or do 1781 // node monitoring in packagefs! 1782 } 1783 1784 // rename the temporary activation file to the final file 1785 status_t error = activationFileEntry.Rename(kActivationFileName, true); 1786 if (error != B_OK) { 1787 throw Exception(B_TRANSACTION_FAILED_TO_MOVE_FILE) 1788 .SetPath1(_GetPath( 1789 FSUtils::Entry(activationFileEntry), 1790 activationFileEntry.Name())) 1791 .SetPath2(kActivationFileName) 1792 .SetSystemError(error); 1793 1794 // TODO: We should probably try to revert the activation changes, though that 1795 // will fail, if this method has been called in response to node monitoring 1796 // events. Alternatively moving the package activation file could be made part 1797 // of the ioctl(), since packagefs should be able to undo package changes until 1798 // the very end, unless running out of memory. In the end the situation would be 1799 // bad anyway, though, since the activation file may refer to removed packages 1800 // and things would be in an inconsistent state after rebooting. 1801 } 1802 1803 // Update our state, i.e. remove deactivated packages and mark activated 1804 // packages accordingly. 1805 fVolumeState->ActivationChanged(packagesToActivate, packagesToDeactivate); 1806 } 1807 1808 1809 void 1810 CommitTransactionHandler::_ChangePackageActivationIOCtl( 1811 const PackageSet& packagesToActivate, 1812 const PackageSet& packagesToDeactivate) 1813 { 1814 // compute the size of the allocation we need for the activation change 1815 // request 1816 int32 itemCount = packagesToActivate.size() + packagesToDeactivate.size(); 1817 size_t requestSize = sizeof(PackageFSActivationChangeRequest) 1818 + itemCount * sizeof(PackageFSActivationChangeItem); 1819 1820 for (PackageSet::iterator it = packagesToActivate.begin(); 1821 it != packagesToActivate.end(); ++it) { 1822 requestSize += (*it)->FileName().Length() + 1; 1823 } 1824 1825 for (PackageSet::iterator it = packagesToDeactivate.begin(); 1826 it != packagesToDeactivate.end(); ++it) { 1827 requestSize += (*it)->FileName().Length() + 1; 1828 } 1829 1830 // allocate and prepare the request 1831 PackageFSActivationChangeRequest* request 1832 = (PackageFSActivationChangeRequest*)malloc(requestSize); 1833 if (request == NULL) 1834 throw Exception(B_TRANSACTION_NO_MEMORY); 1835 MemoryDeleter requestDeleter(request); 1836 1837 request->itemCount = itemCount; 1838 1839 PackageFSActivationChangeItem* item = &request->items[0]; 1840 char* nameBuffer = (char*)(item + itemCount); 1841 1842 for (PackageSet::iterator it = packagesToActivate.begin(); 1843 it != packagesToActivate.end(); ++it, item++) { 1844 _FillInActivationChangeItem(item, PACKAGE_FS_ACTIVATE_PACKAGE, *it, 1845 nameBuffer); 1846 } 1847 1848 for (PackageSet::iterator it = packagesToDeactivate.begin(); 1849 it != packagesToDeactivate.end(); ++it, item++) { 1850 _FillInActivationChangeItem(item, PACKAGE_FS_DEACTIVATE_PACKAGE, *it, 1851 nameBuffer); 1852 } 1853 1854 // issue the request 1855 FileDescriptorCloser fd(fVolume->OpenRootDirectory()); 1856 if (!fd.IsSet()) { 1857 throw Exception(B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY) 1858 .SetPath1(_GetPath( 1859 FSUtils::Entry(fVolume->RootDirectoryRef()), 1860 "<packagefs root>")) 1861 .SetSystemError(fd.Get()); 1862 } 1863 1864 if (ioctl(fd.Get(), PACKAGE_FS_OPERATION_CHANGE_ACTIVATION, request, 1865 requestSize) != 0) { 1866 // TODO: We need more error information and error handling! 1867 throw Exception(B_TRANSACTION_FAILED_TO_CHANGE_PACKAGE_ACTIVATION) 1868 .SetSystemError(errno); 1869 } 1870 } 1871 1872 1873 void 1874 CommitTransactionHandler::_FillInActivationChangeItem( 1875 PackageFSActivationChangeItem* item, PackageFSActivationChangeType type, 1876 Package* package, char*& nameBuffer) 1877 { 1878 item->type = type; 1879 item->packageDeviceID = package->NodeRef().device; 1880 item->packageNodeID = package->NodeRef().node; 1881 item->nameLength = package->FileName().Length(); 1882 item->parentDeviceID = fVolume->PackagesDeviceID(); 1883 item->parentDirectoryID = fVolume->PackagesDirectoryID(); 1884 item->name = nameBuffer; 1885 strcpy(nameBuffer, package->FileName()); 1886 nameBuffer += package->FileName().Length() + 1; 1887 } 1888 1889 1890 bool 1891 CommitTransactionHandler::_IsSystemPackage(Package* package) 1892 { 1893 // package name should be "haiku[_<arch>]" 1894 const BString& name = package->Info().Name(); 1895 if (!name.StartsWith("haiku")) 1896 return false; 1897 if (name.Length() == 5) 1898 return true; 1899 if (name[5] != '_') 1900 return false; 1901 1902 BPackageArchitecture architecture; 1903 return BPackageInfo::GetArchitectureByName(name.String() + 6, architecture) 1904 == B_OK; 1905 } 1906 1907 1908 void 1909 CommitTransactionHandler::_AddIssue(const TransactionIssueBuilder& builder) 1910 { 1911 fResult.AddIssue(builder.BuildIssue(fCurrentPackage)); 1912 } 1913 1914 1915 /*static*/ BString 1916 CommitTransactionHandler::_GetPath(const FSUtils::Entry& entry, 1917 const BString& fallback) 1918 { 1919 BString path = entry.Path(); 1920 return path.IsEmpty() ? fallback : path; 1921 } 1922 1923 1924 /*static*/ void 1925 CommitTransactionHandler::_TagPackageEntriesRecursively(BDirectory& directory, 1926 const BString& value, bool nonDirectoriesOnly) 1927 { 1928 char buffer[offsetof(struct dirent, d_name) + B_FILE_NAME_LENGTH]; 1929 dirent *entry = (dirent*)buffer; 1930 while (directory.GetNextDirents(entry, sizeof(buffer), 1) == 1) { 1931 if (strcmp(entry->d_name, ".") == 0 1932 || strcmp(entry->d_name, "..") == 0) { 1933 continue; 1934 } 1935 1936 // determine type 1937 struct stat st; 1938 status_t error = directory.GetStatFor(entry->d_name, &st); 1939 if (error != B_OK) { 1940 throw Exception(B_TRANSACTION_FAILED_TO_ACCESS_ENTRY) 1941 .SetPath1(_GetPath( 1942 FSUtils::Entry(directory, entry->d_name), 1943 entry->d_name)) 1944 .SetSystemError(error); 1945 } 1946 bool isDirectory = S_ISDIR(st.st_mode); 1947 1948 // open the node and set the attribute 1949 BNode stackNode; 1950 BDirectory stackDirectory; 1951 BNode* node; 1952 if (isDirectory) { 1953 node = &stackDirectory; 1954 error = stackDirectory.SetTo(&directory, entry->d_name); 1955 } else { 1956 node = &stackNode; 1957 error = stackNode.SetTo(&directory, entry->d_name); 1958 } 1959 1960 if (error != B_OK) { 1961 throw Exception(isDirectory 1962 ? B_TRANSACTION_FAILED_TO_OPEN_DIRECTORY 1963 : B_TRANSACTION_FAILED_TO_OPEN_FILE) 1964 .SetPath1(_GetPath( 1965 FSUtils::Entry(directory, entry->d_name), 1966 entry->d_name)) 1967 .SetSystemError(error); 1968 } 1969 1970 if (!isDirectory || !nonDirectoriesOnly) { 1971 error = node->WriteAttrString(kPackageFileAttribute, &value); 1972 if (error != B_OK) { 1973 throw Exception(B_TRANSACTION_FAILED_TO_WRITE_FILE_ATTRIBUTE) 1974 .SetPath1(_GetPath( 1975 FSUtils::Entry(directory, entry->d_name), 1976 entry->d_name)) 1977 .SetSystemError(error); 1978 } 1979 } 1980 1981 // recurse 1982 if (isDirectory) { 1983 _TagPackageEntriesRecursively(stackDirectory, value, 1984 nonDirectoriesOnly); 1985 } 1986 } 1987 } 1988 1989 1990 /*static*/ status_t 1991 CommitTransactionHandler::_AssertEntriesAreEqual(const BEntry& entry, 1992 const BDirectory* directory) 1993 { 1994 BFile a; 1995 status_t status = a.SetTo(&entry, B_READ_ONLY); 1996 if (status != B_OK) 1997 return status; 1998 1999 BFile b; 2000 status = b.SetTo(directory, entry.Name(), B_READ_ONLY); 2001 if (status != B_OK) 2002 return status; 2003 2004 off_t aSize; 2005 status = a.GetSize(&aSize); 2006 if (status != B_OK) 2007 return status; 2008 2009 off_t bSize; 2010 status = b.GetSize(&bSize); 2011 if (status != B_OK) 2012 return status; 2013 2014 if (aSize != bSize) 2015 return B_FILE_EXISTS; 2016 2017 const size_t bufferSize = 4096; 2018 uint8 aBuffer[bufferSize]; 2019 uint8 bBuffer[bufferSize]; 2020 2021 while (aSize > 0) { 2022 ssize_t aRead = a.Read(aBuffer, bufferSize); 2023 ssize_t bRead = b.Read(bBuffer, bufferSize); 2024 if (aRead < 0 || aRead != bRead) 2025 return B_FILE_EXISTS; 2026 if (memcmp(aBuffer, bBuffer, aRead) != 0) 2027 return B_FILE_EXISTS; 2028 aSize -= aRead; 2029 } 2030 2031 INFORM("CommitTransactionHandler::_AssertEntriesAreEqual(): " 2032 "Package file '%s' already exists in target folder " 2033 "with equal contents\n", entry.Name()); 2034 return B_OK; 2035 } 2036