1 /* 2 * Copyright 2013, 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 "Volume.h" 11 12 #include <errno.h> 13 #include <grp.h> 14 #include <pwd.h> 15 #include <stdlib.h> 16 #include <sys/stat.h> 17 #include <time.h> 18 #include <unistd.h> 19 20 #include <string> 21 22 #include <Directory.h> 23 #include <Entry.h> 24 #include <File.h> 25 #include <Looper.h> 26 #include <MessageRunner.h> 27 #include <NodeMonitor.h> 28 #include <Path.h> 29 30 #include <package/solver/Solver.h> 31 #include <package/solver/SolverPackage.h> 32 #include <package/solver/SolverProblem.h> 33 #include <package/solver/SolverProblemSolution.h> 34 #include <package/solver/SolverRepository.h> 35 #include <package/solver/SolverResult.h> 36 37 #include <AutoDeleter.h> 38 #include <AutoLocker.h> 39 #include <CopyEngine.h> 40 #include <NotOwningEntryRef.h> 41 #include <package/DaemonDefs.h> 42 #include <package/PackagesDirectoryDefs.h> 43 #include <RemoveEngine.h> 44 45 #include "DebugSupport.h" 46 #include "Exception.h" 47 #include "FSTransaction.h" 48 49 50 using namespace BPackageKit::BPrivate; 51 52 typedef std::set<std::string> StringSet; 53 54 55 static const char* const kPackageFileNameExtension = ".hpkg"; 56 static const char* const kAdminDirectoryName 57 = PACKAGES_DIRECTORY_ADMIN_DIRECTORY; 58 static const char* const kActivationFileName 59 = PACKAGES_DIRECTORY_ACTIVATION_FILE; 60 static const char* const kTemporaryActivationFileName 61 = PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp"; 62 static const char* const kWritableFilesDirectoryName = "writable-files"; 63 static const char* const kPackageFileAttribute = "SYS:PACKAGE"; 64 65 static const bigtime_t kHandleNodeMonitorEvents = 'nmon'; 66 67 static const bigtime_t kNodeMonitorEventHandlingDelay = 500000; 68 static const bigtime_t kCommunicationTimeout = 1000000; 69 70 71 // #pragma mark - Listener 72 73 74 Volume::Listener::~Listener() 75 { 76 } 77 78 79 // #pragma mark - NodeMonitorEvent 80 81 82 struct Volume::NodeMonitorEvent 83 : public DoublyLinkedListLinkImpl<NodeMonitorEvent> { 84 public: 85 NodeMonitorEvent(const BString& entryName, bool created) 86 : 87 fEntryName(entryName), 88 fCreated(created) 89 { 90 } 91 92 const BString& EntryName() const 93 { 94 return fEntryName; 95 } 96 97 bool WasCreated() const 98 { 99 return fCreated; 100 } 101 102 private: 103 BString fEntryName; 104 bool fCreated; 105 }; 106 107 108 // #pragma mark - CommitTransactionHandler 109 110 111 struct Volume::CommitTransactionHandler { 112 CommitTransactionHandler(Volume* volume, 113 const PackageSet& packagesAlreadyAdded, 114 const PackageSet& packagesAlreadyRemoved) 115 : 116 fVolume(volume), 117 fPackagesToActivate(), 118 fPackagesToDeactivate(), 119 fAddedPackages(), 120 fRemovedPackages(), 121 fPackagesAlreadyAdded(packagesAlreadyAdded), 122 fPackagesAlreadyRemoved(packagesAlreadyRemoved), 123 fAddedGroups(), 124 fAddedUsers(), 125 fFSTransaction() 126 { 127 } 128 129 ~CommitTransactionHandler() 130 { 131 // Delete Package objects we created in case of error (on success 132 // fPackagesToActivate will be empty). 133 int32 count = fPackagesToActivate.CountItems(); 134 for (int32 i = 0; i < count; i++) { 135 Package* package = fPackagesToActivate.ItemAt(i); 136 if (fPackagesAlreadyAdded.find(package) 137 == fPackagesAlreadyAdded.end()) { 138 delete package; 139 } 140 } 141 } 142 143 void HandleRequest(BMessage* request, BMessage* reply) 144 { 145 status_t error; 146 BActivationTransaction transaction(request, &error); 147 if (error == B_OK) 148 error = transaction.InitCheck(); 149 if (error != B_OK) { 150 if (error == B_NO_MEMORY) 151 throw Exception(B_NO_MEMORY); 152 throw Exception(B_DAEMON_BAD_REQUEST); 153 } 154 155 HandleRequest(transaction, reply); 156 } 157 158 void HandleRequest(const BActivationTransaction& transaction, 159 BMessage* reply) 160 { 161 // check the change count 162 if (transaction.ChangeCount() != fVolume->fChangeCount) 163 throw Exception(B_DAEMON_CHANGE_COUNT_MISMATCH); 164 165 // collect the packages to deactivate 166 _GetPackagesToDeactivate(transaction); 167 168 // read the packages to activate 169 _ReadPackagesToActivate(transaction); 170 171 // anything to do at all? 172 if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) { 173 throw Exception(B_DAEMON_BAD_REQUEST, 174 "no packages to activate or deactivate"); 175 } 176 177 _ApplyChanges(reply); 178 } 179 180 void HandleRequest(const PackageSet& packagesAdded, 181 const PackageSet& packagesRemoved) 182 { 183 // Copy package sets to fPackagesToActivate/fPackagesToDeactivate. The 184 // given sets are assumed to be identical to the ones specified in the 185 // constructor invocation (fPackagesAlreadyAdded, 186 // fPackagesAlreadyRemoved). 187 for (PackageSet::const_iterator it = packagesAdded.begin(); 188 it != packagesAdded.end(); ++it) { 189 if (!fPackagesToActivate.AddItem(*it)) 190 throw std::bad_alloc(); 191 } 192 193 fPackagesToDeactivate = packagesRemoved; 194 195 _ApplyChanges(NULL); 196 } 197 198 void Revert() 199 { 200 // move packages to activate back to transaction directory 201 _RevertAddPackagesToActivate(); 202 203 // move packages to deactivate back to packages directory 204 _RevertRemovePackagesToDeactivate(); 205 206 // revert user and group changes 207 _RevertUserGroupChanges(); 208 209 // Revert all other FS operations, i.e. the writable files changes as 210 // well as the creation of the old state directory. 211 fFSTransaction.RollBack(); 212 } 213 214 const BString& OldStateDirectoryName() const 215 { 216 return fOldStateDirectoryName; 217 } 218 219 private: 220 typedef BObjectList<Package> PackageList; 221 222 void _GetPackagesToDeactivate(const BActivationTransaction& transaction) 223 { 224 // get the number of packages to deactivate 225 const BStringList& packagesToDeactivate 226 = transaction.PackagesToDeactivate(); 227 int32 packagesToDeactivateCount = packagesToDeactivate.CountStrings(); 228 if (packagesToDeactivateCount == 0) 229 return; 230 231 for (int32 i = 0; i < packagesToDeactivateCount; i++) { 232 BString packageName = packagesToDeactivate.StringAt(i); 233 Package* package = fVolume->fPackagesByFileName.Lookup(packageName); 234 if (package == NULL) { 235 throw Exception(B_DAEMON_NO_SUCH_PACKAGE, "no such package", 236 packageName); 237 } 238 239 fPackagesToDeactivate.insert(package); 240 241 if (fPackagesAlreadyRemoved.find(package) 242 == fPackagesAlreadyRemoved.end()) { 243 package->IncrementEntryRemovedIgnoreLevel(); 244 } 245 } 246 } 247 248 void _ReadPackagesToActivate(const BActivationTransaction& transaction) 249 { 250 // get the number of packages to activate 251 const BStringList& packagesToActivate 252 = transaction.PackagesToActivate(); 253 int32 packagesToActivateCount = packagesToActivate.CountStrings(); 254 if (packagesToActivateCount == 0) 255 return; 256 257 // check the transaction directory name -- we only allow a simple 258 // subdirectory of the admin directory 259 const BString& transactionDirectoryName 260 = transaction.TransactionDirectoryName(); 261 if (transactionDirectoryName.IsEmpty() 262 || transactionDirectoryName.FindFirst('/') >= 0 263 || transactionDirectoryName == "." 264 || transactionDirectoryName == "..") { 265 throw Exception(B_DAEMON_BAD_REQUEST); 266 } 267 268 // open the directory 269 RelativePath directoryPath(kAdminDirectoryName, 270 transactionDirectoryName); 271 BDirectory directory; 272 status_t error = fVolume->_OpenPackagesSubDirectory(directoryPath, 273 false, directory); 274 if (error != B_OK) 275 throw Exception(error, "failed to open transaction directory"); 276 277 error = directory.GetNodeRef(&fTransactionDirectoryRef); 278 if (error != B_OK) { 279 throw Exception(error, 280 "failed to get transaction directory node ref"); 281 } 282 283 // read the packages 284 for (int32 i = 0; i < packagesToActivateCount; i++) { 285 BString packageName = packagesToActivate.StringAt(i); 286 287 // make sure it doesn't clash with an already existing package 288 Package* package = fVolume->fPackagesByFileName.Lookup(packageName); 289 if (package != NULL) { 290 if (fPackagesAlreadyAdded.find(package) 291 != fPackagesAlreadyAdded.end()) { 292 if (!fPackagesToActivate.AddItem(package)) 293 throw Exception(B_NO_MEMORY); 294 continue; 295 } 296 297 if (fPackagesToDeactivate.find(package) 298 == fPackagesToDeactivate.end()) { 299 throw Exception(B_DAEMON_PACKAGE_ALREADY_EXISTS, NULL, 300 packageName); 301 } 302 } 303 304 // read the package 305 entry_ref entryRef; 306 entryRef.device = fTransactionDirectoryRef.device; 307 entryRef.directory = fTransactionDirectoryRef.node; 308 if (entryRef.set_name(packageName) != B_OK) 309 throw Exception(B_NO_MEMORY); 310 311 package = new(std::nothrow) Package; 312 if (package == NULL || !fPackagesToActivate.AddItem(package)) { 313 delete package; 314 throw Exception(B_NO_MEMORY); 315 } 316 317 error = package->Init(entryRef); 318 if (error != B_OK) 319 throw Exception(error, "failed to read package", packageName); 320 321 package->IncrementEntryCreatedIgnoreLevel(); 322 } 323 } 324 325 void _ApplyChanges(BMessage* reply) 326 { 327 // create an old state directory 328 _CreateOldStateDirectory(reply); 329 330 // move packages to deactivate to old state directory 331 _RemovePackagesToDeactivate(); 332 333 // move packages to activate to packages directory 334 _AddPackagesToActivate(); 335 336 // activate/deactivate packages 337 fVolume->_ChangePackageActivation(fAddedPackages, fRemovedPackages); 338 339 // run post-installation scripts 340 _RunPostInstallScripts(); 341 342 // removed packages have been deleted, new packages shall not be deleted 343 fAddedPackages.clear(); 344 fRemovedPackages.clear(); 345 fPackagesToActivate.MakeEmpty(false); 346 fPackagesToDeactivate.clear(); 347 } 348 349 void _CreateOldStateDirectory(BMessage* reply) 350 { 351 // construct a nice name from the current date and time 352 time_t nowSeconds = time(NULL); 353 struct tm now; 354 BString baseName; 355 if (localtime_r(&nowSeconds, &now) != NULL) { 356 baseName.SetToFormat("state_%d-%02d-%02d_%02d:%02d:%02d", 357 1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour, 358 now.tm_min, now.tm_sec); 359 } else 360 baseName = "state"; 361 362 if (baseName.IsEmpty()) 363 throw Exception(B_NO_MEMORY); 364 365 // make sure the directory doesn't exist yet 366 BDirectory adminDirectory; 367 status_t error = fVolume->_OpenPackagesSubDirectory( 368 RelativePath(kAdminDirectoryName), true, adminDirectory); 369 if (error != B_OK) 370 throw Exception(error, "failed to open administrative directory"); 371 372 int uniqueId = 1; 373 BString directoryName = baseName; 374 while (BEntry(&adminDirectory, directoryName).Exists()) { 375 directoryName.SetToFormat("%s-%d", baseName.String(), uniqueId++); 376 if (directoryName.IsEmpty()) 377 throw Exception(B_NO_MEMORY); 378 } 379 380 // create the directory 381 FSTransaction::CreateOperation createOldStateDirectoryOperation( 382 &fFSTransaction, FSUtils::Entry(adminDirectory, directoryName)); 383 384 error = adminDirectory.CreateDirectory(directoryName, 385 &fOldStateDirectory); 386 if (error != B_OK) 387 throw Exception(error, "failed to create old state directory"); 388 389 createOldStateDirectoryOperation.Finished(); 390 391 fOldStateDirectoryName = directoryName; 392 393 // write the old activation file 394 BEntry activationFile; 395 error = fVolume->_WriteActivationFile( 396 RelativePath(kAdminDirectoryName, directoryName), 397 kActivationFileName, PackageSet(), PackageSet(), activationFile); 398 if (error != B_OK) 399 throw Exception(error, "failed to write old activation file"); 400 401 // add the old state directory to the reply 402 if (reply != NULL) { 403 error = reply->AddString("old state", fOldStateDirectoryName); 404 if (error != B_OK) 405 throw Exception(error, "failed to add field to reply"); 406 } 407 } 408 409 void _RemovePackagesToDeactivate() 410 { 411 if (fPackagesToDeactivate.empty()) 412 return; 413 414 for (PackageSet::const_iterator it = fPackagesToDeactivate.begin(); 415 it != fPackagesToDeactivate.end(); ++it) { 416 Package* package = *it; 417 if (fPackagesAlreadyRemoved.find(package) 418 != fPackagesAlreadyRemoved.end()) { 419 fRemovedPackages.insert(package); 420 continue; 421 } 422 423 // get a BEntry for the package 424 NotOwningEntryRef entryRef(fVolume->fPackagesDirectoryRef, 425 package->FileName()); 426 427 BEntry entry; 428 status_t error = entry.SetTo(&entryRef); 429 if (error != B_OK) { 430 throw Exception(error, "failed to get package entry", 431 package->FileName()); 432 } 433 434 // move entry 435 fRemovedPackages.insert(package); 436 437 error = entry.MoveTo(&fOldStateDirectory); 438 if (error != B_OK) { 439 fRemovedPackages.erase(package); 440 throw Exception(error, 441 "failed to move old package from packages directory", 442 package->FileName()); 443 } 444 } 445 } 446 447 void _AddPackagesToActivate() 448 { 449 if (fPackagesToActivate.IsEmpty()) 450 return; 451 452 // open packages directory 453 BDirectory packagesDirectory; 454 status_t error 455 = packagesDirectory.SetTo(&fVolume->fPackagesDirectoryRef); 456 if (error != B_OK) 457 throw Exception(error, "failed to open packages directory"); 458 459 int32 count = fPackagesToActivate.CountItems(); 460 for (int32 i = 0; i < count; i++) { 461 Package* package = fPackagesToActivate.ItemAt(i); 462 if (fPackagesAlreadyAdded.find(package) 463 != fPackagesAlreadyAdded.end()) { 464 fAddedPackages.insert(package); 465 _PreparePackageToActivate(package); 466 continue; 467 } 468 469 // get a BEntry for the package 470 entry_ref entryRef; 471 entryRef.device = fTransactionDirectoryRef.device; 472 entryRef.directory = fTransactionDirectoryRef.node; 473 if (entryRef.set_name(package->FileName()) != B_OK) 474 throw Exception(B_NO_MEMORY); 475 476 BEntry entry; 477 error = entry.SetTo(&entryRef); 478 if (error != B_OK) { 479 throw Exception(error, "failed to get package entry", 480 package->FileName()); 481 } 482 483 // move entry 484 fAddedPackages.insert(package); 485 486 error = entry.MoveTo(&packagesDirectory); 487 if (error != B_OK) { 488 fAddedPackages.erase(package); 489 throw Exception(error, 490 "failed to move new package to packages directory", 491 package->FileName()); 492 } 493 494 // also add the package to the volume 495 fVolume->_AddPackage(package); 496 497 _PreparePackageToActivate(package); 498 } 499 } 500 501 void _PreparePackageToActivate(Package* package) 502 { 503 // add groups 504 const BStringList& groups = package->Info().Groups(); 505 int32 count = groups.CountStrings(); 506 for (int32 i = 0; i < count; i++) 507 _AddGroup(package, groups.StringAt(i)); 508 509 // add users 510 const BObjectList<BUser>& users = package->Info().Users(); 511 for (int32 i = 0; const BUser* user = users.ItemAt(i); i++) 512 _AddUser(package, *user); 513 514 // handle global writable files 515 _AddGlobalWritableFiles(package); 516 } 517 518 void _AddGroup(Package* package, const BString& groupName) 519 { 520 // Check whether the group already exists. 521 char buffer[256]; 522 struct group groupBuffer; 523 struct group* groupFound; 524 int error = getgrnam_r(groupName, &groupBuffer, buffer, sizeof(buffer), 525 &groupFound); 526 if ((error == 0 && groupFound != NULL) || error == ERANGE) 527 return; 528 529 // add it 530 fAddedGroups.insert(groupName.String()); 531 532 std::string commandLine("groupadd "); 533 commandLine += FSUtils::ShellEscapeString(groupName).String(); 534 535 if (system(commandLine.c_str()) != 0) { 536 fAddedGroups.erase(groupName.String()); 537 throw Exception(error, 538 BString().SetToFormat("failed to add group \%s\"", 539 groupName.String()), 540 package->FileName()); 541 } 542 } 543 544 void _AddUser(Package* package, const BUser& user) 545 { 546 // Check whether the user already exists. 547 char buffer[256]; 548 struct passwd passwdBuffer; 549 struct passwd* passwdFound; 550 int error = getpwnam_r(user.Name(), &passwdBuffer, buffer, 551 sizeof(buffer), &passwdFound); 552 if ((error == 0 && passwdFound != NULL) || error == ERANGE) 553 return; 554 555 // add it 556 fAddedUsers.insert(user.Name().String()); 557 558 std::string commandLine("useradd "); 559 560 if (!user.RealName().IsEmpty()) { 561 commandLine += std::string("-n ") 562 + FSUtils::ShellEscapeString(user.RealName()).String() + " "; 563 } 564 565 if (!user.Home().IsEmpty()) { 566 commandLine += std::string("-d ") 567 + FSUtils::ShellEscapeString(user.Home()).String() + " "; 568 } 569 570 if (!user.Shell().IsEmpty()) { 571 commandLine += std::string("-s ") 572 + FSUtils::ShellEscapeString(user.Shell()).String() + " "; 573 } 574 575 if (!user.Groups().IsEmpty()) { 576 commandLine += std::string("-g ") 577 + FSUtils::ShellEscapeString(user.Groups().First()).String() 578 + " "; 579 } 580 581 commandLine += FSUtils::ShellEscapeString(user.Name()).String(); 582 583 if (system(commandLine.c_str()) != 0) { 584 fAddedUsers.erase(user.Name().String()); 585 throw Exception(error, 586 BString().SetToFormat("failed to add user \%s\"", 587 user.Name().String()), 588 package->FileName()); 589 } 590 591 // add the supplementary groups 592 int32 groupCount = user.Groups().CountStrings(); 593 for (int32 i = 1; i < groupCount; i++) { 594 commandLine = std::string("groupmod -A ") 595 + FSUtils::ShellEscapeString(user.Name()).String() 596 + " " 597 + FSUtils::ShellEscapeString(user.Groups().StringAt(i)) 598 .String(); 599 if (system(commandLine.c_str()) != 0) { 600 fAddedUsers.erase(user.Name().String()); 601 throw Exception(error, 602 BString().SetToFormat("failed to add user \%s\" to group " 603 "\"%s\"", user.Name().String(), 604 user.Groups().StringAt(i).String()), 605 package->FileName()); 606 } 607 } 608 } 609 610 void _AddGlobalWritableFiles(Package* package) 611 { 612 // get the list of included files 613 const BObjectList<BGlobalWritableFileInfo>& files 614 = package->Info().GlobalWritableFileInfos(); 615 BStringList contentPaths; 616 for (int32 i = 0; const BGlobalWritableFileInfo* file = files.ItemAt(i); 617 i++) { 618 if (file->IsIncluded() && !contentPaths.Add(file->Path())) 619 throw std::bad_alloc(); 620 } 621 622 if (contentPaths.IsEmpty()) 623 return; 624 625 // Open the root directory of the installation location where we will 626 // extract the files -- that's the volume's root directory. 627 BDirectory rootDirectory; 628 status_t error = rootDirectory.SetTo(&fVolume->fRootDirectoryRef); 629 if (error != B_OK) { 630 throw Exception(error, 631 BString().SetToFormat("failed to get the root directory " 632 "for writable files"), 633 package->FileName()); 634 } 635 636 // Open writable-files directory in the administrative directory. 637 if (fWritableFilesDirectory.InitCheck() != B_OK) { 638 error = fVolume->_OpenPackagesSubDirectory( 639 RelativePath(kAdminDirectoryName, kWritableFilesDirectoryName), 640 true, fWritableFilesDirectory); 641 642 if (error != B_OK) { 643 throw Exception(error, 644 BString().SetToFormat("failed to get the backup directory " 645 "for writable files"), 646 package->FileName()); 647 } 648 } 649 650 // extract files into a subdir of the writable-files directory 651 BDirectory extractedFilesDirectory; 652 _ExtractPackageContent(package, contentPaths, 653 fWritableFilesDirectory, extractedFilesDirectory); 654 655 for (int32 i = 0; const BGlobalWritableFileInfo* file = files.ItemAt(i); 656 i++) { 657 if (file->IsIncluded()) { 658 _AddGlobalWritableFile(package, *file, rootDirectory, 659 extractedFilesDirectory); 660 } 661 } 662 } 663 664 void _AddGlobalWritableFile(Package* package, 665 const BGlobalWritableFileInfo& file, const BDirectory& rootDirectory, 666 const BDirectory& extractedFilesDirectory) 667 { 668 // Map the path name to the actual target location. Currently this only 669 // concerns "settings/", which is mapped to "settings/global/". 670 BString targetPath(file.Path()); 671 if (fVolume->fMountType == PACKAGE_FS_MOUNT_TYPE_HOME) { 672 if (targetPath == "settings" 673 || targetPath.StartsWith("settings/")) { 674 targetPath.Insert("/global", 8); 675 if (targetPath.Length() == file.Path().Length()) 676 throw std::bad_alloc(); 677 } 678 } 679 680 // open parent directory of the source entry 681 const char* lastSlash = strrchr(file.Path(), '/'); 682 const BDirectory* sourceDirectory; 683 BDirectory stackSourceDirectory; 684 if (lastSlash != NULL) { 685 sourceDirectory = &stackSourceDirectory; 686 BString sourceParentPath(file.Path(), 687 lastSlash - file.Path().String()); 688 if (sourceParentPath.Length() == 0) 689 throw std::bad_alloc(); 690 691 status_t error = stackSourceDirectory.SetTo( 692 &extractedFilesDirectory, sourceParentPath); 693 if (error != B_OK) { 694 throw Exception(error, 695 BString().SetToFormat("failed to open directory \"%s\"", 696 _GetPath( 697 FSUtils::Entry(extractedFilesDirectory, 698 sourceParentPath), 699 sourceParentPath).String()), 700 package->FileName()); 701 } 702 } else { 703 sourceDirectory = &extractedFilesDirectory; 704 } 705 706 // open parent directory of the target entry -- create, if necessary 707 FSUtils::Path relativeSourcePath(file.Path()); 708 lastSlash = strrchr(targetPath, '/'); 709 if (lastSlash != NULL) { 710 BString targetParentPath(targetPath, 711 lastSlash - targetPath.String()); 712 if (targetParentPath.Length() == 0) 713 throw std::bad_alloc(); 714 715 BDirectory targetDirectory; 716 status_t error = FSUtils::OpenSubDirectory(rootDirectory, 717 RelativePath(targetParentPath), true, targetDirectory); 718 if (error != B_OK) { 719 throw Exception(error, 720 BString().SetToFormat("failed to open/create directory " 721 "\"%s\"", 722 _GetPath( 723 FSUtils::Entry(rootDirectory,targetParentPath), 724 targetParentPath).String()), 725 package->FileName()); 726 } 727 _AddGlobalWritableFileRecurse(package, *sourceDirectory, 728 relativeSourcePath, targetDirectory, lastSlash + 1, 729 file.UpdateType()); 730 } else { 731 _AddGlobalWritableFileRecurse(package, *sourceDirectory, 732 relativeSourcePath, rootDirectory, targetPath, 733 file.UpdateType()); 734 } 735 } 736 737 void _AddGlobalWritableFileRecurse(Package* package, 738 const BDirectory& sourceDirectory, FSUtils::Path& relativeSourcePath, 739 const BDirectory& targetDirectory, const char* targetName, 740 BWritableFileUpdateType updateType) 741 { 742 // * If the file doesn't exist, just copy the extracted one. 743 // * If the file does exist, compare with the previous original version: 744 // * If unchanged, just overwrite it. 745 // * If changed, leave it to the user for now. When we support merging 746 // first back the file up, then try the merge. 747 748 // Check whether the target location exists and what type the entry at 749 // both locations are. 750 struct stat targetStat; 751 if (targetDirectory.GetStatFor(targetName, &targetStat) != B_OK) { 752 // target doesn't exist -- just copy 753 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 754 "couldn't get stat for writable file, copying...\n"); 755 FSTransaction::CreateOperation copyOperation(&fFSTransaction, 756 FSUtils::Entry(targetDirectory, targetName)); 757 status_t error = BCopyEngine(BCopyEngine::COPY_RECURSIVELY) 758 .CopyEntry( 759 FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()), 760 FSUtils::Entry(targetDirectory, targetName)); 761 if (error != B_OK) { 762 if (targetDirectory.GetStatFor(targetName, &targetStat) == B_OK) 763 copyOperation.Finished(); 764 765 throw Exception(error, 766 BString().SetToFormat("failed to copy entry \"%s\"", 767 _GetPath( 768 FSUtils::Entry(sourceDirectory, 769 relativeSourcePath.Leaf()), 770 relativeSourcePath).String()), 771 package->FileName()); 772 } 773 copyOperation.Finished(); 774 return; 775 } 776 777 struct stat sourceStat; 778 status_t error = sourceDirectory.GetStatFor(relativeSourcePath.Leaf(), 779 &sourceStat); 780 if (error != B_OK) { 781 throw Exception(error, 782 BString().SetToFormat("failed to get stat data for entry " 783 "\"%s\"", 784 _GetPath( 785 FSUtils::Entry(targetDirectory, targetName), 786 targetName).String()), 787 package->FileName()); 788 } 789 790 if ((sourceStat.st_mode & S_IFMT) != (targetStat.st_mode & S_IFMT) 791 || (!S_ISDIR(sourceStat.st_mode) && !S_ISREG(sourceStat.st_mode) 792 && !S_ISLNK(sourceStat.st_mode))) { 793 // Source and target entry types don't match or this is an entry 794 // we cannot handle. The user must handle this manually. 795 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 796 "writable file exists, but type doesn't match previous type\n"); 797 // TODO: Notify user! 798 return; 799 } 800 801 if (S_ISDIR(sourceStat.st_mode)) { 802 // entry is a directory -- recurse 803 BDirectory sourceSubDirectory; 804 error = sourceSubDirectory.SetTo(&sourceDirectory, 805 relativeSourcePath.Leaf()); 806 if (error != B_OK) { 807 throw Exception(error, 808 BString().SetToFormat("failed to open directory \"%s\"", 809 _GetPath( 810 FSUtils::Entry(sourceDirectory, 811 relativeSourcePath.Leaf()), 812 relativeSourcePath).String()), 813 package->FileName()); 814 } 815 816 BDirectory targetSubDirectory; 817 error = targetSubDirectory.SetTo(&targetDirectory, targetName); 818 if (error != B_OK) { 819 throw Exception(error, 820 BString().SetToFormat("failed to open directory \"%s\"", 821 _GetPath( 822 FSUtils::Entry(targetDirectory, targetName), 823 targetName).String()), 824 package->FileName()); 825 } 826 827 entry_ref entry; 828 while (sourceSubDirectory.GetNextRef(&entry) == B_OK) { 829 relativeSourcePath.AppendComponent(entry.name); 830 _AddGlobalWritableFileRecurse(package, sourceSubDirectory, 831 relativeSourcePath, targetSubDirectory, entry.name, 832 updateType); 833 relativeSourcePath.RemoveLastComponent(); 834 } 835 836 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 837 "writable directory, recursion done\n"); 838 return; 839 } 840 841 // get the package the target file originated from 842 BString originalPackage; 843 if (BNode(&targetDirectory, targetName).ReadAttrString( 844 kPackageFileAttribute, &originalPackage) != B_OK) { 845 // Can't determine the original package. The user must handle this 846 // manually. 847 // TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD! 848 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 849 "failed to get SYS:PACKAGE attribute\n"); 850 return; 851 } 852 853 // If that's our package, we're happy. 854 if (originalPackage == package->RevisionedNameThrows()) { 855 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 856 "file tagged with same package version we're activating\n"); 857 return; 858 } 859 860 // Check, whether the writable-files directory for the original package 861 // exists. 862 BString originalRelativeSourcePath = BString().SetToFormat("%s/%s", 863 originalPackage.String(), relativeSourcePath.ToCString()); 864 if (originalRelativeSourcePath.IsEmpty()) 865 throw std::bad_alloc(); 866 867 struct stat originalPackageStat; 868 if (fWritableFilesDirectory.GetStatFor(originalRelativeSourcePath, 869 &originalPackageStat) != B_OK 870 || (sourceStat.st_mode & S_IFMT) 871 != (originalPackageStat.st_mode & S_IFMT)) { 872 // Original entry doesn't exist (either we don't have the data from 873 // the original package or the entry really didn't exist) or its 874 // type differs from the expected one. The user must handle this 875 // manually. 876 PRINT("Volume::CommitTransactionHandler::_AddGlobalWritableFile(): " 877 "original \"%s\" doesn't exist or has other type\n", 878 _GetPath(FSUtils::Entry(fWritableFilesDirectory, 879 originalRelativeSourcePath), 880 originalRelativeSourcePath).String()); 881 return; 882 // TODO: Notify user! 883 } 884 885 if (S_ISREG(sourceStat.st_mode)) { 886 // compare file content 887 bool equal; 888 error = FSUtils::CompareFileContent( 889 FSUtils::Entry(fWritableFilesDirectory, 890 originalRelativeSourcePath), 891 FSUtils::Entry(targetDirectory, targetName), 892 equal); 893 // TODO: Merge support! 894 if (error != B_OK || !equal) { 895 // The comparison failed or the files differ. The user must 896 // handle this manually. 897 PRINT("Volume::CommitTransactionHandler::" 898 "_AddGlobalWritableFile(): " 899 "file comparison failed (%s) or files aren't equal\n", 900 strerror(error)); 901 return; 902 // TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD! 903 } 904 } else { 905 // compare symlinks 906 bool equal; 907 error = FSUtils::CompareSymLinks( 908 FSUtils::Entry(fWritableFilesDirectory, 909 originalRelativeSourcePath), 910 FSUtils::Entry(targetDirectory, targetName), 911 equal); 912 if (error != B_OK || !equal) { 913 // The comparison failed or the symlinks differ. The user must 914 // handle this manually. 915 PRINT("Volume::CommitTransactionHandler::" 916 "_AddGlobalWritableFile(): " 917 "symlink comparison failed (%s) or symlinks aren't equal\n", 918 strerror(error)); 919 return; 920 // TODO: Notify user, if not B_WRITABLE_FILE_UPDATE_TYPE_KEEP_OLD! 921 } 922 } 923 924 // Replace the existing file/symlink. We do that in two steps: First 925 // copy the new file to a neighoring location, then move-replace the 926 // old file. 927 BString tempTargetName; 928 tempTargetName.SetToFormat("%s.%s", targetName, 929 package->RevisionedNameThrows().String()); 930 if (tempTargetName.IsEmpty()) 931 throw std::bad_alloc(); 932 933 // copy 934 FSTransaction::CreateOperation copyOperation(&fFSTransaction, 935 FSUtils::Entry(targetDirectory, tempTargetName)); 936 937 error = BCopyEngine(BCopyEngine::UNLINK_DESTINATION).CopyEntry( 938 FSUtils::Entry(sourceDirectory, relativeSourcePath.Leaf()), 939 FSUtils::Entry(targetDirectory, tempTargetName)); 940 if (error != B_OK) { 941 throw Exception(error, 942 BString().SetToFormat("failed to copy entry \"%s\"", 943 _GetPath( 944 FSUtils::Entry(sourceDirectory, 945 relativeSourcePath.Leaf()), 946 relativeSourcePath).String()), 947 package->FileName()); 948 } 949 950 copyOperation.Finished(); 951 952 // rename 953 FSTransaction::RemoveOperation renameOperation(&fFSTransaction, 954 FSUtils::Entry(targetDirectory, targetName), 955 FSUtils::Entry(fWritableFilesDirectory, 956 originalRelativeSourcePath)); 957 958 BEntry targetEntry; 959 error = targetEntry.SetTo(&targetDirectory, tempTargetName); 960 if (error == B_OK) 961 error = targetEntry.Rename(targetName, true); 962 if (error != B_OK) { 963 throw Exception(error, 964 BString().SetToFormat("failed to rename entry \"%s\" to \"%s\"", 965 _GetPath( 966 FSUtils::Entry(targetDirectory, tempTargetName), 967 tempTargetName).String(), 968 targetName), 969 package->FileName()); 970 } 971 972 renameOperation.Finished(); 973 copyOperation.Unregister(); 974 } 975 976 void _RevertAddPackagesToActivate() 977 { 978 if (fAddedPackages.empty()) 979 return; 980 981 // open transaction directory 982 BDirectory transactionDirectory; 983 status_t error = transactionDirectory.SetTo(&fTransactionDirectoryRef); 984 if (error != B_OK) { 985 ERROR("failed to open transaction directory: %s\n", 986 strerror(error)); 987 } 988 989 for (PackageSet::iterator it = fAddedPackages.begin(); 990 it != fAddedPackages.end(); ++it) { 991 // remove package from the volume 992 Package* package = *it; 993 994 if (fPackagesAlreadyAdded.find(package) 995 != fPackagesAlreadyAdded.end()) { 996 continue; 997 } 998 999 fVolume->_RemovePackage(package); 1000 1001 if (transactionDirectory.InitCheck() != B_OK) 1002 continue; 1003 1004 // get BEntry for the package 1005 NotOwningEntryRef entryRef(fVolume->fPackagesDirectoryRef, 1006 package->FileName()); 1007 1008 BEntry entry; 1009 error = entry.SetTo(&entryRef); 1010 if (error != B_OK) { 1011 ERROR("failed to get entry for package \"%s\": %s\n", 1012 package->FileName().String(), strerror(error)); 1013 continue; 1014 } 1015 1016 // move entry 1017 error = entry.MoveTo(&transactionDirectory); 1018 if (error != B_OK) { 1019 ERROR("failed to move new package \"%s\" back to transaction " 1020 "directory: %s\n", package->FileName().String(), 1021 strerror(error)); 1022 continue; 1023 } 1024 } 1025 } 1026 1027 void _RevertRemovePackagesToDeactivate() 1028 { 1029 if (fRemovedPackages.empty()) 1030 return; 1031 1032 // open packages directory 1033 BDirectory packagesDirectory; 1034 status_t error 1035 = packagesDirectory.SetTo(&fVolume->fPackagesDirectoryRef); 1036 if (error != B_OK) { 1037 throw Exception(error, "failed to open packages directory"); 1038 ERROR("failed to open packages directory: %s\n", 1039 strerror(error)); 1040 return; 1041 } 1042 1043 for (PackageSet::iterator it = fRemovedPackages.begin(); 1044 it != fRemovedPackages.end(); ++it) { 1045 Package* package = *it; 1046 if (fPackagesAlreadyRemoved.find(package) 1047 != fPackagesAlreadyRemoved.end()) { 1048 continue; 1049 } 1050 1051 // get a BEntry for the package 1052 BEntry entry; 1053 status_t error = entry.SetTo(&fOldStateDirectory, 1054 package->FileName()); 1055 if (error != B_OK) { 1056 ERROR("failed to get entry for package \"%s\": %s\n", 1057 package->FileName().String(), strerror(error)); 1058 continue; 1059 } 1060 1061 // move entry 1062 error = entry.MoveTo(&packagesDirectory); 1063 if (error != B_OK) { 1064 ERROR("failed to move old package \"%s\" back to packages " 1065 "directory: %s\n", package->FileName().String(), 1066 strerror(error)); 1067 continue; 1068 } 1069 } 1070 } 1071 1072 void _RevertUserGroupChanges() 1073 { 1074 // delete users 1075 for (StringSet::const_iterator it = fAddedUsers.begin(); 1076 it != fAddedUsers.end(); ++it) { 1077 std::string commandLine("userdel "); 1078 commandLine += FSUtils::ShellEscapeString(it->c_str()).String(); 1079 if (system(commandLine.c_str()) != 0) 1080 ERROR("failed to remove user \"%s\"\n", it->c_str()); 1081 } 1082 1083 // delete groups 1084 for (StringSet::const_iterator it = fAddedGroups.begin(); 1085 it != fAddedGroups.end(); ++it) { 1086 std::string commandLine("groupdel "); 1087 commandLine += FSUtils::ShellEscapeString(it->c_str()).String(); 1088 if (system(commandLine.c_str()) != 0) 1089 ERROR("failed to remove group \"%s\"\n", it->c_str()); 1090 } 1091 } 1092 1093 void _RunPostInstallScripts() 1094 { 1095 for (PackageSet::iterator it = fAddedPackages.begin(); 1096 it != fAddedPackages.end(); ++it) { 1097 Package* package = *it; 1098 const BStringList& scripts = package->Info().PostInstallScripts(); 1099 int32 count = scripts.CountStrings(); 1100 for (int32 i = 0; i < count; i++) 1101 _RunPostInstallScript(package, scripts.StringAt(i)); 1102 } 1103 } 1104 1105 void _RunPostInstallScript(Package* package, const BString& script) 1106 { 1107 BDirectory rootDir(&fVolume->fRootDirectoryRef); 1108 BPath scriptPath(&rootDir, script); 1109 status_t error = scriptPath.InitCheck(); 1110 if (error != B_OK) { 1111 ERROR("Volume::CommitTransactionHandler::_RunPostInstallScript(): " 1112 "failed get path of post-installation script \"%s\" of package " 1113 "%s: %s\n", script.String(), package->FileName().String(), 1114 strerror(error)); 1115 // TODO: Notify the user! 1116 return; 1117 } 1118 1119 if (system(scriptPath.Path()) != 0) { 1120 ERROR("Volume::CommitTransactionHandler::_RunPostInstallScript(): " 1121 "running post-installation script \"%s\" of package %s " 1122 "failed: %s\n", script.String(), package->FileName().String(), 1123 strerror(error)); 1124 // TODO: Notify the user! 1125 } 1126 } 1127 1128 static BString _GetPath(const FSUtils::Entry& entry, 1129 const BString& fallback) 1130 { 1131 BString path = entry.Path(); 1132 return path.IsEmpty() ? fallback : path; 1133 } 1134 1135 void _ExtractPackageContent(Package* package, 1136 const BStringList& contentPaths, BDirectory& targetDirectory, 1137 BDirectory& _extractedFilesDirectory) 1138 { 1139 // check whether the subdirectory already exists 1140 BString targetName(package->RevisionedNameThrows()); 1141 1142 BEntry targetEntry; 1143 status_t error = targetEntry.SetTo(&targetDirectory, targetName); 1144 if (error != B_OK) { 1145 throw Exception(error, 1146 BString().SetToFormat("failed to init entry \"%s\"", 1147 _GetPath( 1148 FSUtils::Entry(targetDirectory, targetName), 1149 targetName).String()), 1150 package->FileName()); 1151 } 1152 if (targetEntry.Exists()) { 1153 // nothing to do -- the very same version of the package has already 1154 // been extracted 1155 error = _extractedFilesDirectory.SetTo(&targetDirectory, 1156 targetName); 1157 if (error != B_OK) { 1158 throw Exception(error, 1159 BString().SetToFormat("failed to open directory \"%s\"", 1160 _GetPath( 1161 FSUtils::Entry(targetDirectory, targetName), 1162 targetName).String()), 1163 package->FileName()); 1164 } 1165 return; 1166 } 1167 1168 // create the subdirectory with a temporary name (remove, if it already 1169 // exists) 1170 BString temporaryTargetName = BString().SetToFormat("%s.tmp", 1171 targetName.String()); 1172 if (temporaryTargetName.IsEmpty()) 1173 throw std::bad_alloc(); 1174 1175 error = targetEntry.SetTo(&targetDirectory, temporaryTargetName); 1176 if (error != B_OK) { 1177 throw Exception(error, 1178 BString().SetToFormat("failed to init entry \"%s\"", 1179 _GetPath( 1180 FSUtils::Entry(targetDirectory, temporaryTargetName), 1181 temporaryTargetName).String()), 1182 package->FileName()); 1183 } 1184 1185 if (targetEntry.Exists()) { 1186 // remove pre-existing 1187 error = BRemoveEngine().RemoveEntry(FSUtils::Entry(targetEntry)); 1188 if (error != B_OK) { 1189 throw Exception(error, 1190 BString().SetToFormat("failed to remove directory \"%s\"", 1191 _GetPath( 1192 FSUtils::Entry(targetDirectory, 1193 temporaryTargetName), 1194 temporaryTargetName).String()), 1195 package->FileName()); 1196 } 1197 } 1198 1199 BDirectory& subDirectory = _extractedFilesDirectory; 1200 FSTransaction::CreateOperation createSubDirectoryOperation( 1201 &fFSTransaction, 1202 FSUtils::Entry(targetDirectory, temporaryTargetName)); 1203 error = targetDirectory.CreateDirectory(temporaryTargetName, 1204 &subDirectory); 1205 if (error != B_OK) { 1206 throw Exception(error, 1207 BString().SetToFormat("failed to create directory \"%s\"", 1208 _GetPath( 1209 FSUtils::Entry(targetDirectory, temporaryTargetName), 1210 temporaryTargetName).String()), 1211 package->FileName()); 1212 } 1213 1214 createSubDirectoryOperation.Finished(); 1215 1216 // extract 1217 NotOwningEntryRef packageRef(fVolume->fPackagesDirectoryRef, 1218 package->FileName()); 1219 1220 int32 contentPathCount = contentPaths.CountStrings(); 1221 for (int32 i = 0; i < contentPathCount; i++) { 1222 const char* contentPath = contentPaths.StringAt(i); 1223 1224 error = FSUtils::ExtractPackageContent(FSUtils::Entry(packageRef), 1225 contentPath, FSUtils::Entry(subDirectory)); 1226 if (error != B_OK) { 1227 throw Exception(error, 1228 BString().SetToFormat( 1229 "failed to extract \"%s\" from package", contentPath), 1230 package->FileName()); 1231 } 1232 } 1233 1234 // tag all entries with the package attribute 1235 error = _TagPackageEntriesRecursively(subDirectory, targetName, true); 1236 if (error != B_OK) { 1237 throw Exception(error, 1238 BString().SetToFormat("failed to tag extract files in \"%s\" " 1239 "with package attribute", 1240 _GetPath( 1241 FSUtils::Entry(targetDirectory, temporaryTargetName), 1242 temporaryTargetName).String()), 1243 package->FileName()); 1244 } 1245 1246 // rename the subdirectory 1247 error = targetEntry.Rename(targetName); 1248 if (error != B_OK) { 1249 throw Exception(error, 1250 BString().SetToFormat("failed to rename entry \"%s\" to \"%s\"", 1251 _GetPath( 1252 FSUtils::Entry(targetDirectory, temporaryTargetName), 1253 temporaryTargetName).String(), 1254 targetName.String()), 1255 package->FileName()); 1256 } 1257 1258 // keep the directory, regardless of whether the transaction is rolled 1259 // back 1260 createSubDirectoryOperation.Unregister(); 1261 } 1262 1263 static status_t _TagPackageEntriesRecursively(BDirectory& directory, 1264 const BString& value, bool nonDirectoriesOnly) 1265 { 1266 char buffer[sizeof(dirent) + B_FILE_NAME_LENGTH]; 1267 dirent *entry = (dirent*)buffer; 1268 while (directory.GetNextDirents(entry, sizeof(buffer), 1) == 1) { 1269 if (strcmp(entry->d_name, ".") == 0 1270 || strcmp(entry->d_name, "..") == 0) { 1271 continue; 1272 } 1273 1274 // determine type 1275 struct stat st; 1276 status_t error = directory.GetStatFor(entry->d_name, &st); 1277 if (error != B_OK) 1278 return error; 1279 bool isDirectory = S_ISDIR(st.st_mode); 1280 1281 // open the node and set the attribute 1282 BNode stackNode; 1283 BDirectory stackDirectory; 1284 BNode* node; 1285 if (isDirectory) { 1286 node = &stackDirectory; 1287 error = stackDirectory.SetTo(&directory, entry->d_name); 1288 } else { 1289 node = &stackNode; 1290 error = stackNode.SetTo(&directory, entry->d_name); 1291 } 1292 1293 if (error != B_OK) 1294 return error; 1295 1296 if (!isDirectory || !nonDirectoriesOnly) { 1297 error = node->WriteAttrString(kPackageFileAttribute, &value); 1298 if (error != B_OK) 1299 return error; 1300 } 1301 1302 // recurse 1303 if (isDirectory) { 1304 error = _TagPackageEntriesRecursively(stackDirectory, value, 1305 nonDirectoriesOnly); 1306 if (error != B_OK) 1307 return error; 1308 } 1309 } 1310 1311 return B_OK; 1312 } 1313 1314 private: 1315 Volume* fVolume; 1316 PackageList fPackagesToActivate; 1317 PackageSet fPackagesToDeactivate; 1318 PackageSet fAddedPackages; 1319 PackageSet fRemovedPackages; 1320 const PackageSet& fPackagesAlreadyAdded; 1321 const PackageSet& fPackagesAlreadyRemoved; 1322 BDirectory fOldStateDirectory; 1323 BString fOldStateDirectoryName; 1324 node_ref fTransactionDirectoryRef; 1325 BDirectory fWritableFilesDirectory; 1326 StringSet fAddedGroups; 1327 StringSet fAddedUsers; 1328 FSTransaction fFSTransaction; 1329 }; 1330 1331 1332 // #pragma mark - Volume 1333 1334 1335 Volume::Volume(BLooper* looper) 1336 : 1337 BHandler(), 1338 fPath(), 1339 fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM), 1340 fRootDirectoryRef(), 1341 fPackagesDirectoryRef(), 1342 fRoot(NULL), 1343 fListener(NULL), 1344 fPackagesByFileName(), 1345 fPackagesByNodeRef(), 1346 fPendingNodeMonitorEventsLock("pending node monitor events"), 1347 fPendingNodeMonitorEvents(), 1348 fNodeMonitorEventHandleTime(0), 1349 fPackagesToBeActivated(), 1350 fPackagesToBeDeactivated(), 1351 fChangeCount(0), 1352 fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY) 1353 { 1354 looper->AddHandler(this); 1355 } 1356 1357 1358 Volume::~Volume() 1359 { 1360 Unmounted(); 1361 // need for error case in InitPackages() 1362 1363 fPackagesByFileName.Clear(); 1364 1365 Package* package = fPackagesByNodeRef.Clear(true); 1366 while (package != NULL) { 1367 Package* next = package->NodeRefHashTableNext(); 1368 delete package; 1369 package = next; 1370 } 1371 } 1372 1373 1374 status_t 1375 Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef) 1376 { 1377 if (fPackagesByFileName.Init() != B_OK || fPackagesByNodeRef.Init() != B_OK) 1378 RETURN_ERROR(B_NO_MEMORY); 1379 1380 fRootDirectoryRef = rootDirectoryRef; 1381 1382 // open the root directory 1383 BDirectory directory; 1384 status_t error = directory.SetTo(&fRootDirectoryRef); 1385 if (error != B_OK) { 1386 ERROR("Volume::Init(): failed to open root directory: %s\n", 1387 strerror(error)); 1388 RETURN_ERROR(error); 1389 } 1390 1391 // get the directory path 1392 BEntry entry; 1393 error = directory.GetEntry(&entry); 1394 1395 BPath path; 1396 if (error == B_OK) 1397 error = entry.GetPath(&path); 1398 1399 if (error != B_OK) { 1400 ERROR("Volume::Init(): failed to get root directory path: %s\n", 1401 strerror(error)); 1402 RETURN_ERROR(error); 1403 } 1404 1405 fPath = path.Path(); 1406 if (fPath.IsEmpty()) 1407 RETURN_ERROR(B_NO_MEMORY); 1408 1409 // get a volume info from the FS 1410 int fd = directory.Dup(); 1411 if (fd < 0) { 1412 ERROR("Volume::Init(): failed to get root directory FD: %s\n", 1413 strerror(fd)); 1414 RETURN_ERROR(fd); 1415 } 1416 FileDescriptorCloser fdCloser(fd); 1417 1418 PackageFSVolumeInfo info; 1419 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_VOLUME_INFO, &info, sizeof(info)) 1420 != 0) { 1421 ERROR("Volume::Init(): failed to get volume info: %s\n", 1422 strerror(errno)); 1423 RETURN_ERROR(errno); 1424 } 1425 1426 fMountType = info.mountType; 1427 fPackagesDirectoryRef.device = info.packagesDeviceID; 1428 fPackagesDirectoryRef.node = info.packagesDirectoryID; 1429 1430 _packageRootRef.device = info.rootDeviceID; 1431 _packageRootRef.node = info.rootDirectoryID; 1432 1433 return B_OK; 1434 } 1435 1436 1437 status_t 1438 Volume::InitPackages(Listener* listener) 1439 { 1440 // node-monitor the volume's packages directory 1441 status_t error = watch_node(&fPackagesDirectoryRef, B_WATCH_DIRECTORY, 1442 BMessenger(this)); 1443 if (error == B_OK) { 1444 fListener = listener; 1445 } else { 1446 ERROR("Volume::InitPackages(): failed to start watching the packages " 1447 "directory of the volume at \"%s\": %s\n", 1448 fPath.String(), strerror(error)); 1449 // Not good, but not fatal. Only the manual package operations in the 1450 // packages directory won't work correctly. 1451 } 1452 1453 // read the packages directory and get the active packages 1454 int fd = OpenRootDirectory(); 1455 if (fd < 0) { 1456 ERROR("Volume::InitPackages(): failed to open root directory: %s\n", 1457 strerror(fd)); 1458 RETURN_ERROR(fd); 1459 } 1460 FileDescriptorCloser fdCloser(fd); 1461 1462 error = _ReadPackagesDirectory(); 1463 if (error != B_OK) 1464 RETURN_ERROR(error); 1465 1466 error = _GetActivePackages(fd); 1467 if (error != B_OK) 1468 RETURN_ERROR(error); 1469 1470 // create the admin directory, if it doesn't exist yet 1471 BDirectory packagesDirectory; 1472 if (packagesDirectory.SetTo(&fPackagesDirectoryRef) == B_OK) { 1473 if (!BEntry(&packagesDirectory, kAdminDirectoryName).Exists()) 1474 packagesDirectory.CreateDirectory(kAdminDirectoryName, NULL); 1475 } 1476 1477 return B_OK; 1478 } 1479 1480 1481 status_t 1482 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly) 1483 { 1484 for (PackageFileNameHashTable::Iterator it 1485 = fPackagesByFileName.GetIterator(); it.HasNext();) { 1486 Package* package = it.Next(); 1487 if (activeOnly && !package->IsActive()) 1488 continue; 1489 1490 status_t error = repository.AddPackage(package->Info()); 1491 if (error != B_OK) { 1492 ERROR("Volume::AddPackagesToRepository(): failed to add package %s " 1493 "to repository: %s\n", package->FileName().String(), 1494 strerror(error)); 1495 return error; 1496 } 1497 } 1498 1499 return B_OK; 1500 } 1501 1502 1503 void 1504 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume) 1505 { 1506 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume); 1507 // create the solver 1508 BSolver* solver; 1509 status_t error = BSolver::Create(solver); 1510 if (error != B_OK) { 1511 ERROR("Volume::InitialVerify(): failed to create solver: %s\n", 1512 strerror(error)); 1513 return; 1514 } 1515 ObjectDeleter<BSolver> solverDeleter(solver); 1516 1517 // add a repository with all active packages 1518 BSolverRepository repository; 1519 error = _AddRepository(solver, repository, true, true); 1520 if (error != B_OK) { 1521 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 1522 strerror(error)); 1523 return; 1524 } 1525 1526 // add a repository for the next volume 1527 BSolverRepository nextRepository; 1528 if (nextVolume != NULL) { 1529 nextRepository.SetPriority(1); 1530 error = nextVolume->_AddRepository(solver, nextRepository, true, false); 1531 if (error != B_OK) { 1532 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 1533 strerror(error)); 1534 return; 1535 } 1536 } 1537 1538 // add a repository for the next next volume 1539 BSolverRepository nextNextRepository; 1540 if (nextNextVolume != NULL) { 1541 nextNextRepository.SetPriority(2); 1542 error = nextNextVolume->_AddRepository(solver, nextNextRepository, true, 1543 false); 1544 if (error != B_OK) { 1545 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 1546 strerror(error)); 1547 return; 1548 } 1549 } 1550 1551 // verify 1552 error = solver->VerifyInstallation(); 1553 if (error != B_OK) { 1554 ERROR("Volume::InitialVerify(): failed to verify: %s\n", 1555 strerror(error)); 1556 return; 1557 } 1558 1559 if (!solver->HasProblems()) { 1560 INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n", 1561 Path().String()); 1562 return; 1563 } 1564 1565 // print the problems 1566 // TODO: Notify the user ... 1567 INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n", 1568 Path().String()); 1569 1570 int32 problemCount = solver->CountProblems(); 1571 for (int32 i = 0; i < problemCount; i++) { 1572 BSolverProblem* problem = solver->ProblemAt(i); 1573 INFORM(" %" B_PRId32 ": %s\n", i + 1, problem->ToString().String()); 1574 int32 solutionCount = problem->CountSolutions(); 1575 for (int32 k = 0; k < solutionCount; k++) { 1576 const BSolverProblemSolution* solution = problem->SolutionAt(k); 1577 INFORM(" solution %" B_PRId32 ":\n", k + 1); 1578 int32 elementCount = solution->CountElements(); 1579 for (int32 l = 0; l < elementCount; l++) { 1580 const BSolverProblemSolutionElement* element 1581 = solution->ElementAt(l); 1582 INFORM(" - %s\n", element->ToString().String()); 1583 } 1584 } 1585 } 1586 } 1587 1588 1589 void 1590 Volume::HandleGetLocationInfoRequest(BMessage* message) 1591 { 1592 // If the cached reply message is up-to-date, just send it. 1593 int64 changeCount; 1594 if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK 1595 && changeCount == fChangeCount) { 1596 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 1597 kCommunicationTimeout); 1598 return; 1599 } 1600 1601 // rebuild the reply message 1602 fLocationInfoReply.MakeEmpty(); 1603 1604 if (fLocationInfoReply.AddInt32("base directory device", 1605 fRootDirectoryRef.device) != B_OK 1606 || fLocationInfoReply.AddInt64("base directory node", 1607 fRootDirectoryRef.node) != B_OK 1608 || fLocationInfoReply.AddInt32("packages directory device", 1609 fPackagesDirectoryRef.device) != B_OK 1610 || fLocationInfoReply.AddInt64("packages directory node", 1611 fPackagesDirectoryRef.node) != B_OK) { 1612 return; 1613 } 1614 1615 for (PackageFileNameHashTable::Iterator it 1616 = fPackagesByFileName.GetIterator(); it.HasNext();) { 1617 Package* package = it.Next(); 1618 const char* fieldName = package->IsActive() 1619 ? "active packages" : "inactive packages"; 1620 BMessage packageArchive; 1621 if (package->Info().Archive(&packageArchive) != B_OK 1622 || fLocationInfoReply.AddMessage(fieldName, &packageArchive) 1623 != B_OK) { 1624 return; 1625 } 1626 } 1627 1628 if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK) 1629 return; 1630 1631 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 1632 kCommunicationTimeout); 1633 } 1634 1635 1636 void 1637 Volume::HandleCommitTransactionRequest(BMessage* message) 1638 { 1639 // Prepare the reply in so far that we can at least set the error code 1640 // without risk of failure. 1641 BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); 1642 if (reply.AddInt32("error", B_ERROR) != B_OK) 1643 return; 1644 1645 // perform the request 1646 PackageSet dummy; 1647 CommitTransactionHandler handler(this, dummy, dummy); 1648 int32 error; 1649 try { 1650 handler.HandleRequest(message, &reply); 1651 error = B_DAEMON_OK; 1652 } catch (Exception& exception) { 1653 error = exception.Error(); 1654 1655 if (!exception.ErrorMessage().IsEmpty()) 1656 reply.AddString("error message", exception.ErrorMessage()); 1657 if (!exception.PackageName().IsEmpty()) 1658 reply.AddString("error package", exception.PackageName()); 1659 } catch (std::bad_alloc& exception) { 1660 error = B_NO_MEMORY; 1661 } 1662 1663 // revert on error 1664 if (error != B_DAEMON_OK) 1665 handler.Revert(); 1666 1667 // send the reply 1668 reply.ReplaceInt32("error", error); 1669 message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout); 1670 } 1671 1672 1673 void 1674 Volume::Unmounted() 1675 { 1676 if (fListener != NULL) { 1677 stop_watching(BMessenger(this)); 1678 fListener = NULL; 1679 } 1680 1681 if (BLooper* looper = Looper()) 1682 looper->RemoveHandler(this); 1683 } 1684 1685 1686 void 1687 Volume::MessageReceived(BMessage* message) 1688 { 1689 switch (message->what) { 1690 case B_NODE_MONITOR: 1691 { 1692 int32 opcode; 1693 if (message->FindInt32("opcode", &opcode) != B_OK) 1694 break; 1695 1696 switch (opcode) { 1697 case B_ENTRY_CREATED: 1698 _HandleEntryCreatedOrRemoved(message, true); 1699 break; 1700 case B_ENTRY_REMOVED: 1701 _HandleEntryCreatedOrRemoved(message, false); 1702 break; 1703 case B_ENTRY_MOVED: 1704 _HandleEntryMoved(message); 1705 break; 1706 default: 1707 break; 1708 } 1709 break; 1710 } 1711 1712 case kHandleNodeMonitorEvents: 1713 if (fListener != NULL) { 1714 if (system_time() >= fNodeMonitorEventHandleTime) 1715 fListener->VolumeNodeMonitorEventOccurred(this); 1716 } 1717 break; 1718 1719 default: 1720 BHandler::MessageReceived(message); 1721 break; 1722 } 1723 } 1724 1725 1726 BPackageInstallationLocation 1727 Volume::Location() const 1728 { 1729 switch (fMountType) { 1730 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 1731 return B_PACKAGE_INSTALLATION_LOCATION_SYSTEM; 1732 case PACKAGE_FS_MOUNT_TYPE_HOME: 1733 return B_PACKAGE_INSTALLATION_LOCATION_HOME; 1734 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 1735 default: 1736 return B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT; 1737 } 1738 } 1739 1740 1741 int 1742 Volume::OpenRootDirectory() const 1743 { 1744 BDirectory directory; 1745 status_t error = directory.SetTo(&fRootDirectoryRef); 1746 if (error != B_OK) { 1747 ERROR("Volume::OpenRootDirectory(): failed to open root directory: " 1748 "%s\n", strerror(error)); 1749 RETURN_ERROR(error); 1750 } 1751 1752 return directory.Dup(); 1753 } 1754 1755 1756 void 1757 Volume::ProcessPendingNodeMonitorEvents() 1758 { 1759 // get the events 1760 NodeMonitorEventList events; 1761 { 1762 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 1763 events.MoveFrom(&fPendingNodeMonitorEvents); 1764 } 1765 1766 // process them 1767 while (NodeMonitorEvent* event = events.RemoveHead()) { 1768 ObjectDeleter<NodeMonitorEvent> eventDeleter(event); 1769 if (event->WasCreated()) 1770 _PackagesEntryCreated(event->EntryName()); 1771 else 1772 _PackagesEntryRemoved(event->EntryName()); 1773 } 1774 } 1775 1776 1777 bool 1778 Volume::HasPendingPackageActivationChanges() const 1779 { 1780 return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty(); 1781 } 1782 1783 1784 void 1785 Volume::ProcessPendingPackageActivationChanges() 1786 { 1787 if (!HasPendingPackageActivationChanges()) 1788 return; 1789 1790 // perform the request 1791 CommitTransactionHandler handler(this, fPackagesToBeActivated, 1792 fPackagesToBeDeactivated); 1793 int32 error; 1794 try { 1795 handler.HandleRequest(fPackagesToBeActivated, fPackagesToBeDeactivated); 1796 error = B_DAEMON_OK; 1797 } catch (Exception& exception) { 1798 error = exception.Error(); 1799 ERROR("Volume::ProcessPendingPackageActivationChanges(): package " 1800 "activation failed: %s\n", exception.ToString().String()); 1801 // TODO: Notify the user! 1802 } catch (std::bad_alloc& exception) { 1803 error = B_NO_MEMORY; 1804 ERROR("Volume::ProcessPendingPackageActivationChanges(): package " 1805 "activation failed: out of memory\n"); 1806 // TODO: Notify the user! 1807 } 1808 1809 // revert on error 1810 if (error != B_DAEMON_OK) 1811 handler.Revert(); 1812 1813 // clear the activation/deactivation sets in any event 1814 fPackagesToBeActivated.clear(); 1815 fPackagesToBeDeactivated.clear(); 1816 } 1817 1818 1819 void 1820 Volume::ClearPackageActivationChanges() 1821 { 1822 fPackagesToBeActivated.clear(); 1823 fPackagesToBeDeactivated.clear(); 1824 } 1825 1826 1827 status_t 1828 Volume::CreateTransaction(BPackageInstallationLocation location, 1829 BActivationTransaction& _transaction, BDirectory& _transactionDirectory) 1830 { 1831 // open admin directory 1832 BDirectory adminDirectory; 1833 status_t error = _OpenPackagesSubDirectory( 1834 RelativePath(kAdminDirectoryName), true, adminDirectory); 1835 if (error != B_OK) 1836 return error; 1837 1838 // create a transaction directory 1839 int uniqueId = 1; 1840 BString directoryName; 1841 for (;; uniqueId++) { 1842 directoryName.SetToFormat("transaction-%d", uniqueId); 1843 if (directoryName.IsEmpty()) 1844 return B_NO_MEMORY; 1845 1846 error = adminDirectory.CreateDirectory(directoryName, 1847 &_transactionDirectory); 1848 if (error == B_OK) 1849 break; 1850 if (error != B_FILE_EXISTS) 1851 return error; 1852 } 1853 1854 // init the transaction 1855 error = _transaction.SetTo(location, fChangeCount, directoryName); 1856 if (error != B_OK) { 1857 BEntry entry; 1858 _transactionDirectory.GetEntry(&entry); 1859 _transactionDirectory.Unset(); 1860 if (entry.InitCheck() == B_OK) 1861 entry.Remove(); 1862 return error; 1863 } 1864 1865 return B_OK; 1866 } 1867 1868 1869 void 1870 Volume::CommitTransaction(const BActivationTransaction& transaction, 1871 const PackageSet& packagesAlreadyAdded, 1872 const PackageSet& packagesAlreadyRemoved, 1873 BDaemonClient::BCommitTransactionResult& _result) 1874 { 1875 // perform the request 1876 CommitTransactionHandler handler(this, packagesAlreadyAdded, 1877 packagesAlreadyRemoved); 1878 int32 error; 1879 try { 1880 handler.HandleRequest(transaction, NULL); 1881 error = B_DAEMON_OK; 1882 _result.SetTo(error, BString(), BString(), 1883 handler.OldStateDirectoryName()); 1884 } catch (Exception& exception) { 1885 error = exception.Error(); 1886 _result.SetTo(error, exception.ErrorMessage(), exception.PackageName(), 1887 BString()); 1888 } catch (std::bad_alloc& exception) { 1889 error = B_NO_MEMORY; 1890 _result.SetTo(error, BString(), BString(), BString()); 1891 } 1892 1893 // revert on error 1894 if (error != B_DAEMON_OK) 1895 handler.Revert(); 1896 } 1897 1898 1899 void 1900 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created) 1901 { 1902 // only moves to or from our packages directory are interesting 1903 int32 deviceID; 1904 int64 directoryID; 1905 const char* name; 1906 if (message->FindInt32("device", &deviceID) != B_OK 1907 || message->FindInt64("directory", &directoryID) != B_OK 1908 || message->FindString("name", &name) != B_OK 1909 || node_ref(deviceID, directoryID) != fPackagesDirectoryRef) { 1910 return; 1911 } 1912 1913 _QueueNodeMonitorEvent(name, created); 1914 } 1915 1916 1917 void 1918 Volume::_HandleEntryMoved(const BMessage* message) 1919 { 1920 int32 deviceID; 1921 int64 fromDirectoryID; 1922 int64 toDirectoryID; 1923 const char* fromName; 1924 const char* toName; 1925 if (message->FindInt32("device", &deviceID) != B_OK 1926 || message->FindInt64("from directory", &fromDirectoryID) != B_OK 1927 || message->FindInt64("to directory", &toDirectoryID) != B_OK 1928 || message->FindString("from name", &fromName) != B_OK 1929 || message->FindString("name", &toName) != B_OK 1930 || deviceID != fPackagesDirectoryRef.device 1931 || (fromDirectoryID != fPackagesDirectoryRef.node 1932 && toDirectoryID != fPackagesDirectoryRef.node)) { 1933 return; 1934 } 1935 1936 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 1937 // make sure for a move the two events cannot get split 1938 1939 if (fromDirectoryID == fPackagesDirectoryRef.node) 1940 _QueueNodeMonitorEvent(fromName, false); 1941 if (toDirectoryID == fPackagesDirectoryRef.node) 1942 _QueueNodeMonitorEvent(toName, true); 1943 } 1944 1945 1946 void 1947 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated) 1948 { 1949 if (name.IsEmpty()) { 1950 ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n"); 1951 return; 1952 } 1953 1954 // ignore entries that don't have the ".hpkg" extension 1955 if (!name.EndsWith(kPackageFileNameExtension)) 1956 return; 1957 1958 NodeMonitorEvent* event 1959 = new(std::nothrow) NodeMonitorEvent(name, wasCreated); 1960 if (event == NULL) { 1961 ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n"); 1962 return; 1963 } 1964 1965 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 1966 fPendingNodeMonitorEvents.Add(event); 1967 eventsLock.Unlock(); 1968 1969 fNodeMonitorEventHandleTime 1970 = system_time() + kNodeMonitorEventHandlingDelay; 1971 BMessage message(kHandleNodeMonitorEvents); 1972 BMessageRunner::StartSending(this, &message, kNodeMonitorEventHandlingDelay, 1973 1); 1974 } 1975 1976 1977 void 1978 Volume::_PackagesEntryCreated(const char* name) 1979 { 1980 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name); 1981 // Ignore the event, if the package is already known. 1982 Package* package = fPackagesByFileName.Lookup(name); 1983 if (package != NULL) { 1984 if (package->EntryCreatedIgnoreLevel() > 0) { 1985 package->DecrementEntryCreatedIgnoreLevel(); 1986 } else { 1987 WARN("node monitoring created event for already known entry " 1988 "\"%s\"\n", name); 1989 } 1990 1991 return; 1992 } 1993 1994 entry_ref entry; 1995 entry.device = fPackagesDirectoryRef.device; 1996 entry.directory = fPackagesDirectoryRef.node; 1997 status_t error = entry.set_name(name); 1998 if (error != B_OK) { 1999 ERROR("out of memory\n"); 2000 return; 2001 } 2002 2003 package = new(std::nothrow) Package; 2004 if (package == NULL) { 2005 ERROR("out of memory\n"); 2006 return; 2007 } 2008 ObjectDeleter<Package> packageDeleter(package); 2009 2010 error = package->Init(entry); 2011 if (error != B_OK) { 2012 ERROR("failed to init package for file \"%s\"\n", name); 2013 return; 2014 } 2015 2016 _AddPackage(package); 2017 packageDeleter.Detach(); 2018 2019 try { 2020 fPackagesToBeActivated.insert(package); 2021 } catch (std::bad_alloc& exception) { 2022 ERROR("out of memory\n"); 2023 return; 2024 } 2025 } 2026 2027 2028 void 2029 Volume::_PackagesEntryRemoved(const char* name) 2030 { 2031 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name); 2032 Package* package = fPackagesByFileName.Lookup(name); 2033 if (package == NULL) 2034 return; 2035 2036 // Ignore the event, if we generated it ourselves. 2037 if (package->EntryRemovedIgnoreLevel() > 0) { 2038 package->DecrementEntryRemovedIgnoreLevel(); 2039 return; 2040 } 2041 2042 // Remove the package from the packages-to-be-activated set, if it is in 2043 // there (unlikely, unless we see a create-remove-create sequence). 2044 PackageSet::iterator it = fPackagesToBeActivated.find(package); 2045 if (it != fPackagesToBeActivated.end()) 2046 fPackagesToBeActivated.erase(it); 2047 2048 // If the package isn't active, just remove it for good. 2049 if (!package->IsActive()) { 2050 _RemovePackage(package); 2051 delete package; 2052 return; 2053 } 2054 2055 // The package must be deactivated. 2056 try { 2057 fPackagesToBeDeactivated.insert(package); 2058 } catch (std::bad_alloc& exception) { 2059 ERROR("out of memory\n"); 2060 return; 2061 } 2062 } 2063 2064 2065 void 2066 Volume::_FillInActivationChangeItem(PackageFSActivationChangeItem* item, 2067 PackageFSActivationChangeType type, Package* package, char*& nameBuffer) 2068 { 2069 item->type = type; 2070 item->packageDeviceID = package->NodeRef().device; 2071 item->packageNodeID = package->NodeRef().node; 2072 item->nameLength = package->FileName().Length(); 2073 item->parentDeviceID = fPackagesDirectoryRef.device; 2074 item->parentDirectoryID = fPackagesDirectoryRef.node; 2075 item->name = nameBuffer; 2076 strcpy(nameBuffer, package->FileName()); 2077 nameBuffer += package->FileName().Length() + 1; 2078 } 2079 2080 2081 void 2082 Volume::_AddPackage(Package* package) 2083 { 2084 fPackagesByFileName.Insert(package); 2085 fPackagesByNodeRef.Insert(package); 2086 } 2087 2088 void 2089 Volume::_RemovePackage(Package* package) 2090 { 2091 fPackagesByFileName.Remove(package); 2092 fPackagesByNodeRef.Remove(package); 2093 fChangeCount++; 2094 } 2095 2096 2097 status_t 2098 Volume::_ReadPackagesDirectory() 2099 { 2100 BDirectory directory; 2101 status_t error = directory.SetTo(&fPackagesDirectoryRef); 2102 if (error != B_OK) { 2103 ERROR("Volume::_ReadPackagesDirectory(): failed to open packages " 2104 "directory: %s\n", strerror(error)); 2105 RETURN_ERROR(error); 2106 } 2107 2108 entry_ref entry; 2109 while (directory.GetNextRef(&entry) == B_OK) { 2110 if (!BString(entry.name).EndsWith(kPackageFileNameExtension)) 2111 continue; 2112 2113 Package* package = new(std::nothrow) Package; 2114 if (package == NULL) 2115 RETURN_ERROR(B_NO_MEMORY); 2116 ObjectDeleter<Package> packageDeleter(package); 2117 2118 status_t error = package->Init(entry); 2119 if (error == B_OK) { 2120 _AddPackage(package); 2121 packageDeleter.Detach(); 2122 } 2123 } 2124 2125 return B_OK; 2126 } 2127 2128 2129 status_t 2130 Volume::_GetActivePackages(int fd) 2131 { 2132 uint32 maxPackageCount = 16 * 1024; 2133 PackageFSGetPackageInfosRequest* request = NULL; 2134 MemoryDeleter requestDeleter; 2135 size_t bufferSize; 2136 for (;;) { 2137 bufferSize = sizeof(PackageFSGetPackageInfosRequest) 2138 + (maxPackageCount - 1) * sizeof(PackageFSPackageInfo); 2139 request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize); 2140 if (request == NULL) 2141 RETURN_ERROR(B_NO_MEMORY); 2142 requestDeleter.SetTo(request); 2143 2144 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request, 2145 bufferSize) != 0) { 2146 ERROR("Volume::_GetActivePackages(): failed to get active package " 2147 "info from package FS: %s\n", strerror(errno)); 2148 RETURN_ERROR(errno); 2149 } 2150 2151 if (request->packageCount <= maxPackageCount) 2152 break; 2153 2154 maxPackageCount = request->packageCount; 2155 requestDeleter.Unset(); 2156 } 2157 2158 // mark the returned packages active 2159 for (uint32 i = 0; i < request->packageCount; i++) { 2160 Package* package = fPackagesByNodeRef.Lookup( 2161 node_ref(request->infos[i].packageDeviceID, 2162 request->infos[i].packageNodeID)); 2163 if (package == NULL) { 2164 WARN("active package (dev: %" B_PRIdDEV ", node: %" B_PRIdINO ") " 2165 "not found in package directory\n", 2166 request->infos[i].packageDeviceID, 2167 request->infos[i].packageNodeID); 2168 // TODO: Deactivate the package right away? 2169 continue; 2170 } 2171 2172 package->SetActive(true); 2173 INFORM("active package: \"%s\"\n", package->FileName().String()); 2174 } 2175 2176 for (PackageNodeRefHashTable::Iterator it = fPackagesByNodeRef.GetIterator(); 2177 it.HasNext();) { 2178 Package* package = it.Next(); 2179 if (!package->IsActive()) 2180 INFORM("inactive package: \"%s\"\n", package->FileName().String()); 2181 } 2182 2183 PackageNodeRefHashTable fPackagesByNodeRef; 2184 // INFORM("%" B_PRIu32 " active packages:\n", request->packageCount); 2185 // for (uint32 i = 0; i < request->packageCount; i++) { 2186 // INFORM(" dev: %" B_PRIdDEV ", node: %" B_PRIdINO "\n", 2187 // request->infos[i].packageDeviceID, request->infos[i].packageNodeID); 2188 // } 2189 2190 return B_OK; 2191 } 2192 2193 2194 status_t 2195 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository, 2196 bool activeOnly, bool installed) 2197 { 2198 status_t error = repository.SetTo(Path()); 2199 if (error != B_OK) { 2200 ERROR("Volume::_AddRepository(): failed to init repository: %s\n", 2201 strerror(error)); 2202 return error; 2203 } 2204 2205 repository.SetInstalled(installed); 2206 2207 error = AddPackagesToRepository(repository, true); 2208 if (error != B_OK) { 2209 ERROR("Volume::_AddRepository(): failed to add packages to " 2210 "repository: %s\n", strerror(error)); 2211 return error; 2212 } 2213 2214 error = solver->AddRepository(&repository); 2215 if (error != B_OK) { 2216 ERROR("Volume::_AddRepository(): failed to add repository to solver: " 2217 "%s\n", strerror(error)); 2218 return error; 2219 } 2220 2221 return B_OK; 2222 } 2223 2224 2225 status_t 2226 Volume::_OpenPackagesFile(const RelativePath& subDirectoryPath, 2227 const char* fileName, uint32 openMode, BFile& _file, BEntry* _entry) 2228 { 2229 BDirectory directory; 2230 if (!subDirectoryPath.IsEmpty()) { 2231 status_t error = _OpenPackagesSubDirectory(subDirectoryPath, 2232 (openMode & B_CREATE_FILE) != 0, directory); 2233 if (error != B_OK) { 2234 ERROR("Volume::_OpenPackagesFile(): failed to open packages " 2235 "subdirectory \"%s\": %s\n", 2236 subDirectoryPath.ToString().String(), strerror(error)); 2237 RETURN_ERROR(error); 2238 } 2239 } else { 2240 status_t error = directory.SetTo(&fPackagesDirectoryRef); 2241 if (error != B_OK) { 2242 ERROR("Volume::_OpenPackagesFile(): failed to open packages " 2243 "directory: %s\n", strerror(error)); 2244 RETURN_ERROR(error); 2245 } 2246 } 2247 2248 BEntry stackEntry; 2249 BEntry& entry = _entry != NULL ? *_entry : stackEntry; 2250 status_t error = entry.SetTo(&directory, fileName); 2251 if (error != B_OK) { 2252 ERROR("Volume::_OpenPackagesFile(): failed to get entry for file: %s", 2253 strerror(error)); 2254 RETURN_ERROR(error); 2255 } 2256 2257 return _file.SetTo(&entry, openMode); 2258 } 2259 2260 2261 status_t 2262 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create, 2263 BDirectory& _directory) 2264 { 2265 // open the packages directory 2266 BDirectory directory; 2267 status_t error = directory.SetTo(&fPackagesDirectoryRef); 2268 if (error != B_OK) { 2269 ERROR("Volume::_OpenPackagesSubDirectory(): failed to open packages " 2270 "directory: %s\n", strerror(error)); 2271 RETURN_ERROR(error); 2272 } 2273 2274 return FSUtils::OpenSubDirectory(directory, path, create, _directory); 2275 } 2276 2277 2278 status_t 2279 Volume::_CreateActivationFileContent(const PackageSet& toActivate, 2280 const PackageSet& toDeactivate, BString& _content) 2281 { 2282 BString activationFileContent; 2283 for (PackageFileNameHashTable::Iterator it 2284 = fPackagesByFileName.GetIterator(); it.HasNext();) { 2285 Package* package = it.Next(); 2286 if (package->IsActive() 2287 && toDeactivate.find(package) == toDeactivate.end()) { 2288 int32 length = activationFileContent.Length(); 2289 activationFileContent << package->FileName() << '\n'; 2290 if (activationFileContent.Length() 2291 < length + package->FileName().Length() + 1) { 2292 return B_NO_MEMORY; 2293 } 2294 } 2295 } 2296 2297 for (PackageSet::const_iterator it = toActivate.begin(); 2298 it != toActivate.end(); ++it) { 2299 Package* package = *it; 2300 int32 length = activationFileContent.Length(); 2301 activationFileContent << package->FileName() << '\n'; 2302 if (activationFileContent.Length() 2303 < length + package->FileName().Length() + 1) { 2304 return B_NO_MEMORY; 2305 } 2306 } 2307 2308 _content = activationFileContent; 2309 return B_OK; 2310 } 2311 2312 2313 status_t 2314 Volume::_WriteActivationFile(const RelativePath& directoryPath, 2315 const char* fileName, const PackageSet& toActivate, 2316 const PackageSet& toDeactivate, 2317 BEntry& _entry) 2318 { 2319 // create the content 2320 BString activationFileContent; 2321 status_t error = _CreateActivationFileContent(toActivate, toDeactivate, 2322 activationFileContent); 2323 if (error != B_OK) 2324 return error; 2325 2326 // write the file 2327 error = _WriteTextFile(directoryPath, fileName, activationFileContent, 2328 _entry); 2329 if (error != B_OK) { 2330 ERROR("Volume::_WriteActivationFile(): failed to write activation " 2331 "file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName, 2332 strerror(error)); 2333 return error; 2334 } 2335 2336 return B_OK; 2337 } 2338 2339 2340 status_t 2341 Volume::_WriteTextFile(const RelativePath& directoryPath, const char* fileName, 2342 const BString& content, BEntry& _entry) 2343 { 2344 BFile file; 2345 status_t error = _OpenPackagesFile(directoryPath, 2346 fileName, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE, file, &_entry); 2347 if (error != B_OK) { 2348 ERROR("Volume::_WriteTextFile(): failed to create file \"%s/%s\": %s\n", 2349 directoryPath.ToString().String(), fileName, strerror(error)); 2350 return error; 2351 } 2352 2353 ssize_t bytesWritten = file.Write(content.String(), 2354 content.Length()); 2355 if (bytesWritten < 0) { 2356 ERROR("Volume::_WriteTextFile(): failed to write file \"%s/%s\": %s\n", 2357 directoryPath.ToString().String(), fileName, 2358 strerror(bytesWritten)); 2359 return bytesWritten; 2360 } 2361 2362 return B_OK; 2363 } 2364 2365 2366 void 2367 Volume::_ChangePackageActivation(const PackageSet& packagesToActivate, 2368 const PackageSet& packagesToDeactivate) 2369 { 2370 INFORM("Volume::_ChangePackageActivation(): activating %zu, deactivating %zu packages\n", 2371 packagesToActivate.size(), packagesToDeactivate.size()); 2372 2373 // write the temporary package activation file 2374 BEntry activationFileEntry; 2375 status_t error = _WriteActivationFile(RelativePath(kAdminDirectoryName), 2376 kTemporaryActivationFileName, packagesToActivate, packagesToDeactivate, 2377 activationFileEntry); 2378 if (error != B_OK) 2379 throw Exception(error, "failed to write activation file"); 2380 2381 // compute the size of the allocation we need for the activation change 2382 // request 2383 int32 itemCount = packagesToActivate.size() + packagesToDeactivate.size(); 2384 size_t requestSize = sizeof(PackageFSActivationChangeRequest) 2385 + itemCount * sizeof(PackageFSActivationChangeItem); 2386 2387 for (PackageSet::iterator it = packagesToActivate.begin(); 2388 it != packagesToActivate.end(); ++it) { 2389 requestSize += (*it)->FileName().Length() + 1; 2390 } 2391 2392 for (PackageSet::iterator it = packagesToDeactivate.begin(); 2393 it != packagesToDeactivate.end(); ++it) { 2394 requestSize += (*it)->FileName().Length() + 1; 2395 } 2396 2397 // allocate and prepare the request 2398 PackageFSActivationChangeRequest* request 2399 = (PackageFSActivationChangeRequest*)malloc(requestSize); 2400 if (request == NULL) 2401 throw Exception(B_NO_MEMORY); 2402 MemoryDeleter requestDeleter(request); 2403 2404 request->itemCount = itemCount; 2405 2406 PackageFSActivationChangeItem* item = &request->items[0]; 2407 char* nameBuffer = (char*)(item + itemCount); 2408 2409 for (PackageSet::iterator it = packagesToActivate.begin(); 2410 it != packagesToActivate.end(); ++it, item++) { 2411 _FillInActivationChangeItem(item, PACKAGE_FS_ACTIVATE_PACKAGE, *it, 2412 nameBuffer); 2413 } 2414 2415 for (PackageSet::iterator it = packagesToDeactivate.begin(); 2416 it != packagesToDeactivate.end(); ++it, item++) { 2417 _FillInActivationChangeItem(item, PACKAGE_FS_DEACTIVATE_PACKAGE, *it, 2418 nameBuffer); 2419 } 2420 2421 // issue the request 2422 int fd = OpenRootDirectory(); 2423 if (fd < 0) 2424 throw Exception(fd, "failed to open root directory"); 2425 FileDescriptorCloser fdCloser(fd); 2426 2427 if (ioctl(fd, PACKAGE_FS_OPERATION_CHANGE_ACTIVATION, request, requestSize) 2428 != 0) { 2429 // TODO: We need more error information and error handling! 2430 throw Exception(errno, "ioctl() to de-/activate packages failed"); 2431 } 2432 2433 // rename the temporary activation file to the final file 2434 error = activationFileEntry.Rename(kActivationFileName, true); 2435 if (error != B_OK) { 2436 throw Exception(error, 2437 "failed to rename temporary activation file to final file"); 2438 // TODO: We should probably try to reverse the activation changes, though that 2439 // will fail, if this method has been called in response to node monitoring 2440 // events. Alternatively moving the package activation file could be made part 2441 // of the ioctl(), since packagefs should be able to undo package changes until 2442 // the very end, unless running out of memory. In the end the situation would be 2443 // bad anyway, though, since the activation file may refer to removed packages 2444 // and things would be in an inconsistent state after rebooting. 2445 } 2446 2447 // Update our state, i.e. remove deactivated packages and mark activated 2448 // packages accordingly. 2449 for (PackageSet::iterator it = packagesToActivate.begin(); 2450 it != packagesToActivate.end(); ++it) { 2451 (*it)->SetActive(true); 2452 fChangeCount++; 2453 } 2454 2455 for (PackageSet::iterator it = packagesToDeactivate.begin(); 2456 it != packagesToDeactivate.end(); ++it) { 2457 Package* package = *it; 2458 _RemovePackage(package); 2459 delete package; 2460 } 2461 } 2462