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 <stdlib.h> 14 #include <sys/stat.h> 15 #include <time.h> 16 #include <unistd.h> 17 18 #include <Directory.h> 19 #include <Entry.h> 20 #include <File.h> 21 #include <Looper.h> 22 #include <NodeMonitor.h> 23 #include <Path.h> 24 25 #include <package/solver/Solver.h> 26 #include <package/solver/SolverPackage.h> 27 #include <package/solver/SolverProblem.h> 28 #include <package/solver/SolverProblemSolution.h> 29 #include <package/solver/SolverRepository.h> 30 #include <package/solver/SolverResult.h> 31 32 #include <AutoDeleter.h> 33 #include <AutoLocker.h> 34 #include <package/DaemonDefs.h> 35 #include <package/PackagesDirectoryDefs.h> 36 37 #include "DebugSupport.h" 38 39 40 using namespace BPackageKit::BPrivate; 41 42 43 static const char* const kPackageFileNameExtension = ".hpkg"; 44 static const char* const kAdminDirectoryName 45 = PACKAGES_DIRECTORY_ADMIN_DIRECTORY; 46 static const char* const kActivationFileName 47 = PACKAGES_DIRECTORY_ACTIVATION_FILE; 48 static const char* const kTemporaryActivationFileName 49 = PACKAGES_DIRECTORY_ACTIVATION_FILE ".tmp"; 50 51 static bigtime_t kCommunicationTimeout = 1000000; 52 53 54 // #pragma mark - Listener 55 56 57 Volume::Listener::~Listener() 58 { 59 } 60 61 62 // #pragma mark - NodeMonitorEvent 63 64 65 struct Volume::NodeMonitorEvent 66 : public DoublyLinkedListLinkImpl<NodeMonitorEvent> { 67 public: 68 NodeMonitorEvent(const BString& entryName, bool created) 69 : 70 fEntryName(entryName), 71 fCreated(created) 72 { 73 } 74 75 const BString& EntryName() const 76 { 77 return fEntryName; 78 } 79 80 bool WasCreated() const 81 { 82 return fCreated; 83 } 84 85 private: 86 BString fEntryName; 87 bool fCreated; 88 }; 89 90 91 // #pragma mark - RelativePath 92 93 94 struct Volume::RelativePath { 95 RelativePath(const char* component1 = NULL, const char* component2 = NULL, 96 const char* component3 = NULL) 97 : 98 fComponentCount(kMaxComponentCount) 99 { 100 fComponents[0] = component1; 101 fComponents[1] = component2; 102 fComponents[2] = component3; 103 104 for (size_t i = 0; i < kMaxComponentCount; i++) { 105 if (fComponents[i] == NULL) { 106 fComponentCount = i; 107 break; 108 } 109 } 110 } 111 112 bool IsEmpty() const 113 { 114 return fComponentCount == 0; 115 } 116 117 RelativePath HeadPath(size_t componentsToDropCount = 1) 118 { 119 RelativePath result; 120 if (componentsToDropCount < fComponentCount) { 121 result.fComponentCount = fComponentCount - componentsToDropCount; 122 for (size_t i = 0; i < result.fComponentCount; i++) 123 result.fComponents[i] = fComponents[i]; 124 } 125 126 return result; 127 } 128 129 const char* LastComponent() const 130 { 131 return fComponentCount > 0 ? fComponents[fComponentCount - 1] : NULL; 132 } 133 134 BString ToString() const 135 { 136 if (fComponentCount == 0) 137 return BString(); 138 139 size_t length = fComponentCount - 1; 140 for (size_t i = 0; i < fComponentCount; i++) 141 length += strlen(fComponents[i]); 142 143 BString result; 144 char* buffer = result.LockBuffer(length + 1); 145 if (buffer == NULL) 146 return BString(); 147 148 for (size_t i = 0; i < fComponentCount; i++) { 149 if (i > 0) { 150 *buffer = '/'; 151 buffer++; 152 } 153 strcpy(buffer, fComponents[i]); 154 buffer += strlen(buffer); 155 } 156 157 return result.UnlockBuffer(); 158 } 159 160 private: 161 static const size_t kMaxComponentCount = 3; 162 163 const char* fComponents[kMaxComponentCount]; 164 size_t fComponentCount; 165 }; 166 167 168 // #pragma mark - Exception 169 170 171 struct Volume::Exception { 172 Exception(int32 error, const char* errorMessage = NULL, 173 const char* packageName = NULL) 174 : 175 fError(error), 176 fErrorMessage(errorMessage), 177 fPackageName(packageName) 178 { 179 } 180 181 int32 Error() const 182 { 183 return fError; 184 } 185 186 const BString& ErrorMessage() const 187 { 188 return fErrorMessage; 189 } 190 191 const BString& PackageName() const 192 { 193 return fPackageName; 194 } 195 196 BString ToString() const 197 { 198 const char* error; 199 if (fError >= 0) { 200 switch (fError) { 201 case B_DAEMON_OK: 202 error = "no error"; 203 break; 204 case B_DAEMON_CHANGE_COUNT_MISMATCH: 205 error = "transaction out of date"; 206 break; 207 case B_DAEMON_BAD_REQUEST: 208 error = "invalid transaction"; 209 break; 210 case B_DAEMON_NO_SUCH_PACKAGE: 211 error = "no such package"; 212 break; 213 case B_DAEMON_PACKAGE_ALREADY_EXISTS: 214 error = "package already exists"; 215 break; 216 default: 217 error = "unknown error"; 218 break; 219 } 220 } else 221 error = strerror(fError); 222 223 BString string; 224 if (!fErrorMessage.IsEmpty()) { 225 string = fErrorMessage; 226 string << ": "; 227 } 228 229 string << error; 230 231 if (!fPackageName.IsEmpty()) 232 string << ", package: \"" << fPackageName << '"'; 233 234 return string; 235 } 236 237 private: 238 int32 fError; 239 BString fErrorMessage; 240 BString fPackageName; 241 }; 242 243 244 // #pragma mark - CommitTransactionHandler 245 246 247 struct Volume::CommitTransactionHandler { 248 CommitTransactionHandler(Volume* volume, BMessage* request, BMessage& reply) 249 : 250 fVolume(volume), 251 fRequest(request), 252 fReply(reply), 253 fPackagesToActivate(20, true), 254 fPackagesToDeactivate(), 255 fAddedPackages(), 256 fRemovedPackages() 257 { 258 } 259 260 void HandleRequest() 261 { 262 // check the change count 263 int64 changeCount; 264 if (fRequest->FindInt64("change count", &changeCount) != B_OK) 265 throw Exception(B_DAEMON_CHANGE_COUNT_MISMATCH); 266 267 // collect the packages to deactivate 268 _GetPackagesToDeactivate(); 269 270 // read the packages to activate 271 _ReadPackagesToActivate(); 272 273 // anything to do at all? 274 if (fPackagesToActivate.IsEmpty() && fPackagesToDeactivate.empty()) { 275 throw Exception(B_DAEMON_BAD_REQUEST, 276 "no packages to activate or deactivate"); 277 } 278 279 // create an old state directory 280 _CreateOldStateDirectory(); 281 282 // move packages to deactivate to old state directory 283 _RemovePackagesToDeactivate(); 284 285 // move packages to activate to packages directory 286 _AddPackagesToActivate(); 287 288 // activate/deactivate packages 289 fVolume->_ChangePackageActivation(fAddedPackages, fRemovedPackages); 290 291 // removed packages have been deleted, new packages shall not be deleted 292 fAddedPackages.clear(); 293 fRemovedPackages.clear(); 294 fPackagesToActivate.MakeEmpty(false); 295 fPackagesToDeactivate.clear(); 296 } 297 298 void Revert() 299 { 300 // move packages to activate back to transaction directory 301 _RevertAddPackagesToActivate(); 302 303 // move packages to deactivate back to packages directory 304 _RevertRemovePackagesToDeactivate(); 305 306 // remove old state directory 307 _RemoveOldStateDirectory(); 308 } 309 310 private: 311 typedef BObjectList<Package> PackageList; 312 313 void _GetPackagesToDeactivate() 314 { 315 static const char* const kPackagesToDeactivateFieldName = "deactivate"; 316 317 // get the number of packages to activate 318 type_code type; 319 int32 packagesToDeactivateCount; 320 if (fRequest->GetInfo(kPackagesToDeactivateFieldName, &type, 321 &packagesToDeactivateCount) != B_OK) { 322 // the field is missing, i.e. no packages shall be deactivated 323 return; 324 } 325 326 for (int32 i = 0; i < packagesToDeactivateCount; i++) { 327 const char* packageName; 328 status_t error = fRequest->FindString( 329 kPackagesToDeactivateFieldName, i, &packageName); 330 if (error != B_OK) 331 throw Exception(error); 332 333 Package* package = fVolume->fPackagesByFileName.Lookup(packageName); 334 if (package == NULL) { 335 throw Exception(B_DAEMON_NO_SUCH_PACKAGE, "no such package", 336 packageName); 337 } 338 339 fPackagesToDeactivate.insert(package); 340 341 package->IncrementEntryRemovedIgnoreLevel(); 342 } 343 } 344 345 void _ReadPackagesToActivate() 346 { 347 static const char* const kPackagesToActivateFieldName = "activate"; 348 349 // get the number of packages to activate 350 type_code type; 351 int32 packagesToActivateCount; 352 if (fRequest->GetInfo(kPackagesToActivateFieldName, &type, 353 &packagesToActivateCount) != B_OK) { 354 // the field is missing, i.e. no packages shall be activated 355 return; 356 } 357 358 // get the name of the transaction directory 359 BString transactionDirectoryName; 360 if (packagesToActivateCount > 0) { 361 if (fRequest->FindString("transaction", &transactionDirectoryName) 362 != B_OK) { 363 throw Exception(B_DAEMON_BAD_REQUEST); 364 } 365 } 366 367 // check the name -- we only allow a simple subdirectory of the admin 368 // directory 369 if (transactionDirectoryName.IsEmpty() 370 || transactionDirectoryName.FindFirst('/') >= 0 371 || transactionDirectoryName == "." 372 || transactionDirectoryName == "..") { 373 throw Exception(B_DAEMON_BAD_REQUEST); 374 } 375 376 // open the directory 377 RelativePath directoryPath(kAdminDirectoryName, 378 transactionDirectoryName); 379 BDirectory directory; 380 status_t error = fVolume->_OpenPackagesSubDirectory(directoryPath, 381 false, directory); 382 if (error != B_OK) 383 throw Exception(error, "failed to open transaction directory"); 384 385 error = directory.GetNodeRef(&fTransactionDirectoryRef); 386 if (error != B_OK) { 387 throw Exception(error, 388 "failed to get transaction directory node ref"); 389 } 390 391 // read the packages 392 for (int32 i = 0; i < packagesToActivateCount; i++) { 393 const char* packageName; 394 error = fRequest->FindString(kPackagesToActivateFieldName, i, 395 &packageName); 396 if (error != B_OK) 397 throw Exception(error); 398 399 // make sure it doesn't clash with an already existing package 400 Package* package = fVolume->fPackagesByFileName.Lookup(packageName); 401 if (package != NULL 402 && fPackagesToDeactivate.find(package) 403 == fPackagesToDeactivate.end()) { 404 throw Exception(B_DAEMON_PACKAGE_ALREADY_EXISTS, NULL, 405 packageName); 406 } 407 408 // read the package 409 entry_ref entryRef; 410 entryRef.device = fTransactionDirectoryRef.device; 411 entryRef.directory = fTransactionDirectoryRef.node; 412 if (entryRef.set_name(packageName) != B_OK) 413 throw Exception(B_NO_MEMORY); 414 415 package = new(std::nothrow) Package; 416 if (package == NULL || !fPackagesToActivate.AddItem(package)) { 417 delete package; 418 throw Exception(B_NO_MEMORY); 419 } 420 421 error = package->Init(entryRef); 422 if (error != B_OK) 423 throw Exception(error, "failed to read package", packageName); 424 425 package->IncrementEntryCreatedIgnoreLevel(); 426 } 427 } 428 429 void _CreateOldStateDirectory() 430 { 431 // construct a nice name from the current date and time 432 time_t nowSeconds = time(NULL); 433 struct tm now; 434 BString baseName; 435 if (localtime_r(&nowSeconds, &now) != NULL) { 436 baseName.SetToFormat("state_%d-%02d-%02d_%02d:%02d:%02d", 437 1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour, 438 now.tm_min, now.tm_sec); 439 } else 440 baseName = "state"; 441 442 if (baseName.IsEmpty()) 443 throw Exception(B_NO_MEMORY); 444 445 // make sure the directory doesn't exist yet 446 BDirectory adminDirectory; 447 status_t error = fVolume->_OpenPackagesSubDirectory( 448 RelativePath(kAdminDirectoryName), true, adminDirectory); 449 if (error != B_OK) 450 throw Exception(error, "failed to open administrative directory"); 451 452 int uniqueId = 1; 453 BString directoryName = baseName; 454 while (BEntry(&adminDirectory, directoryName).Exists()) { 455 directoryName.SetToFormat("%s-%d", baseName.String(), uniqueId++); 456 if (directoryName.IsEmpty()) 457 throw Exception(B_NO_MEMORY); 458 } 459 460 // create the directory 461 error = adminDirectory.CreateDirectory(directoryName, 462 &fOldStateDirectory); 463 if (error != B_OK) 464 throw Exception(error, "failed to create old state directory"); 465 466 fOldStateDirectoryName = directoryName; 467 468 // write the old activation file 469 BEntry activationFile; 470 error = fVolume->_WriteActivationFile( 471 RelativePath(kAdminDirectoryName, directoryName), 472 kActivationFileName, PackageSet(), PackageSet(), activationFile); 473 if (error != B_OK) 474 throw Exception(error, "failed to write old activation file"); 475 476 // add the old state directory to the reply 477 error = fReply.AddString("old state", fOldStateDirectoryName); 478 if (error != B_OK) 479 throw Exception(error, "failed to add field to reply"); 480 } 481 482 void _RemovePackagesToDeactivate() 483 { 484 if (fPackagesToDeactivate.empty()) 485 return; 486 487 for (PackageSet::const_iterator it = fPackagesToDeactivate.begin(); 488 it != fPackagesToDeactivate.end(); ++it) { 489 // get an BEntry for the package 490 Package* package = *it; 491 entry_ref entryRef; 492 entryRef.device = fVolume->fPackagesDirectoryRef.device; 493 entryRef.directory = fVolume->fPackagesDirectoryRef.node; 494 if (entryRef.set_name(package->FileName()) != B_OK) 495 throw Exception(B_NO_MEMORY); 496 497 BEntry entry; 498 status_t error = entry.SetTo(&entryRef); 499 if (error != B_OK) { 500 throw Exception(error, "failed to get package entry", 501 package->FileName()); 502 } 503 504 // move entry 505 fRemovedPackages.insert(package); 506 507 error = entry.MoveTo(&fOldStateDirectory); 508 if (error != B_OK) { 509 fRemovedPackages.erase(package); 510 throw Exception(error, 511 "failed to move old package from packages directory", 512 package->FileName()); 513 } 514 } 515 } 516 517 void _AddPackagesToActivate() 518 { 519 if (fPackagesToActivate.IsEmpty()) 520 return; 521 522 // open packages directory 523 BDirectory packagesDirectory; 524 status_t error 525 = packagesDirectory.SetTo(&fVolume->fPackagesDirectoryRef); 526 if (error != B_OK) 527 throw Exception(error, "failed to open packages directory"); 528 529 int32 count = fPackagesToActivate.CountItems(); 530 for (int32 i = 0; i < count; i++) { 531 // get an BEntry for the package 532 Package* package = fPackagesToActivate.ItemAt(i); 533 entry_ref entryRef; 534 entryRef.device = fTransactionDirectoryRef.device; 535 entryRef.directory = fTransactionDirectoryRef.node; 536 if (entryRef.set_name(package->FileName()) != B_OK) 537 throw Exception(B_NO_MEMORY); 538 539 BEntry entry; 540 error = entry.SetTo(&entryRef); 541 if (error != B_OK) { 542 throw Exception(error, "failed to get package entry", 543 package->FileName()); 544 } 545 546 // move entry 547 fAddedPackages.insert(package); 548 549 error = entry.MoveTo(&packagesDirectory); 550 if (error != B_OK) { 551 fAddedPackages.erase(package); 552 throw Exception(error, 553 "failed to move new package to packages directory", 554 package->FileName()); 555 } 556 557 // also add the package to the volume 558 fVolume->_AddPackage(package); 559 } 560 } 561 562 void _RevertAddPackagesToActivate() 563 { 564 if (fAddedPackages.empty()) 565 return; 566 567 // open transaction directory 568 BDirectory transactionDirectory; 569 status_t error = transactionDirectory.SetTo(&fTransactionDirectoryRef); 570 if (error != B_OK) { 571 ERROR("failed to open transaction directory: %s\n", 572 strerror(error)); 573 } 574 575 for (PackageSet::iterator it = fAddedPackages.begin(); 576 it != fAddedPackages.end(); ++it) { 577 // remove package from the volume 578 Package* package = *it; 579 fVolume->_RemovePackage(package); 580 581 if (transactionDirectory.InitCheck() != B_OK) 582 continue; 583 584 // get BEntry for the package 585 entry_ref entryRef; 586 entryRef.device = fVolume->fPackagesDirectoryRef.device; 587 entryRef.directory = fVolume->fPackagesDirectoryRef.node; 588 if (entryRef.set_name(package->FileName()) != B_OK) { 589 ERROR("out of memory\n"); 590 continue; 591 } 592 593 BEntry entry; 594 error = entry.SetTo(&entryRef); 595 if (error != B_OK) { 596 ERROR("failed to get entry for package \"%s\": %s\n", 597 package->FileName().String(), strerror(error)); 598 continue; 599 } 600 601 // move entry 602 error = entry.MoveTo(&transactionDirectory); 603 if (error != B_OK) { 604 ERROR("failed to move new package \"%s\" back to transaction " 605 "directory: %s\n", package->FileName().String(), 606 strerror(error)); 607 continue; 608 } 609 } 610 } 611 612 void _RevertRemovePackagesToDeactivate() 613 { 614 if (fRemovedPackages.empty()) 615 return; 616 617 // open packages directory 618 BDirectory packagesDirectory; 619 status_t error 620 = packagesDirectory.SetTo(&fVolume->fPackagesDirectoryRef); 621 if (error != B_OK) { 622 throw Exception(error, "failed to open packages directory"); 623 ERROR("failed to open packages directory: %s\n", 624 strerror(error)); 625 return; 626 } 627 628 for (PackageSet::iterator it = fRemovedPackages.begin(); 629 it != fRemovedPackages.end(); ++it) { 630 // get an BEntry for the package 631 Package* package = *it; 632 BEntry entry; 633 status_t error = entry.SetTo(&fOldStateDirectory, 634 package->FileName()); 635 if (error != B_OK) { 636 ERROR("failed to get entry for package \"%s\": %s\n", 637 package->FileName().String(), strerror(error)); 638 continue; 639 } 640 641 // move entry 642 error = entry.MoveTo(&packagesDirectory); 643 if (error != B_OK) { 644 ERROR("failed to move old package \"%s\" back to packages " 645 "directory: %s\n", package->FileName().String(), 646 strerror(error)); 647 continue; 648 } 649 } 650 } 651 652 void _RemoveOldStateDirectory() 653 { 654 if (fOldStateDirectory.InitCheck() != B_OK) 655 return; 656 657 // remove the old activation file (it won't exist, if creating it 658 // failed) 659 BEntry(&fOldStateDirectory, kActivationFileName).Remove(); 660 661 // Now the directory should be empty. If it isn't, it still contains 662 // some old package file, which we failed to move back. 663 BEntry entry; 664 status_t error = fOldStateDirectory.GetEntry(&entry); 665 if (error != B_OK) { 666 ERROR("failed to get entry for old state directory: %s\n", 667 strerror(error)); 668 return; 669 } 670 671 error = entry.Remove(); 672 if (error != B_OK) { 673 ERROR("failed to remove old state directory: %s\n", 674 strerror(error)); 675 return; 676 } 677 } 678 679 private: 680 Volume* fVolume; 681 BMessage* fRequest; 682 BMessage& fReply; 683 PackageList fPackagesToActivate; 684 PackageSet fPackagesToDeactivate; 685 PackageSet fAddedPackages; 686 PackageSet fRemovedPackages; 687 BDirectory fOldStateDirectory; 688 BString fOldStateDirectoryName; 689 node_ref fTransactionDirectoryRef; 690 }; 691 692 693 // #pragma mark - Volume 694 695 696 Volume::Volume(BLooper* looper) 697 : 698 BHandler(), 699 fPath(), 700 fMountType(PACKAGE_FS_MOUNT_TYPE_CUSTOM), 701 fRootDirectoryRef(), 702 fPackagesDirectoryRef(), 703 fRoot(NULL), 704 fListener(NULL), 705 fPackagesByFileName(), 706 fPackagesByNodeRef(), 707 fPendingNodeMonitorEventsLock("pending node monitor events"), 708 fPendingNodeMonitorEvents(), 709 fPackagesToBeActivated(), 710 fPackagesToBeDeactivated(), 711 fChangeCount(0), 712 fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY) 713 { 714 looper->AddHandler(this); 715 } 716 717 718 Volume::~Volume() 719 { 720 Unmounted(); 721 // need for error case in InitPackages() 722 723 fPackagesByFileName.Clear(); 724 725 Package* package = fPackagesByNodeRef.Clear(true); 726 while (package != NULL) { 727 Package* next = package->NodeRefHashTableNext(); 728 delete package; 729 package = next; 730 } 731 } 732 733 734 status_t 735 Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef) 736 { 737 if (fPackagesByFileName.Init() != B_OK || fPackagesByNodeRef.Init() != B_OK) 738 RETURN_ERROR(B_NO_MEMORY); 739 740 fRootDirectoryRef = rootDirectoryRef; 741 742 // open the root directory 743 BDirectory directory; 744 status_t error = directory.SetTo(&fRootDirectoryRef); 745 if (error != B_OK) { 746 ERROR("Volume::Init(): failed to open root directory: %s\n", 747 strerror(error)); 748 RETURN_ERROR(error); 749 } 750 751 // get the directory path 752 BEntry entry; 753 error = directory.GetEntry(&entry); 754 755 BPath path; 756 if (error == B_OK) 757 error = entry.GetPath(&path); 758 759 if (error != B_OK) { 760 ERROR("Volume::Init(): failed to get root directory path: %s\n", 761 strerror(error)); 762 RETURN_ERROR(error); 763 } 764 765 fPath = path.Path(); 766 if (fPath.IsEmpty()) 767 RETURN_ERROR(B_NO_MEMORY); 768 769 // get a volume info from the FS 770 int fd = directory.Dup(); 771 if (fd < 0) { 772 ERROR("Volume::Init(): failed to get root directory FD: %s\n", 773 strerror(fd)); 774 RETURN_ERROR(fd); 775 } 776 FileDescriptorCloser fdCloser(fd); 777 778 PackageFSVolumeInfo info; 779 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_VOLUME_INFO, &info, sizeof(info)) 780 != 0) { 781 ERROR("Volume::Init(): failed to get volume info: %s\n", 782 strerror(errno)); 783 RETURN_ERROR(errno); 784 } 785 786 fMountType = info.mountType; 787 fPackagesDirectoryRef.device = info.packagesDeviceID; 788 fPackagesDirectoryRef.node = info.packagesDirectoryID; 789 790 _packageRootRef.device = info.rootDeviceID; 791 _packageRootRef.node = info.rootDirectoryID; 792 793 return B_OK; 794 } 795 796 797 status_t 798 Volume::InitPackages(Listener* listener) 799 { 800 // node-monitor the volume's packages directory 801 status_t error = watch_node(&fPackagesDirectoryRef, B_WATCH_DIRECTORY, 802 BMessenger(this)); 803 if (error == B_OK) { 804 fListener = listener; 805 } else { 806 ERROR("Volume::InitPackages(): failed to start watching the packages " 807 "directory of the volume at \"%s\": %s\n", 808 fPath.String(), strerror(error)); 809 // Not good, but not fatal. Only the manual package operations in the 810 // packages directory won't work correctly. 811 } 812 813 // read the packages directory and get the active packages 814 int fd = OpenRootDirectory(); 815 if (fd < 0) { 816 ERROR("Volume::InitPackages(): failed to open root directory: %s\n", 817 strerror(fd)); 818 RETURN_ERROR(fd); 819 } 820 FileDescriptorCloser fdCloser(fd); 821 822 error = _ReadPackagesDirectory(); 823 if (error != B_OK) 824 RETURN_ERROR(error); 825 826 error = _GetActivePackages(fd); 827 if (error != B_OK) 828 RETURN_ERROR(error); 829 830 return B_OK; 831 } 832 833 834 status_t 835 Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly) 836 { 837 for (PackageFileNameHashTable::Iterator it 838 = fPackagesByFileName.GetIterator(); it.HasNext();) { 839 Package* package = it.Next(); 840 if (activeOnly && !package->IsActive()) 841 continue; 842 843 status_t error = repository.AddPackage(package->Info()); 844 if (error != B_OK) { 845 ERROR("Volume::AddPackagesToRepository(): failed to add package %s " 846 "to repository: %s\n", package->FileName().String(), 847 strerror(error)); 848 return error; 849 } 850 } 851 852 return B_OK; 853 } 854 855 856 void 857 Volume::InitialVerify(Volume* nextVolume, Volume* nextNextVolume) 858 { 859 INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume); 860 // create the solver 861 BSolver* solver; 862 status_t error = BSolver::Create(solver); 863 if (error != B_OK) { 864 ERROR("Volume::InitialVerify(): failed to create solver: %s\n", 865 strerror(error)); 866 return; 867 } 868 ObjectDeleter<BSolver> solverDeleter(solver); 869 870 // add a repository with all active packages 871 BSolverRepository repository; 872 error = _AddRepository(solver, repository, true, true); 873 if (error != B_OK) { 874 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 875 strerror(error)); 876 return; 877 } 878 879 // add a repository for the next volume 880 BSolverRepository nextRepository; 881 if (nextVolume != NULL) { 882 nextRepository.SetPriority(1); 883 error = nextVolume->_AddRepository(solver, nextRepository, true, false); 884 if (error != B_OK) { 885 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 886 strerror(error)); 887 return; 888 } 889 } 890 891 // add a repository for the next next volume 892 BSolverRepository nextNextRepository; 893 if (nextNextVolume != NULL) { 894 nextNextRepository.SetPriority(2); 895 error = nextNextVolume->_AddRepository(solver, nextNextRepository, true, 896 false); 897 if (error != B_OK) { 898 ERROR("Volume::InitialVerify(): failed to add repository: %s\n", 899 strerror(error)); 900 return; 901 } 902 } 903 904 // verify 905 error = solver->VerifyInstallation(); 906 if (error != B_OK) { 907 ERROR("Volume::InitialVerify(): failed to verify: %s\n", 908 strerror(error)); 909 return; 910 } 911 912 if (!solver->HasProblems()) { 913 INFORM("Volume::InitialVerify(): volume at \"%s\" is consistent\n", 914 Path().String()); 915 return; 916 } 917 918 // print the problems 919 // TODO: Notify the user ... 920 INFORM("Volume::InitialVerify(): volume at \"%s\" has problems:\n", 921 Path().String()); 922 923 int32 problemCount = solver->CountProblems(); 924 for (int32 i = 0; i < problemCount; i++) { 925 BSolverProblem* problem = solver->ProblemAt(i); 926 INFORM(" %" B_PRId32 ": %s\n", i + 1, problem->ToString().String()); 927 int32 solutionCount = problem->CountSolutions(); 928 for (int32 k = 0; k < solutionCount; k++) { 929 const BSolverProblemSolution* solution = problem->SolutionAt(k); 930 INFORM(" solution %" B_PRId32 ":\n", k + 1); 931 int32 elementCount = solution->CountElements(); 932 for (int32 l = 0; l < elementCount; l++) { 933 const BSolverProblemSolutionElement* element 934 = solution->ElementAt(l); 935 INFORM(" - %s\n", element->ToString().String()); 936 } 937 } 938 } 939 } 940 941 942 void 943 Volume::HandleGetLocationInfoRequest(BMessage* message) 944 { 945 // If the cached reply message is up-to-date, just send it. 946 int64 changeCount; 947 if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK 948 && changeCount == fChangeCount) { 949 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 950 kCommunicationTimeout); 951 return; 952 } 953 954 // rebuild the reply message 955 fLocationInfoReply.MakeEmpty(); 956 957 if (fLocationInfoReply.AddInt32("base directory device", 958 fRootDirectoryRef.device) != B_OK 959 || fLocationInfoReply.AddInt64("base directory node", 960 fRootDirectoryRef.node) != B_OK 961 || fLocationInfoReply.AddInt32("packages directory device", 962 fPackagesDirectoryRef.device) != B_OK 963 || fLocationInfoReply.AddInt64("packages directory node", 964 fPackagesDirectoryRef.node) != B_OK) { 965 return; 966 } 967 968 for (PackageFileNameHashTable::Iterator it 969 = fPackagesByFileName.GetIterator(); it.HasNext();) { 970 Package* package = it.Next(); 971 const char* fieldName = package->IsActive() 972 ? "active packages" : "inactive packages"; 973 BMessage packageArchive; 974 if (package->Info().Archive(&packageArchive) != B_OK 975 || fLocationInfoReply.AddMessage(fieldName, &packageArchive) 976 != B_OK) { 977 return; 978 } 979 } 980 981 if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK) 982 return; 983 984 message->SendReply(&fLocationInfoReply, (BHandler*)NULL, 985 kCommunicationTimeout); 986 } 987 988 989 void 990 Volume::HandleCommitTransactionRequest(BMessage* message) 991 { 992 // Prepare the reply in so far that we can at least set the error code 993 // without risk of failure. 994 BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); 995 if (reply.AddInt32("error", B_ERROR) != B_OK) 996 return; 997 998 // perform the request 999 CommitTransactionHandler handler(this, message, reply); 1000 int32 error; 1001 try { 1002 handler.HandleRequest(); 1003 error = B_DAEMON_OK; 1004 } catch (Exception& exception) { 1005 error = exception.Error(); 1006 1007 if (!exception.ErrorMessage().IsEmpty()) 1008 reply.AddString("error message", exception.ErrorMessage()); 1009 if (!exception.PackageName().IsEmpty()) 1010 reply.AddString("error package", exception.PackageName()); 1011 } catch (std::bad_alloc& exception) { 1012 error = B_NO_MEMORY; 1013 } 1014 1015 // revert on error 1016 if (error != B_DAEMON_OK) 1017 handler.Revert(); 1018 1019 // send the reply 1020 reply.ReplaceInt32("error", error); 1021 message->SendReply(&reply, (BHandler*)NULL, kCommunicationTimeout); 1022 } 1023 1024 1025 void 1026 Volume::Unmounted() 1027 { 1028 if (fListener != NULL) { 1029 stop_watching(BMessenger(this)); 1030 fListener = NULL; 1031 } 1032 1033 if (BLooper* looper = Looper()) 1034 looper->RemoveHandler(this); 1035 } 1036 1037 1038 void 1039 Volume::MessageReceived(BMessage* message) 1040 { 1041 switch (message->what) { 1042 case B_NODE_MONITOR: 1043 { 1044 int32 opcode; 1045 if (message->FindInt32("opcode", &opcode) != B_OK) 1046 break; 1047 1048 switch (opcode) { 1049 case B_ENTRY_CREATED: 1050 _HandleEntryCreatedOrRemoved(message, true); 1051 break; 1052 case B_ENTRY_REMOVED: 1053 _HandleEntryCreatedOrRemoved(message, false); 1054 break; 1055 case B_ENTRY_MOVED: 1056 _HandleEntryMoved(message); 1057 break; 1058 default: 1059 break; 1060 } 1061 break; 1062 } 1063 1064 default: 1065 BHandler::MessageReceived(message); 1066 break; 1067 } 1068 } 1069 1070 1071 int 1072 Volume::OpenRootDirectory() const 1073 { 1074 BDirectory directory; 1075 status_t error = directory.SetTo(&fRootDirectoryRef); 1076 if (error != B_OK) { 1077 ERROR("Volume::OpenRootDirectory(): failed to open root directory: " 1078 "%s\n", strerror(error)); 1079 RETURN_ERROR(error); 1080 } 1081 1082 return directory.Dup(); 1083 } 1084 1085 1086 void 1087 Volume::ProcessPendingNodeMonitorEvents() 1088 { 1089 // get the events 1090 NodeMonitorEventList events; 1091 { 1092 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 1093 events.MoveFrom(&fPendingNodeMonitorEvents); 1094 } 1095 1096 // process them 1097 while (NodeMonitorEvent* event = events.RemoveHead()) { 1098 ObjectDeleter<NodeMonitorEvent> eventDeleter(event); 1099 if (event->WasCreated()) 1100 _PackagesEntryCreated(event->EntryName()); 1101 else 1102 _PackagesEntryRemoved(event->EntryName()); 1103 } 1104 } 1105 1106 1107 bool 1108 Volume::HasPendingPackageActivationChanges() const 1109 { 1110 return !fPackagesToBeActivated.empty() || !fPackagesToBeDeactivated.empty(); 1111 } 1112 1113 1114 void 1115 Volume::ProcessPendingPackageActivationChanges() 1116 { 1117 if (!HasPendingPackageActivationChanges()) 1118 return; 1119 1120 try { 1121 _ChangePackageActivation(fPackagesToBeActivated, 1122 fPackagesToBeDeactivated); 1123 } catch (Exception& exception) { 1124 ERROR("Volume::ProcessPendingPackageActivationChanges(): package " 1125 "activation failed: %s\n", exception.ToString().String()); 1126 // TODO: Notify the user! 1127 } 1128 1129 // clear the activation/deactivation sets in any event 1130 fPackagesToBeActivated.clear(); 1131 fPackagesToBeDeactivated.clear(); 1132 } 1133 1134 1135 void 1136 Volume::_HandleEntryCreatedOrRemoved(const BMessage* message, bool created) 1137 { 1138 // only moves to or from our packages directory are interesting 1139 int32 deviceID; 1140 int64 directoryID; 1141 const char* name; 1142 if (message->FindInt32("device", &deviceID) != B_OK 1143 || message->FindInt64("directory", &directoryID) != B_OK 1144 || message->FindString("name", &name) != B_OK 1145 || node_ref(deviceID, directoryID) != fPackagesDirectoryRef) { 1146 return; 1147 } 1148 1149 _QueueNodeMonitorEvent(name, created); 1150 } 1151 1152 1153 void 1154 Volume::_HandleEntryMoved(const BMessage* message) 1155 { 1156 int32 deviceID; 1157 int64 fromDirectoryID; 1158 int64 toDirectoryID; 1159 const char* fromName; 1160 const char* toName; 1161 if (message->FindInt32("device", &deviceID) != B_OK 1162 || message->FindInt64("from directory", &fromDirectoryID) != B_OK 1163 || message->FindInt64("to directory", &toDirectoryID) != B_OK 1164 || message->FindString("from name", &fromName) != B_OK 1165 || message->FindString("name", &toName) != B_OK 1166 || deviceID != fPackagesDirectoryRef.device 1167 || (fromDirectoryID != fPackagesDirectoryRef.node 1168 && toDirectoryID != fPackagesDirectoryRef.node)) { 1169 return; 1170 } 1171 1172 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 1173 // make sure for a move the two events cannot get split 1174 1175 if (fromDirectoryID == fPackagesDirectoryRef.node) 1176 _QueueNodeMonitorEvent(fromName, false); 1177 if (toDirectoryID == fPackagesDirectoryRef.node) 1178 _QueueNodeMonitorEvent(toName, true); 1179 } 1180 1181 1182 void 1183 Volume::_QueueNodeMonitorEvent(const BString& name, bool wasCreated) 1184 { 1185 if (name.IsEmpty()) { 1186 ERROR("Volume::_QueueNodeMonitorEvent(): got empty name.\n"); 1187 return; 1188 } 1189 1190 // ignore entries that don't have the ".hpkg" extension 1191 if (!name.EndsWith(kPackageFileNameExtension)) 1192 return; 1193 1194 NodeMonitorEvent* event 1195 = new(std::nothrow) NodeMonitorEvent(name, wasCreated); 1196 if (event == NULL) { 1197 ERROR("Volume::_QueueNodeMonitorEvent(): out of memory.\n"); 1198 return; 1199 } 1200 1201 AutoLocker<BLocker> eventsLock(fPendingNodeMonitorEventsLock); 1202 bool firstEvent = fPendingNodeMonitorEvents.IsEmpty(); 1203 fPendingNodeMonitorEvents.Add(event); 1204 eventsLock.Unlock(); 1205 1206 if (firstEvent && fListener != NULL) 1207 fListener->VolumeNodeMonitorEventOccurred(this); 1208 } 1209 1210 1211 void 1212 Volume::_PackagesEntryCreated(const char* name) 1213 { 1214 INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name); 1215 // Ignore the event, if the package is already known. 1216 Package* package = fPackagesByFileName.Lookup(name); 1217 if (package != NULL) { 1218 if (package->EntryCreatedIgnoreLevel() > 0) { 1219 package->DecrementEntryCreatedIgnoreLevel(); 1220 } else { 1221 WARN("node monitoring created event for already known entry " 1222 "\"%s\"\n", name); 1223 } 1224 1225 return; 1226 } 1227 1228 entry_ref entry; 1229 entry.device = fPackagesDirectoryRef.device; 1230 entry.directory = fPackagesDirectoryRef.node; 1231 status_t error = entry.set_name(name); 1232 if (error != B_OK) { 1233 ERROR("out of memory\n"); 1234 return; 1235 } 1236 1237 package = new(std::nothrow) Package; 1238 if (package == NULL) { 1239 ERROR("out of memory\n"); 1240 return; 1241 } 1242 ObjectDeleter<Package> packageDeleter(package); 1243 1244 error = package->Init(entry); 1245 if (error != B_OK) { 1246 ERROR("failed to init package for file \"%s\"\n", name); 1247 return; 1248 } 1249 1250 _AddPackage(package); 1251 packageDeleter.Detach(); 1252 1253 try { 1254 fPackagesToBeActivated.insert(package); 1255 } catch (std::bad_alloc& exception) { 1256 ERROR("out of memory\n"); 1257 return; 1258 } 1259 } 1260 1261 1262 void 1263 Volume::_PackagesEntryRemoved(const char* name) 1264 { 1265 INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name); 1266 Package* package = fPackagesByFileName.Lookup(name); 1267 if (package == NULL) 1268 return; 1269 1270 // Ignore the event, if we generated it ourselves. 1271 if (package->EntryRemovedIgnoreLevel() > 0) { 1272 package->DecrementEntryRemovedIgnoreLevel(); 1273 return; 1274 } 1275 1276 // Remove the package from the packages-to-be-activated set, if it is in 1277 // there (unlikely, unless we see a create-remove-create sequence). 1278 PackageSet::iterator it = fPackagesToBeActivated.find(package); 1279 if (it != fPackagesToBeActivated.end()) 1280 fPackagesToBeActivated.erase(it); 1281 1282 // If the package isn't active, just remove it for good. 1283 if (!package->IsActive()) { 1284 _RemovePackage(package); 1285 delete package; 1286 return; 1287 } 1288 1289 // The package must be deactivated. 1290 try { 1291 fPackagesToBeDeactivated.insert(package); 1292 } catch (std::bad_alloc& exception) { 1293 ERROR("out of memory\n"); 1294 return; 1295 } 1296 } 1297 1298 1299 void 1300 Volume::_FillInActivationChangeItem(PackageFSActivationChangeItem* item, 1301 PackageFSActivationChangeType type, Package* package, char*& nameBuffer) 1302 { 1303 item->type = type; 1304 item->packageDeviceID = package->NodeRef().device; 1305 item->packageNodeID = package->NodeRef().node; 1306 item->nameLength = package->FileName().Length(); 1307 item->parentDeviceID = fPackagesDirectoryRef.device; 1308 item->parentDirectoryID = fPackagesDirectoryRef.node; 1309 item->name = nameBuffer; 1310 strcpy(nameBuffer, package->FileName()); 1311 nameBuffer += package->FileName().Length() + 1; 1312 } 1313 1314 1315 void 1316 Volume::_AddPackage(Package* package) 1317 { 1318 fPackagesByFileName.Insert(package); 1319 fPackagesByNodeRef.Insert(package); 1320 } 1321 1322 void 1323 Volume::_RemovePackage(Package* package) 1324 { 1325 fPackagesByFileName.Remove(package); 1326 fPackagesByNodeRef.Remove(package); 1327 fChangeCount++; 1328 } 1329 1330 1331 status_t 1332 Volume::_ReadPackagesDirectory() 1333 { 1334 BDirectory directory; 1335 status_t error = directory.SetTo(&fPackagesDirectoryRef); 1336 if (error != B_OK) { 1337 ERROR("Volume::_ReadPackagesDirectory(): failed to open packages " 1338 "directory: %s\n", strerror(error)); 1339 RETURN_ERROR(error); 1340 } 1341 1342 entry_ref entry; 1343 while (directory.GetNextRef(&entry) == B_OK) { 1344 if (!BString(entry.name).EndsWith(kPackageFileNameExtension)) 1345 continue; 1346 1347 Package* package = new(std::nothrow) Package; 1348 if (package == NULL) 1349 RETURN_ERROR(B_NO_MEMORY); 1350 ObjectDeleter<Package> packageDeleter(package); 1351 1352 status_t error = package->Init(entry); 1353 if (error == B_OK) { 1354 _AddPackage(package); 1355 packageDeleter.Detach(); 1356 } 1357 } 1358 1359 return B_OK; 1360 } 1361 1362 1363 status_t 1364 Volume::_GetActivePackages(int fd) 1365 { 1366 uint32 maxPackageCount = 16 * 1024; 1367 PackageFSGetPackageInfosRequest* request = NULL; 1368 MemoryDeleter requestDeleter; 1369 size_t bufferSize; 1370 for (;;) { 1371 bufferSize = sizeof(PackageFSGetPackageInfosRequest) 1372 + (maxPackageCount - 1) * sizeof(PackageFSPackageInfo); 1373 request = (PackageFSGetPackageInfosRequest*)malloc(bufferSize); 1374 if (request == NULL) 1375 RETURN_ERROR(B_NO_MEMORY); 1376 requestDeleter.SetTo(request); 1377 1378 if (ioctl(fd, PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS, request, 1379 bufferSize) != 0) { 1380 ERROR("Volume::_GetActivePackages(): failed to get active package " 1381 "info from package FS: %s\n", strerror(errno)); 1382 RETURN_ERROR(errno); 1383 } 1384 1385 if (request->packageCount <= maxPackageCount) 1386 break; 1387 1388 maxPackageCount = request->packageCount; 1389 requestDeleter.Unset(); 1390 } 1391 1392 // mark the returned packages active 1393 for (uint32 i = 0; i < request->packageCount; i++) { 1394 Package* package = fPackagesByNodeRef.Lookup( 1395 node_ref(request->infos[i].packageDeviceID, 1396 request->infos[i].packageNodeID)); 1397 if (package == NULL) { 1398 WARN("active package (dev: %" B_PRIdDEV ", node: %" B_PRIdINO ") " 1399 "not found in package directory\n", 1400 request->infos[i].packageDeviceID, 1401 request->infos[i].packageNodeID); 1402 // TODO: Deactivate the package right away? 1403 continue; 1404 } 1405 1406 package->SetActive(true); 1407 INFORM("active package: \"%s\"\n", package->FileName().String()); 1408 } 1409 1410 for (PackageNodeRefHashTable::Iterator it = fPackagesByNodeRef.GetIterator(); 1411 it.HasNext();) { 1412 Package* package = it.Next(); 1413 if (!package->IsActive()) 1414 INFORM("inactive package: \"%s\"\n", package->FileName().String()); 1415 } 1416 1417 PackageNodeRefHashTable fPackagesByNodeRef; 1418 // INFORM("%" B_PRIu32 " active packages:\n", request->packageCount); 1419 // for (uint32 i = 0; i < request->packageCount; i++) { 1420 // INFORM(" dev: %" B_PRIdDEV ", node: %" B_PRIdINO "\n", 1421 // request->infos[i].packageDeviceID, request->infos[i].packageNodeID); 1422 // } 1423 1424 return B_OK; 1425 } 1426 1427 1428 status_t 1429 Volume::_AddRepository(BSolver* solver, BSolverRepository& repository, 1430 bool activeOnly, bool installed) 1431 { 1432 status_t error = repository.SetTo(Path()); 1433 if (error != B_OK) { 1434 ERROR("Volume::_AddRepository(): failed to init repository: %s\n", 1435 strerror(error)); 1436 return error; 1437 } 1438 1439 repository.SetInstalled(installed); 1440 1441 error = AddPackagesToRepository(repository, true); 1442 if (error != B_OK) { 1443 ERROR("Volume::_AddRepository(): failed to add packages to " 1444 "repository: %s\n", strerror(error)); 1445 return error; 1446 } 1447 1448 error = solver->AddRepository(&repository); 1449 if (error != B_OK) { 1450 ERROR("Volume::_AddRepository(): failed to add repository to solver: " 1451 "%s\n", strerror(error)); 1452 return error; 1453 } 1454 1455 return B_OK; 1456 } 1457 1458 1459 status_t 1460 Volume::_OpenPackagesFile(const RelativePath& subDirectoryPath, 1461 const char* fileName, uint32 openMode, BFile& _file, BEntry* _entry) 1462 { 1463 BDirectory directory; 1464 if (!subDirectoryPath.IsEmpty()) { 1465 status_t error = _OpenPackagesSubDirectory(subDirectoryPath, 1466 (openMode & B_CREATE_FILE) != 0, directory); 1467 if (error != B_OK) { 1468 ERROR("Volume::_OpenPackagesFile(): failed to open packages " 1469 "subdirectory \"%s\": %s\n", 1470 subDirectoryPath.ToString().String(), strerror(error)); 1471 RETURN_ERROR(error); 1472 } 1473 } else { 1474 status_t error = directory.SetTo(&fPackagesDirectoryRef); 1475 if (error != B_OK) { 1476 ERROR("Volume::_OpenPackagesFile(): failed to open packages " 1477 "directory: %s\n", strerror(error)); 1478 RETURN_ERROR(error); 1479 } 1480 } 1481 1482 BEntry stackEntry; 1483 BEntry& entry = _entry != NULL ? *_entry : stackEntry; 1484 status_t error = entry.SetTo(&directory, fileName); 1485 if (error != B_OK) { 1486 ERROR("Volume::_OpenPackagesFile(): failed to get entry for file: %s", 1487 strerror(error)); 1488 RETURN_ERROR(error); 1489 } 1490 1491 return _file.SetTo(&entry, openMode); 1492 } 1493 1494 1495 status_t 1496 Volume::_OpenPackagesSubDirectory(const RelativePath& path, bool create, 1497 BDirectory& _directory) 1498 { 1499 // open the packages directory 1500 BDirectory directory; 1501 status_t error = directory.SetTo(&fPackagesDirectoryRef); 1502 if (error != B_OK) { 1503 ERROR("Volume::_OpenConfigSubDirectory(): failed to open packages " 1504 "directory: %s\n", strerror(error)); 1505 RETURN_ERROR(error); 1506 } 1507 1508 // get a string for the path 1509 BString pathString = path.ToString(); 1510 if (pathString.IsEmpty()) 1511 RETURN_ERROR(B_NO_MEMORY); 1512 1513 // If creating is not allowed, just try to open it. 1514 if (!create) 1515 RETURN_ERROR(_directory.SetTo(&directory, pathString)); 1516 1517 // get an absolute path and create the subdirectory 1518 BPath absolutePath; 1519 error = absolutePath.SetTo(&directory, pathString); 1520 if (error != B_OK) { 1521 ERROR("Volume::_OpenConfigSubDirectory(): failed to get absolute path " 1522 "for subdirectory \"%s\": %s\n", pathString.String(), 1523 strerror(error)); 1524 RETURN_ERROR(error); 1525 } 1526 1527 error = create_directory(absolutePath.Path(), 1528 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 1529 if (error != B_OK) { 1530 ERROR("Volume::_OpenConfigSubDirectory(): failed to create packages " 1531 "subdirectory \"%s\": %s\n", pathString.String(), 1532 strerror(error)); 1533 RETURN_ERROR(error); 1534 } 1535 1536 RETURN_ERROR(_directory.SetTo(&directory, pathString)); 1537 } 1538 1539 1540 status_t 1541 Volume::_CreateActivationFileContent(const PackageSet& toActivate, 1542 const PackageSet& toDeactivate, BString& _content) 1543 { 1544 BString activationFileContent; 1545 for (PackageFileNameHashTable::Iterator it 1546 = fPackagesByFileName.GetIterator(); it.HasNext();) { 1547 Package* package = it.Next(); 1548 if (package->IsActive() 1549 && toDeactivate.find(package) == toDeactivate.end()) { 1550 int32 length = activationFileContent.Length(); 1551 activationFileContent << package->FileName() << '\n'; 1552 if (activationFileContent.Length() 1553 < length + package->FileName().Length() + 1) { 1554 return B_NO_MEMORY; 1555 } 1556 } 1557 } 1558 1559 for (PackageSet::const_iterator it = toActivate.begin(); 1560 it != toActivate.end(); ++it) { 1561 Package* package = *it; 1562 int32 length = activationFileContent.Length(); 1563 activationFileContent << package->FileName() << '\n'; 1564 if (activationFileContent.Length() 1565 < length + package->FileName().Length() + 1) { 1566 return B_NO_MEMORY; 1567 } 1568 } 1569 1570 _content = activationFileContent; 1571 return B_OK; 1572 } 1573 1574 1575 status_t 1576 Volume::_WriteActivationFile(const RelativePath& directoryPath, 1577 const char* fileName, const PackageSet& toActivate, 1578 const PackageSet& toDeactivate, 1579 BEntry& _entry) 1580 { 1581 // create the content 1582 BString activationFileContent; 1583 status_t error = _CreateActivationFileContent(toActivate, toDeactivate, 1584 activationFileContent); 1585 if (error != B_OK) 1586 return error; 1587 1588 // write the file 1589 error = _WriteTextFile(directoryPath, fileName, activationFileContent, 1590 _entry); 1591 if (error != B_OK) { 1592 ERROR("Volume::_WriteActivationFile(): failed to write activation " 1593 "file \"%s/%s\": %s\n", directoryPath.ToString().String(), fileName, 1594 strerror(error)); 1595 return error; 1596 } 1597 1598 return B_OK; 1599 } 1600 1601 1602 status_t 1603 Volume::_WriteTextFile(const RelativePath& directoryPath, const char* fileName, 1604 const BString& content, BEntry& _entry) 1605 { 1606 BFile file; 1607 status_t error = _OpenPackagesFile(directoryPath, 1608 fileName, B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE, file, &_entry); 1609 if (error != B_OK) { 1610 ERROR("Volume::_WriteTextFile(): failed to create file \"%s/%s\": %s\n", 1611 directoryPath.ToString().String(), fileName, strerror(error)); 1612 return error; 1613 } 1614 1615 ssize_t bytesWritten = file.Write(content.String(), 1616 content.Length()); 1617 if (bytesWritten < 0) { 1618 ERROR("Volume::_WriteTextFile(): failed to write file \"%s/%s\": %s\n", 1619 directoryPath.ToString().String(), fileName, 1620 strerror(bytesWritten)); 1621 return bytesWritten; 1622 } 1623 1624 return B_OK; 1625 } 1626 1627 1628 void 1629 Volume::_ChangePackageActivation(const PackageSet& packagesToActivate, 1630 const PackageSet& packagesToDeactivate) 1631 { 1632 INFORM("Volume::_ChangePackageActivation(): activating %zu, deactivating %zu packages\n", 1633 packagesToActivate.size(), packagesToDeactivate.size()); 1634 1635 // write the temporary package activation file 1636 BEntry activationFileEntry; 1637 status_t error = _WriteActivationFile(RelativePath(kAdminDirectoryName), 1638 kTemporaryActivationFileName, packagesToActivate, packagesToDeactivate, 1639 activationFileEntry); 1640 if (error != B_OK) 1641 throw Exception(error, "failed to write activation file"); 1642 1643 // compute the size of the allocation we need for the activation change 1644 // request 1645 int32 itemCount = packagesToActivate.size() + packagesToDeactivate.size(); 1646 size_t requestSize = sizeof(PackageFSActivationChangeRequest) 1647 + itemCount * sizeof(PackageFSActivationChangeItem); 1648 1649 for (PackageSet::iterator it = packagesToActivate.begin(); 1650 it != packagesToActivate.end(); ++it) { 1651 requestSize += (*it)->FileName().Length() + 1; 1652 } 1653 1654 for (PackageSet::iterator it = packagesToDeactivate.begin(); 1655 it != packagesToDeactivate.end(); ++it) { 1656 requestSize += (*it)->FileName().Length() + 1; 1657 } 1658 1659 // allocate and prepare the request 1660 PackageFSActivationChangeRequest* request 1661 = (PackageFSActivationChangeRequest*)malloc(requestSize); 1662 if (request == NULL) 1663 throw Exception(B_NO_MEMORY); 1664 MemoryDeleter requestDeleter(request); 1665 1666 request->itemCount = itemCount; 1667 1668 PackageFSActivationChangeItem* item = &request->items[0]; 1669 char* nameBuffer = (char*)(item + itemCount); 1670 1671 for (PackageSet::iterator it = packagesToActivate.begin(); 1672 it != packagesToActivate.end(); ++it, item++) { 1673 _FillInActivationChangeItem(item, PACKAGE_FS_ACTIVATE_PACKAGE, *it, 1674 nameBuffer); 1675 } 1676 1677 for (PackageSet::iterator it = packagesToDeactivate.begin(); 1678 it != packagesToDeactivate.end(); ++it, item++) { 1679 _FillInActivationChangeItem(item, PACKAGE_FS_DEACTIVATE_PACKAGE, *it, 1680 nameBuffer); 1681 } 1682 1683 // issue the request 1684 int fd = OpenRootDirectory(); 1685 if (fd < 0) 1686 throw Exception(fd, "failed to open root directory"); 1687 FileDescriptorCloser fdCloser(fd); 1688 1689 if (ioctl(fd, PACKAGE_FS_OPERATION_CHANGE_ACTIVATION, request, requestSize) 1690 != 0) { 1691 // TODO: We need more error information and error handling! 1692 throw Exception(errno, "ioctl() to de-/activate packages failed"); 1693 } 1694 1695 // rename the temporary activation file to the final file 1696 error = activationFileEntry.Rename(kActivationFileName, true); 1697 if (error != B_OK) { 1698 throw Exception(error, 1699 "failed to rename temporary activation file to final file"); 1700 // TODO: We should probably try to reverse the activation changes, though that 1701 // will fail, if this method has been called in response to node monitoring 1702 // events. Alternatively moving the package activation file could be made part 1703 // of the ioctl(), since packagefs should be able to undo package changes until 1704 // the very end, unless running out of memory. In the end the situation would be 1705 // bad anyway, though, since the activation file may refer to removed packages 1706 // and things would be in an inconsistent state after rebooting. 1707 } 1708 1709 // Update our state, i.e. remove deactivated packages and mark activated 1710 // packages accordingly. 1711 for (PackageSet::iterator it = packagesToActivate.begin(); 1712 it != packagesToActivate.end(); ++it) { 1713 (*it)->SetActive(true); 1714 fChangeCount++; 1715 } 1716 1717 for (PackageSet::iterator it = fPackagesToBeDeactivated.begin(); 1718 it != fPackagesToBeDeactivated.end(); ++it) { 1719 Package* package = *it; 1720 _RemovePackage(package); 1721 delete package; 1722 } 1723 } 1724