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