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