1 /* 2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "Volume.h" 8 9 #include <dirent.h> 10 #include <errno.h> 11 #include <fcntl.h> 12 #include <string.h> 13 #include <sys/param.h> 14 #include <sys/stat.h> 15 16 #include <new> 17 18 #include <AppDefs.h> 19 #include <driver_settings.h> 20 #include <KernelExport.h> 21 #include <NodeMonitor.h> 22 #include <package/PackageInfoAttributes.h> 23 24 #include <AutoDeleter.h> 25 #include <AutoDeleterPosix.h> 26 #include <AutoDeleterDrivers.h> 27 #include <PackagesDirectoryDefs.h> 28 29 #include <vfs.h> 30 31 #include "AttributeIndex.h" 32 #include "DebugSupport.h" 33 #include "kernel_interface.h" 34 #include "LastModifiedIndex.h" 35 #include "NameIndex.h" 36 #include "OldUnpackingNodeAttributes.h" 37 #include "PackageFSRoot.h" 38 #include "PackageLinkDirectory.h" 39 #include "PackageLinksDirectory.h" 40 #include "Resolvable.h" 41 #include "SizeIndex.h" 42 #include "UnpackingLeafNode.h" 43 #include "UnpackingDirectory.h" 44 #include "Utils.h" 45 #include "Version.h" 46 47 48 // node ID of the root directory 49 static const ino_t kRootDirectoryID = 1; 50 51 static const uint32 kAllStatFields = B_STAT_MODE | B_STAT_UID | B_STAT_GID 52 | B_STAT_SIZE | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME 53 | B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME; 54 55 // shine-through directories 56 const char* const kShineThroughDirectories[] = { 57 "cache", "non-packaged", "packages", "settings", "var", NULL 58 }; 59 60 // sanity limit for activation change request 61 const size_t kMaxActivationRequestSize = 10 * 1024 * 1024; 62 63 // sanity limit for activation file size 64 const size_t kMaxActivationFileSize = 10 * 1024 * 1024; 65 66 static const char* const kAdministrativeDirectoryName 67 = PACKAGES_DIRECTORY_ADMIN_DIRECTORY; 68 static const char* const kActivationFileName 69 = PACKAGES_DIRECTORY_ACTIVATION_FILE; 70 static const char* const kActivationFilePath 71 = PACKAGES_DIRECTORY_ADMIN_DIRECTORY "/" 72 PACKAGES_DIRECTORY_ACTIVATION_FILE; 73 74 75 // #pragma mark - ShineThroughDirectory 76 77 78 struct Volume::ShineThroughDirectory : public Directory { 79 ShineThroughDirectory(ino_t id) 80 : 81 Directory(id) 82 { 83 get_real_time(fModifiedTime); 84 } 85 86 virtual timespec ModifiedTime() const 87 { 88 return fModifiedTime; 89 } 90 91 private: 92 timespec fModifiedTime; 93 }; 94 95 96 // #pragma mark - ActivationChangeRequest 97 98 99 struct Volume::ActivationChangeRequest { 100 public: 101 ActivationChangeRequest() 102 : 103 fRequest(NULL), 104 fRequestSize(0) 105 { 106 } 107 108 ~ActivationChangeRequest() 109 { 110 free(fRequest); 111 } 112 113 status_t Init(const void* userRequest, size_t requestSize) 114 { 115 // copy request to kernel 116 if (requestSize > kMaxActivationRequestSize) 117 RETURN_ERROR(B_BAD_VALUE); 118 119 fRequest = (PackageFSActivationChangeRequest*)malloc(requestSize); 120 if (fRequest == NULL) 121 RETURN_ERROR(B_NO_MEMORY); 122 fRequestSize = requestSize; 123 124 status_t error = user_memcpy(fRequest, userRequest, fRequestSize); 125 if (error != B_OK) 126 RETURN_ERROR(error); 127 128 uint32 itemCount = fRequest->itemCount; 129 const char* requestEnd = (const char*)fRequest + requestSize; 130 if (&fRequest->items[itemCount] > (void*)requestEnd) 131 RETURN_ERROR(B_BAD_VALUE); 132 133 // adjust the item name pointers and check their validity 134 addr_t nameDelta = (addr_t)fRequest - (addr_t)userRequest; 135 for (uint32 i = 0; i < itemCount; i++) { 136 PackageFSActivationChangeItem& item = fRequest->items[i]; 137 item.name += nameDelta; 138 if (item.name < (char*)fRequest || item.name >= requestEnd) 139 RETURN_ERROR(B_BAD_VALUE); 140 size_t maxNameSize = requestEnd - item.name; 141 if (strnlen(item.name, maxNameSize) == maxNameSize) 142 RETURN_ERROR(B_BAD_VALUE); 143 } 144 145 return B_OK; 146 } 147 148 uint32 CountItems() const 149 { 150 return fRequest->itemCount; 151 } 152 153 PackageFSActivationChangeItem* ItemAt(uint32 index) const 154 { 155 return index < CountItems() ? &fRequest->items[index] : NULL; 156 } 157 158 private: 159 PackageFSActivationChangeRequest* fRequest; 160 size_t fRequestSize; 161 }; 162 163 164 // #pragma mark - Volume 165 166 167 Volume::Volume(fs_volume* fsVolume) 168 : 169 fFSVolume(fsVolume), 170 fRootDirectory(NULL), 171 fPackageFSRoot(NULL), 172 fPackagesDirectory(NULL), 173 fPackagesDirectories(), 174 fPackagesDirectoriesByNodeRef(), 175 fPackageSettings(), 176 fNextNodeID(kRootDirectoryID + 1) 177 { 178 rw_lock_init(&fLock, "packagefs volume"); 179 } 180 181 182 Volume::~Volume() 183 { 184 // remove the packages from the node tree 185 { 186 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 187 VolumeWriteLocker volumeLocker(this); 188 for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator(); 189 Package* package = it.Next();) { 190 _RemovePackageContent(package, NULL, false); 191 } 192 } 193 194 // delete the packages 195 _RemoveAllPackages(); 196 197 // delete all indices 198 Index* index = fIndices.Clear(true); 199 while (index != NULL) { 200 Index* next = index->IndexHashLink(); 201 delete index; 202 index = next; 203 } 204 205 // remove all nodes from the ID hash table 206 Node* node = fNodes.Clear(true); 207 while (node != NULL) { 208 Node* next = node->IDHashTableNext(); 209 node->ReleaseReference(); 210 node = next; 211 } 212 213 if (fPackageFSRoot != NULL) { 214 if (this == fPackageFSRoot->SystemVolume()) 215 _RemovePackageLinksDirectory(); 216 217 fPackageFSRoot->UnregisterVolume(this); 218 } 219 220 if (fRootDirectory != NULL) 221 fRootDirectory->ReleaseReference(); 222 223 while (PackagesDirectory* directory = fPackagesDirectories.RemoveHead()) 224 directory->ReleaseReference(); 225 226 rw_lock_destroy(&fLock); 227 } 228 229 230 status_t 231 Volume::Mount(const char* parameterString) 232 { 233 // init the hash tables 234 status_t error = fPackagesDirectoriesByNodeRef.Init(); 235 if (error != B_OK) 236 RETURN_ERROR(error); 237 238 error = fNodes.Init(); 239 if (error != B_OK) 240 RETURN_ERROR(error); 241 242 error = fNodeListeners.Init(); 243 if (error != B_OK) 244 RETURN_ERROR(error); 245 246 error = fPackages.Init(); 247 if (error != B_OK) 248 RETURN_ERROR(error); 249 250 error = fIndices.Init(); 251 if (error != B_OK) 252 RETURN_ERROR(error); 253 254 // create the name index 255 { 256 NameIndex* index = new(std::nothrow) NameIndex; 257 if (index == NULL) 258 RETURN_ERROR(B_NO_MEMORY); 259 260 error = index->Init(this); 261 if (error != B_OK) { 262 delete index; 263 RETURN_ERROR(error); 264 } 265 266 fIndices.Insert(index); 267 } 268 269 // create the size index 270 { 271 SizeIndex* index = new(std::nothrow) SizeIndex; 272 if (index == NULL) 273 RETURN_ERROR(B_NO_MEMORY); 274 275 error = index->Init(this); 276 if (error != B_OK) { 277 delete index; 278 RETURN_ERROR(error); 279 } 280 281 fIndices.Insert(index); 282 } 283 284 // create the last modified index 285 { 286 LastModifiedIndex* index = new(std::nothrow) LastModifiedIndex; 287 if (index == NULL) 288 RETURN_ERROR(B_NO_MEMORY); 289 290 error = index->Init(this); 291 if (error != B_OK) { 292 delete index; 293 RETURN_ERROR(error); 294 } 295 296 fIndices.Insert(index); 297 } 298 299 // create a BEOS:APP_SIG index 300 { 301 AttributeIndex* index = new(std::nothrow) AttributeIndex; 302 if (index == NULL) 303 RETURN_ERROR(B_NO_MEMORY); 304 305 error = index->Init(this, "BEOS:APP_SIG", B_MIME_STRING_TYPE, 0); 306 if (error != B_OK) { 307 delete index; 308 RETURN_ERROR(error); 309 } 310 311 fIndices.Insert(index); 312 } 313 314 // get the mount parameters 315 const char* packages = NULL; 316 const char* volumeName = NULL; 317 const char* mountType = NULL; 318 const char* shineThrough = NULL; 319 const char* packagesState = NULL; 320 321 DriverSettingsUnloader parameterHandle( 322 parse_driver_settings_string(parameterString)); 323 if (parameterHandle.IsSet()) { 324 packages = get_driver_parameter(parameterHandle.Get(), "packages", 325 NULL, NULL); 326 volumeName = get_driver_parameter(parameterHandle.Get(), "volume-name", 327 NULL, NULL); 328 mountType = get_driver_parameter(parameterHandle.Get(), "type", NULL, 329 NULL); 330 shineThrough = get_driver_parameter(parameterHandle.Get(), 331 "shine-through", NULL, NULL); 332 packagesState = get_driver_parameter(parameterHandle.Get(), "state", 333 NULL, NULL); 334 } 335 336 if (packages != NULL && packages[0] == '\0') { 337 FATAL("invalid package folder ('packages' parameter)!\n"); 338 RETURN_ERROR(B_BAD_VALUE); 339 } 340 341 error = _InitMountType(mountType); 342 if (error != B_OK) { 343 FATAL("invalid mount type: \"%s\"\n", mountType); 344 RETURN_ERROR(error); 345 } 346 347 // get our mount point 348 error = vfs_get_mount_point(fFSVolume->id, &fMountPoint.deviceID, 349 &fMountPoint.nodeID); 350 if (error != B_OK) 351 RETURN_ERROR(error); 352 353 // load package settings 354 error = fPackageSettings.Load(fMountPoint.deviceID, fMountPoint.nodeID, 355 fMountType); 356 // abort only in case of serious issues (memory shortage) 357 if (error == B_NO_MEMORY) 358 RETURN_ERROR(error); 359 360 // create package domain 361 fPackagesDirectory = new(std::nothrow) PackagesDirectory; 362 if (fPackagesDirectory == NULL) 363 RETURN_ERROR(B_NO_MEMORY); 364 fPackagesDirectories.Add(fPackagesDirectory); 365 fPackagesDirectoriesByNodeRef.Insert(fPackagesDirectory); 366 367 struct stat st; 368 error = fPackagesDirectory->Init(packages, fMountPoint.deviceID, 369 fMountPoint.nodeID, st); 370 if (error != B_OK) 371 RETURN_ERROR(error); 372 373 // If a packages state has been specified, load the needed states. 374 if (packagesState != NULL) { 375 error = _LoadOldPackagesStates(packagesState); 376 if (error != B_OK) 377 RETURN_ERROR(error); 378 } 379 380 // If no volume name is given, infer it from the mount type. 381 if (volumeName == NULL) { 382 switch (fMountType) { 383 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 384 volumeName = "system"; 385 break; 386 case PACKAGE_FS_MOUNT_TYPE_HOME: 387 volumeName = "config"; 388 break; 389 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 390 default: 391 volumeName = "Package FS"; 392 break; 393 } 394 } 395 396 String volumeNameString; 397 if (!volumeNameString.SetTo(volumeName)) 398 RETURN_ERROR(B_NO_MEMORY); 399 400 // create the root node 401 fRootDirectory 402 = new(std::nothrow) ::RootDirectory(kRootDirectoryID, st.st_mtim); 403 if (fRootDirectory == NULL) 404 RETURN_ERROR(B_NO_MEMORY); 405 fRootDirectory->Init(NULL, volumeNameString); 406 fNodes.Insert(fRootDirectory); 407 fRootDirectory->AcquireReference(); 408 // one reference for the table 409 410 // register with packagefs root 411 error = ::PackageFSRoot::RegisterVolume(this); 412 if (error != B_OK) 413 RETURN_ERROR(error); 414 415 if (this == fPackageFSRoot->SystemVolume()) { 416 error = _AddPackageLinksDirectory(); 417 if (error != B_OK) 418 RETURN_ERROR(error); 419 } 420 421 // create shine-through directories 422 error = _CreateShineThroughDirectories(shineThrough); 423 if (error != B_OK) 424 RETURN_ERROR(error); 425 426 // add initial packages 427 error = _AddInitialPackages(); 428 if (error != B_OK) 429 RETURN_ERROR(error); 430 431 // publish the root node 432 fRootDirectory->AcquireReference(); 433 error = PublishVNode(fRootDirectory); 434 if (error != B_OK) { 435 fRootDirectory->ReleaseReference(); 436 RETURN_ERROR(error); 437 } 438 439 // bind and publish the shine-through directories 440 error = _PublishShineThroughDirectories(); 441 if (error != B_OK) 442 RETURN_ERROR(error); 443 444 StringPool::DumpUsageStatistics(); 445 446 return B_OK; 447 } 448 449 450 void 451 Volume::Unmount() 452 { 453 } 454 455 456 status_t 457 Volume::IOCtl(Node* node, uint32 operation, void* buffer, size_t size) 458 { 459 switch (operation) { 460 case PACKAGE_FS_OPERATION_GET_VOLUME_INFO: 461 { 462 if (size < sizeof(PackageFSVolumeInfo)) 463 RETURN_ERROR(B_BAD_VALUE); 464 465 PackageFSVolumeInfo* userVolumeInfo 466 = (PackageFSVolumeInfo*)buffer; 467 468 VolumeReadLocker volumeReadLocker(this); 469 470 PackageFSVolumeInfo volumeInfo; 471 volumeInfo.mountType = fMountType; 472 volumeInfo.rootDeviceID = fPackageFSRoot->DeviceID(); 473 volumeInfo.rootDirectoryID = fPackageFSRoot->NodeID(); 474 volumeInfo.packagesDirectoryCount = fPackagesDirectories.Count(); 475 476 status_t error = user_memcpy(userVolumeInfo, &volumeInfo, 477 sizeof(volumeInfo)); 478 if (error != B_OK) 479 RETURN_ERROR(error); 480 481 uint32 directoryIndex = 0; 482 for (PackagesDirectoryList::Iterator it 483 = fPackagesDirectories.GetIterator(); 484 PackagesDirectory* directory = it.Next(); 485 directoryIndex++) { 486 PackageFSDirectoryInfo info; 487 info.deviceID = directory->DeviceID(); 488 info.nodeID = directory->NodeID(); 489 490 PackageFSDirectoryInfo* userInfo 491 = userVolumeInfo->packagesDirectoryInfos + directoryIndex; 492 if (addr_t(userInfo + 1) > (addr_t)buffer + size) 493 break; 494 495 if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK) 496 return B_BAD_ADDRESS; 497 } 498 499 return B_OK; 500 } 501 502 case PACKAGE_FS_OPERATION_GET_PACKAGE_INFOS: 503 { 504 if (size < sizeof(PackageFSGetPackageInfosRequest)) 505 RETURN_ERROR(B_BAD_VALUE); 506 507 PackageFSGetPackageInfosRequest* request 508 = (PackageFSGetPackageInfosRequest*)buffer; 509 510 VolumeReadLocker volumeReadLocker(this); 511 512 addr_t bufferEnd = (addr_t)buffer + size; 513 uint32 packageCount = fPackages.CountElements(); 514 char* nameBuffer = (char*)(request->infos + packageCount); 515 516 uint32 packageIndex = 0; 517 for (PackageFileNameHashTable::Iterator it 518 = fPackages.GetIterator(); it.HasNext(); 519 packageIndex++) { 520 Package* package = it.Next(); 521 PackageFSPackageInfo info; 522 info.packageDeviceID = package->DeviceID(); 523 info.packageNodeID = package->NodeID(); 524 PackagesDirectory* directory = package->Directory(); 525 info.directoryDeviceID = directory->DeviceID(); 526 info.directoryNodeID = directory->NodeID(); 527 info.name = nameBuffer; 528 529 PackageFSPackageInfo* userInfo = request->infos + packageIndex; 530 if (addr_t(userInfo + 1) <= bufferEnd) { 531 if (user_memcpy(userInfo, &info, sizeof(info)) != B_OK) 532 return B_BAD_ADDRESS; 533 } 534 535 const char* name = package->FileName(); 536 size_t nameSize = strlen(name) + 1; 537 char* nameEnd = nameBuffer + nameSize; 538 if ((addr_t)nameEnd <= bufferEnd) { 539 if (user_memcpy(nameBuffer, name, nameSize) != B_OK) 540 return B_BAD_ADDRESS; 541 } 542 nameBuffer = nameEnd; 543 } 544 545 PackageFSGetPackageInfosRequest header; 546 header.bufferSize = nameBuffer - (char*)request; 547 header.packageCount = packageCount; 548 size_t headerSize = (char*)&request->infos - (char*)request; 549 RETURN_ERROR(user_memcpy(request, &header, headerSize)); 550 } 551 552 case PACKAGE_FS_OPERATION_CHANGE_ACTIVATION: 553 { 554 ActivationChangeRequest request; 555 status_t error = request.Init(buffer, size); 556 if (error != B_OK) 557 RETURN_ERROR(B_BAD_VALUE); 558 559 return _ChangeActivation(request); 560 } 561 562 default: 563 return B_BAD_VALUE; 564 } 565 } 566 567 568 void 569 Volume::AddNodeListener(NodeListener* listener, Node* node) 570 { 571 ASSERT(!listener->IsListening()); 572 573 listener->StartedListening(node); 574 575 if (NodeListener* list = fNodeListeners.Lookup(node)) 576 list->AddNodeListener(listener); 577 else 578 fNodeListeners.Insert(listener); 579 } 580 581 582 void 583 Volume::RemoveNodeListener(NodeListener* listener) 584 { 585 ASSERT(listener->IsListening()); 586 587 Node* node = listener->ListenedNode(); 588 589 if (NodeListener* next = listener->RemoveNodeListener()) { 590 // list not empty yet -- if we removed the head, add a new head to the 591 // hash table 592 NodeListener* list = fNodeListeners.Lookup(node); 593 if (list == listener) { 594 fNodeListeners.Remove(listener); 595 fNodeListeners.Insert(next); 596 } 597 } else 598 fNodeListeners.Remove(listener); 599 600 listener->StoppedListening(); 601 } 602 603 604 void 605 Volume::AddQuery(Query* query) 606 { 607 fQueries.Add(query); 608 } 609 610 611 void 612 Volume::RemoveQuery(Query* query) 613 { 614 fQueries.Remove(query); 615 } 616 617 618 void 619 Volume::UpdateLiveQueries(Node* node, const char* attribute, int32 type, 620 const void* oldKey, size_t oldLength, const void* newKey, 621 size_t newLength) 622 { 623 for (QueryList::Iterator it = fQueries.GetIterator(); 624 Query* query = it.Next();) { 625 query->LiveUpdate(node, attribute, type, oldKey, oldLength, newKey, 626 newLength); 627 } 628 } 629 630 631 status_t 632 Volume::GetVNode(ino_t nodeID, Node*& _node) 633 { 634 return get_vnode(fFSVolume, nodeID, (void**)&_node); 635 } 636 637 638 status_t 639 Volume::PutVNode(ino_t nodeID) 640 { 641 return put_vnode(fFSVolume, nodeID); 642 } 643 644 645 status_t 646 Volume::RemoveVNode(ino_t nodeID) 647 { 648 return remove_vnode(fFSVolume, nodeID); 649 } 650 651 652 status_t 653 Volume::PublishVNode(Node* node) 654 { 655 return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps, 656 node->Mode() & S_IFMT, 0); 657 } 658 659 660 void 661 Volume::PackageLinkNodeAdded(Node* node) 662 { 663 _AddPackageLinksNode(node); 664 665 notify_entry_created(ID(), node->Parent()->ID(), node->Name(), node->ID()); 666 _NotifyNodeAdded(node); 667 } 668 669 670 void 671 Volume::PackageLinkNodeRemoved(Node* node) 672 { 673 _RemovePackageLinksNode(node); 674 675 notify_entry_removed(ID(), node->Parent()->ID(), node->Name(), node->ID()); 676 _NotifyNodeRemoved(node); 677 } 678 679 680 void 681 Volume::PackageLinkNodeChanged(Node* node, uint32 statFields, 682 const OldNodeAttributes& oldAttributes) 683 { 684 Directory* parent = node->Parent(); 685 notify_stat_changed(ID(), parent != NULL ? parent->ID() : -1, node->ID(), 686 statFields); 687 _NotifyNodeChanged(node, statFields, oldAttributes); 688 } 689 690 691 status_t 692 Volume::_LoadOldPackagesStates(const char* packagesState) 693 { 694 // open and stat the admininistrative dir 695 int fd = openat(fPackagesDirectory->DirectoryFD(), 696 kAdministrativeDirectoryName, O_RDONLY); 697 if (fd < 0) { 698 ERROR("Failed to open administrative directory: %s\n", strerror(errno)); 699 RETURN_ERROR(errno); 700 } 701 702 struct stat adminDirStat; 703 if (fstat(fd, &adminDirStat) < 0) { 704 ERROR("Failed to fstat() administrative directory: %s\n", 705 strerror(errno)); 706 RETURN_ERROR(errno); 707 } 708 709 // iterate through the "administrative" dir 710 DirCloser dir(fdopendir(fd)); 711 if (!dir.IsSet()) { 712 ERROR("Failed to open administrative directory: %s\n", strerror(errno)); 713 RETURN_ERROR(errno); 714 } 715 716 while (dirent* entry = readdir(dir.Get())) { 717 if (strncmp(entry->d_name, "state_", 6) != 0 718 || strcmp(entry->d_name, packagesState) < 0) { 719 continue; 720 } 721 722 PackagesDirectory* packagesDirectory 723 = new(std::nothrow) PackagesDirectory; 724 status_t error = packagesDirectory->InitOldState(adminDirStat.st_dev, 725 adminDirStat.st_ino, entry->d_name); 726 if (error != B_OK) { 727 delete packagesDirectory; 728 continue; 729 } 730 731 fPackagesDirectories.Add(packagesDirectory); 732 fPackagesDirectoriesByNodeRef.Insert(packagesDirectory); 733 734 INFORM("added old packages dir state \"%s\"\n", 735 packagesDirectory->StateName().Data()); 736 } 737 738 // sort the packages directories by state age 739 fPackagesDirectories.Sort(&PackagesDirectory::IsNewer); 740 741 return B_OK; 742 } 743 744 745 status_t 746 Volume::_AddInitialPackages() 747 { 748 PackagesDirectory* packagesDirectory = fPackagesDirectories.Last(); 749 INFORM("Adding packages from \"%s\"\n", packagesDirectory->Path()); 750 751 // try reading the activation file of the oldest state 752 status_t error = _AddInitialPackagesFromActivationFile(packagesDirectory); 753 if (error != B_OK && packagesDirectory != fPackagesDirectory) { 754 WARN("Loading packages from old state \"%s\" failed. Loading packages " 755 "from latest state.\n", packagesDirectory->StateName().Data()); 756 757 // remove all packages already added 758 { 759 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 760 VolumeWriteLocker volumeLocker(this); 761 _RemoveAllPackages(); 762 } 763 764 // remove the old states 765 while (fPackagesDirectories.Last() != fPackagesDirectory) 766 fPackagesDirectories.RemoveTail()->ReleaseReference(); 767 768 // try reading the activation file of the latest state 769 packagesDirectory = fPackagesDirectory; 770 error = _AddInitialPackagesFromActivationFile(packagesDirectory); 771 } 772 773 if (error != B_OK) { 774 INFORM("Loading packages from activation file failed. Loading all " 775 "packages in packages directory.\n"); 776 777 // remove all packages already added 778 { 779 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 780 VolumeWriteLocker volumeLocker(this); 781 _RemoveAllPackages(); 782 } 783 784 // read the whole directory 785 error = _AddInitialPackagesFromDirectory(); 786 if (error != B_OK) 787 RETURN_ERROR(error); 788 } 789 790 // add the packages to the node tree 791 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 792 VolumeWriteLocker volumeLocker(this); 793 for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator(); 794 Package* package = it.Next();) { 795 error = _AddPackageContent(package, false); 796 if (error != B_OK) { 797 for (it.Rewind(); Package* activePackage = it.Next();) { 798 if (activePackage == package) 799 break; 800 _RemovePackageContent(activePackage, NULL, false); 801 } 802 RETURN_ERROR(error); 803 } 804 } 805 806 return B_OK; 807 } 808 809 810 status_t 811 Volume::_AddInitialPackagesFromActivationFile( 812 PackagesDirectory* packagesDirectory) 813 { 814 // try reading the activation file 815 FileDescriptorCloser fd(openat(packagesDirectory->DirectoryFD(), 816 packagesDirectory == fPackagesDirectory 817 ? kActivationFilePath : kActivationFileName, 818 O_RDONLY)); 819 if (!fd.IsSet()) { 820 INFORM("Failed to open packages activation file: %s\n", 821 strerror(errno)); 822 RETURN_ERROR(errno); 823 } 824 825 // read the whole file into memory to simplify things 826 struct stat st; 827 if (fstat(fd.Get(), &st) != 0) { 828 ERROR("Failed to stat packages activation file: %s\n", 829 strerror(errno)); 830 RETURN_ERROR(errno); 831 } 832 833 if (st.st_size > (off_t)kMaxActivationFileSize) { 834 ERROR("The packages activation file is too big.\n"); 835 RETURN_ERROR(B_BAD_DATA); 836 } 837 838 char* fileContent = (char*)malloc(st.st_size + 1); 839 if (fileContent == NULL) 840 RETURN_ERROR(B_NO_MEMORY); 841 MemoryDeleter fileContentDeleter(fileContent); 842 843 ssize_t bytesRead = read(fd.Get(), fileContent, st.st_size); 844 if (bytesRead < 0) { 845 ERROR("Failed to read packages activation file: %s\n", strerror(errno)); 846 RETURN_ERROR(errno); 847 } 848 849 if (bytesRead != st.st_size) { 850 ERROR("Failed to read whole packages activation file\n"); 851 RETURN_ERROR(B_ERROR); 852 } 853 854 // null-terminate to simplify parsing 855 fileContent[st.st_size] = '\0'; 856 857 // parse the file and add the respective packages 858 const char* packageName = fileContent; 859 char* const fileContentEnd = fileContent + st.st_size; 860 while (packageName < fileContentEnd) { 861 char* packageNameEnd = strchr(packageName, '\n'); 862 if (packageNameEnd == NULL) 863 packageNameEnd = fileContentEnd; 864 865 // skip empty lines 866 if (packageName == packageNameEnd) { 867 packageName++; 868 continue; 869 } 870 *packageNameEnd = '\0'; 871 872 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) { 873 ERROR("Invalid packages activation file content.\n"); 874 RETURN_ERROR(B_BAD_DATA); 875 } 876 877 status_t error = _LoadAndAddInitialPackage(packagesDirectory, 878 packageName); 879 if (error != B_OK) 880 RETURN_ERROR(error); 881 882 packageName = packageNameEnd + 1; 883 } 884 885 return B_OK; 886 } 887 888 889 status_t 890 Volume::_AddInitialPackagesFromDirectory() 891 { 892 // iterate through the dir and create packages 893 int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY); 894 if (fd < 0) { 895 ERROR("Failed to open packages directory: %s\n", strerror(errno)); 896 RETURN_ERROR(errno); 897 } 898 899 DirCloser dir(fdopendir(fd)); 900 if (!dir.IsSet()) { 901 ERROR("Failed to open packages directory \"%s\": %s\n", 902 fPackagesDirectory->Path(), strerror(errno)); 903 RETURN_ERROR(errno); 904 } 905 906 while (dirent* entry = readdir(dir.Get())) { 907 // skip "." and ".." 908 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) 909 continue; 910 911 // also skip any entry without a ".hpkg" extension 912 size_t nameLength = strlen(entry->d_name); 913 if (nameLength < 5 914 || memcmp(entry->d_name + nameLength - 5, ".hpkg", 5) != 0) { 915 continue; 916 } 917 918 _LoadAndAddInitialPackage(fPackagesDirectory, entry->d_name); 919 } 920 921 return B_OK; 922 } 923 924 925 status_t 926 Volume::_LoadAndAddInitialPackage(PackagesDirectory* packagesDirectory, 927 const char* name) 928 { 929 Package* package; 930 status_t error = _LoadPackage(packagesDirectory, name, package); 931 if (error != B_OK) { 932 ERROR("Failed to load package \"%s\": %s\n", name, strerror(error)); 933 RETURN_ERROR(error); 934 } 935 BReference<Package> packageReference(package, true); 936 937 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 938 VolumeWriteLocker volumeLocker(this); 939 _AddPackage(package); 940 941 return B_OK; 942 } 943 944 945 inline void 946 Volume::_AddPackage(Package* package) 947 { 948 fPackages.Insert(package); 949 package->AcquireReference(); 950 } 951 952 953 inline void 954 Volume::_RemovePackage(Package* package) 955 { 956 fPackages.Remove(package); 957 package->ReleaseReference(); 958 } 959 960 961 void 962 Volume::_RemoveAllPackages() 963 { 964 Package* package = fPackages.Clear(true); 965 while (package != NULL) { 966 Package* next = package->FileNameHashTableNext(); 967 package->ReleaseReference(); 968 package = next; 969 } 970 } 971 972 973 inline Package* 974 Volume::_FindPackage(const char* fileName) const 975 { 976 return fPackages.Lookup(fileName); 977 } 978 979 980 status_t 981 Volume::_AddPackageContent(Package* package, bool notify) 982 { 983 // Open the package. We don't need the FD here, but this is an optimization. 984 // The attribute indices may want to read the package nodes' attributes and 985 // the package file would be opened and closed for each attribute instance. 986 // Since Package keeps and shares the FD as long as at least one party has 987 // the package open, we prevent that. 988 int fd = package->Open(); 989 if (fd < 0) 990 RETURN_ERROR(fd); 991 PackageCloser packageCloser(package); 992 993 status_t error = fPackageFSRoot->AddPackage(package); 994 if (error != B_OK) 995 RETURN_ERROR(error); 996 997 for (PackageNodeList::Iterator it = package->Nodes().GetIterator(); 998 PackageNode* node = it.Next();) { 999 // skip over ".PackageInfo" file, it isn't part of the package content 1000 if (strcmp(node->Name(), 1001 BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) { 1002 continue; 1003 } 1004 error = _AddPackageContentRootNode(package, node, notify); 1005 if (error != B_OK) { 1006 _RemovePackageContent(package, node, notify); 1007 RETURN_ERROR(error); 1008 } 1009 } 1010 1011 return B_OK; 1012 } 1013 1014 1015 void 1016 Volume::_RemovePackageContent(Package* package, PackageNode* endNode, 1017 bool notify) 1018 { 1019 PackageNode* node = package->Nodes().Head(); 1020 while (node != NULL) { 1021 if (node == endNode) 1022 break; 1023 1024 PackageNode* nextNode = package->Nodes().GetNext(node); 1025 1026 // skip over ".PackageInfo" file, it isn't part of the package content 1027 if (strcmp(node->Name(), 1028 BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) != 0) { 1029 _RemovePackageContentRootNode(package, node, NULL, notify); 1030 } 1031 1032 node = nextNode; 1033 } 1034 1035 fPackageFSRoot->RemovePackage(package); 1036 } 1037 1038 1039 /*! This method recursively iterates through the descendents of the given 1040 package root node and adds all package nodes to the node tree in 1041 pre-order. 1042 Due to limited kernel stack space we avoid deep recursive function calls 1043 and rather use the package node stack implied by the tree. 1044 */ 1045 status_t 1046 Volume::_AddPackageContentRootNode(Package* package, 1047 PackageNode* rootPackageNode, bool notify) 1048 { 1049 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); 1050 1051 PackageNode* packageNode = rootPackageNode; 1052 Directory* directory = fRootDirectory; 1053 directory->WriteLock(); 1054 1055 do { 1056 Node* node; 1057 status_t error = _AddPackageNode(directory, packageNode, notify, node); 1058 // returns B_OK with a NULL node, when skipping the node 1059 if (error != B_OK) { 1060 // unlock all directories 1061 while (directory != NULL) { 1062 directory->WriteUnlock(); 1063 directory = directory->Parent(); 1064 } 1065 1066 // remove the added package nodes 1067 _RemovePackageContentRootNode(package, rootPackageNode, packageNode, 1068 notify); 1069 RETURN_ERROR(error); 1070 } 1071 1072 // recurse into directory, unless we're supposed to skip the node 1073 if (node != NULL) { 1074 if (PackageDirectory* packageDirectory 1075 = dynamic_cast<PackageDirectory*>(packageNode)) { 1076 if (packageDirectory->FirstChild() != NULL) { 1077 directory = dynamic_cast<Directory*>(node); 1078 packageNode = packageDirectory->FirstChild(); 1079 directory->WriteLock(); 1080 continue; 1081 } 1082 } 1083 } 1084 1085 // continue with the next available (ancestors's) sibling 1086 do { 1087 PackageDirectory* packageDirectory = packageNode->Parent(); 1088 PackageNode* sibling = packageDirectory != NULL 1089 ? packageDirectory->NextChild(packageNode) : NULL; 1090 1091 if (sibling != NULL) { 1092 packageNode = sibling; 1093 break; 1094 } 1095 1096 // no more siblings -- go back up the tree 1097 packageNode = packageDirectory; 1098 directory->WriteUnlock(); 1099 directory = directory->Parent(); 1100 // the parent is still locked, so this is safe 1101 } while (packageNode != NULL); 1102 } while (packageNode != NULL); 1103 1104 ASSERT(directory == NULL); 1105 1106 return B_OK; 1107 } 1108 1109 1110 /*! Recursively iterates through the descendents of the given package root node 1111 and removes all package nodes from the node tree in post-order, until 1112 encountering \a endPackageNode (if non-null). 1113 Due to limited kernel stack space we avoid deep recursive function calls 1114 and rather use the package node stack implied by the tree. 1115 */ 1116 void 1117 Volume::_RemovePackageContentRootNode(Package* package, 1118 PackageNode* rootPackageNode, PackageNode* endPackageNode, bool notify) 1119 { 1120 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); 1121 1122 PackageNode* packageNode = rootPackageNode; 1123 Directory* directory = fRootDirectory; 1124 directory->WriteLock(); 1125 1126 do { 1127 if (packageNode == endPackageNode) { 1128 // unlock all directories 1129 while (directory != NULL) { 1130 directory->WriteUnlock(); 1131 directory = directory->Parent(); 1132 } 1133 break; 1134 } 1135 1136 // recurse into directory 1137 if (PackageDirectory* packageDirectory 1138 = dynamic_cast<PackageDirectory*>(packageNode)) { 1139 if (packageDirectory->FirstChild() != NULL) { 1140 if (Directory* childDirectory = dynamic_cast<Directory*>( 1141 directory->FindChild(packageNode->Name()))) { 1142 directory = childDirectory; 1143 packageNode = packageDirectory->FirstChild(); 1144 directory->WriteLock(); 1145 continue; 1146 } 1147 } 1148 } 1149 1150 // continue with the next available (ancestors's) sibling 1151 do { 1152 PackageDirectory* packageDirectory = packageNode->Parent(); 1153 PackageNode* sibling = packageDirectory != NULL 1154 ? packageDirectory->NextChild(packageNode) : NULL; 1155 1156 // we're done with the node -- remove it 1157 _RemovePackageNode(directory, packageNode, 1158 directory->FindChild(packageNode->Name()), notify); 1159 1160 if (sibling != NULL) { 1161 packageNode = sibling; 1162 break; 1163 } 1164 1165 // no more siblings -- go back up the tree 1166 packageNode = packageDirectory; 1167 directory->WriteUnlock(); 1168 directory = directory->Parent(); 1169 // the parent is still locked, so this is safe 1170 } while (packageNode != NULL/* && packageNode != rootPackageNode*/); 1171 } while (packageNode != NULL/* && packageNode != rootPackageNode*/); 1172 1173 ASSERT(directory == NULL); 1174 } 1175 1176 1177 status_t 1178 Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode, 1179 bool notify, Node*& _node) 1180 { 1181 bool newNode = false; 1182 UnpackingNode* unpackingNode; 1183 Node* node = directory->FindChild(packageNode->Name()); 1184 PackageNode* oldPackageNode = NULL; 1185 1186 if (node != NULL) { 1187 unpackingNode = dynamic_cast<UnpackingNode*>(node); 1188 if (unpackingNode == NULL) { 1189 _node = NULL; 1190 return B_OK; 1191 } 1192 oldPackageNode = unpackingNode->GetPackageNode(); 1193 } else { 1194 status_t error = _CreateUnpackingNode(packageNode->Mode(), directory, 1195 packageNode->Name(), unpackingNode); 1196 if (error != B_OK) 1197 RETURN_ERROR(error); 1198 1199 node = unpackingNode->GetNode(); 1200 newNode = true; 1201 } 1202 1203 BReference<Node> nodeReference(node); 1204 NodeWriteLocker nodeWriteLocker(node); 1205 1206 BReference<Node> newNodeReference; 1207 NodeWriteLocker newNodeWriteLocker; 1208 Node* oldNode = NULL; 1209 1210 if (!newNode && !S_ISDIR(node->Mode()) && oldPackageNode != NULL 1211 && unpackingNode->WillBeFirstPackageNode(packageNode)) { 1212 // The package node we're going to add will represent the node, 1213 // replacing the current head package node. Since the node isn't a 1214 // directory, we must make sure that clients having opened or mapped the 1215 // node won't be surprised. So we create a new node and remove the 1216 // current one. 1217 // create a new node and transfer the package nodes to it 1218 UnpackingNode* newUnpackingNode; 1219 status_t error = unpackingNode->CloneTransferPackageNodes( 1220 fNextNodeID++, newUnpackingNode); 1221 if (error != B_OK) 1222 RETURN_ERROR(error); 1223 1224 // remove the old node 1225 _NotifyNodeRemoved(node); 1226 _RemoveNodeAndVNode(node); 1227 oldNode = node; 1228 1229 // add the new node 1230 unpackingNode = newUnpackingNode; 1231 node = unpackingNode->GetNode(); 1232 newNodeReference.SetTo(node); 1233 newNodeWriteLocker.SetTo(node, false); 1234 1235 directory->AddChild(node); 1236 fNodes.Insert(node); 1237 newNode = true; 1238 } 1239 1240 status_t error = unpackingNode->AddPackageNode(packageNode, ID()); 1241 if (error != B_OK) { 1242 // Remove the node, if created before. If the node was created to 1243 // replace the previous node, send out notifications instead. 1244 if (newNode) { 1245 if (oldNode != NULL) { 1246 _NotifyNodeAdded(node); 1247 if (notify) { 1248 notify_entry_removed(ID(), directory->ID(), oldNode->Name(), 1249 oldNode->ID()); 1250 notify_entry_created(ID(), directory->ID(), node->Name(), 1251 node->ID()); 1252 } 1253 } else 1254 _RemoveNode(node); 1255 } 1256 RETURN_ERROR(error); 1257 } 1258 1259 if (newNode) { 1260 _NotifyNodeAdded(node); 1261 } else if (packageNode == unpackingNode->GetPackageNode()) { 1262 _NotifyNodeChanged(node, kAllStatFields, 1263 OldUnpackingNodeAttributes(oldPackageNode)); 1264 } 1265 1266 if (notify) { 1267 if (newNode) { 1268 if (oldNode != NULL) { 1269 notify_entry_removed(ID(), directory->ID(), oldNode->Name(), 1270 oldNode->ID()); 1271 } 1272 notify_entry_created(ID(), directory->ID(), node->Name(), 1273 node->ID()); 1274 } else if (packageNode == unpackingNode->GetPackageNode()) { 1275 // The new package node has become the one representing the node. 1276 // Send stat changed notification for directories and entry 1277 // removed + created notifications for files and symlinks. 1278 notify_stat_changed(ID(), directory->ID(), node->ID(), 1279 kAllStatFields); 1280 // TODO: Actually the attributes might change, too! 1281 } 1282 } 1283 1284 _node = node; 1285 return B_OK; 1286 } 1287 1288 1289 void 1290 Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode, 1291 Node* node, bool notify) 1292 { 1293 UnpackingNode* unpackingNode = dynamic_cast<UnpackingNode*>(node); 1294 if (unpackingNode == NULL) 1295 return; 1296 1297 BReference<Node> nodeReference(node); 1298 NodeWriteLocker nodeWriteLocker(node); 1299 1300 PackageNode* headPackageNode = unpackingNode->GetPackageNode(); 1301 bool nodeRemoved = false; 1302 Node* newNode = NULL; 1303 1304 BReference<Node> newNodeReference; 1305 NodeWriteLocker newNodeWriteLocker; 1306 1307 // If this is the last package node of the node, remove it completely. 1308 if (unpackingNode->IsOnlyPackageNode(packageNode)) { 1309 // Notify before removing the node. Otherwise the indices might not 1310 // find the node anymore. 1311 _NotifyNodeRemoved(node); 1312 1313 unpackingNode->PrepareForRemoval(); 1314 1315 _RemoveNodeAndVNode(node); 1316 nodeRemoved = true; 1317 } else if (packageNode == headPackageNode) { 1318 // The node does at least have one more package node, but the one to be 1319 // removed is the head. Unless it's a directory, we replace the node 1320 // with a completely new one and let the old one die. This is necessary 1321 // to avoid surprises for clients that have opened/mapped the node. 1322 if (S_ISDIR(packageNode->Mode())) { 1323 unpackingNode->RemovePackageNode(packageNode, ID()); 1324 _NotifyNodeChanged(node, kAllStatFields, 1325 OldUnpackingNodeAttributes(headPackageNode)); 1326 } else { 1327 // create a new node and transfer the package nodes to it 1328 UnpackingNode* newUnpackingNode; 1329 status_t error = unpackingNode->CloneTransferPackageNodes( 1330 fNextNodeID++, newUnpackingNode); 1331 if (error == B_OK) { 1332 // remove the package node 1333 newUnpackingNode->RemovePackageNode(packageNode, ID()); 1334 1335 // remove the old node 1336 _NotifyNodeRemoved(node); 1337 _RemoveNodeAndVNode(node); 1338 1339 // add the new node 1340 newNode = newUnpackingNode->GetNode(); 1341 newNodeReference.SetTo(newNode); 1342 newNodeWriteLocker.SetTo(newNode, false); 1343 1344 directory->AddChild(newNode); 1345 fNodes.Insert(newNode); 1346 _NotifyNodeAdded(newNode); 1347 } else { 1348 // There's nothing we can do. Remove the node completely. 1349 _NotifyNodeRemoved(node); 1350 1351 unpackingNode->PrepareForRemoval(); 1352 1353 _RemoveNodeAndVNode(node); 1354 nodeRemoved = true; 1355 } 1356 } 1357 } else { 1358 // The package node to remove is not the head of the node. This change 1359 // doesn't have any visible effect. 1360 unpackingNode->RemovePackageNode(packageNode, ID()); 1361 } 1362 1363 if (!notify) 1364 return; 1365 1366 // send notifications 1367 if (nodeRemoved) { 1368 notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID()); 1369 } else if (packageNode == headPackageNode) { 1370 // The removed package node was the one representing the node. 1371 // Send stat changed notification for directories and entry 1372 // removed + created notifications for files and symlinks. 1373 if (S_ISDIR(packageNode->Mode())) { 1374 notify_stat_changed(ID(), directory->ID(), node->ID(), 1375 kAllStatFields); 1376 // TODO: Actually the attributes might change, too! 1377 } else { 1378 notify_entry_removed(ID(), directory->ID(), node->Name(), 1379 node->ID()); 1380 notify_entry_created(ID(), directory->ID(), newNode->Name(), 1381 newNode->ID()); 1382 } 1383 } 1384 } 1385 1386 1387 status_t 1388 Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const String& name, 1389 UnpackingNode*& _node) 1390 { 1391 UnpackingNode* unpackingNode; 1392 if (S_ISREG(mode) || S_ISLNK(mode)) 1393 unpackingNode = new(std::nothrow) UnpackingLeafNode(fNextNodeID++); 1394 else if (S_ISDIR(mode)) 1395 unpackingNode = new(std::nothrow) UnpackingDirectory(fNextNodeID++); 1396 else 1397 RETURN_ERROR(B_UNSUPPORTED); 1398 1399 if (unpackingNode == NULL) 1400 RETURN_ERROR(B_NO_MEMORY); 1401 1402 Node* node = unpackingNode->GetNode(); 1403 BReference<Node> nodeReference(node, true); 1404 1405 status_t error = node->Init(parent, name); 1406 if (error != B_OK) 1407 RETURN_ERROR(error); 1408 1409 parent->AddChild(node); 1410 1411 fNodes.Insert(node); 1412 nodeReference.Detach(); 1413 // we keep the initial node reference for the table 1414 1415 _node = unpackingNode; 1416 return B_OK; 1417 } 1418 1419 1420 void 1421 Volume::_RemoveNode(Node* node) 1422 { 1423 // remove from parent 1424 Directory* parent = node->Parent(); 1425 parent->RemoveChild(node); 1426 1427 // remove from node table 1428 fNodes.Remove(node); 1429 node->ReleaseReference(); 1430 } 1431 1432 1433 void 1434 Volume::_RemoveNodeAndVNode(Node* node) 1435 { 1436 // If the node is known to the VFS, we get the vnode, remove it, and put it, 1437 // so that the VFS will discard it as soon as possible (i.e. now, if no one 1438 // else is using it). 1439 NodeWriteLocker nodeWriteLocker(node); 1440 1441 // Remove the node from its parent and the volume. This makes the node 1442 // inaccessible via the get_vnode() and lookup() hooks. 1443 _RemoveNode(node); 1444 1445 bool getVNode = node->IsKnownToVFS(); 1446 1447 nodeWriteLocker.Unlock(); 1448 1449 // Get a vnode reference, if the node is already known to the VFS. 1450 Node* dummyNode; 1451 if (getVNode && GetVNode(node->ID(), dummyNode) == B_OK) { 1452 // TODO: There still is a race condition here which we can't avoid 1453 // without more help from the VFS. Right after we drop the write 1454 // lock a vnode for the node could be discarded by the VFS. At that 1455 // point another thread trying to get the vnode by ID would create 1456 // a vnode, mark it busy and call our get_vnode() hook. It would 1457 // block since we (i.e. the package loader thread executing this 1458 // method) still have the volume write lock. Our get_vnode() call 1459 // would block, since it finds the vnode marked busy. It times out 1460 // eventually, but until then a good deal of FS operations might 1461 // block as well due to us holding the volume lock and probably 1462 // several node locks as well. A get_vnode*() variant (e.g. 1463 // get_vnode_etc() with flags parameter) that wouldn't block and 1464 // only get the vnode, if already loaded and non-busy, would be 1465 // perfect here. 1466 RemoveVNode(node->ID()); 1467 PutVNode(node->ID()); 1468 } 1469 } 1470 1471 1472 status_t 1473 Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name, 1474 Package*& _package) 1475 { 1476 // Find the package -- check the specified packages directory and iterate 1477 // toward the newer states. 1478 struct stat st; 1479 for (;;) { 1480 if (packagesDirectory == NULL) 1481 return B_ENTRY_NOT_FOUND; 1482 1483 if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) { 1484 // check whether the entry is a file 1485 if (!S_ISREG(st.st_mode)) 1486 return B_BAD_VALUE; 1487 break; 1488 } 1489 1490 packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory); 1491 } 1492 1493 // create a package 1494 Package* package = new(std::nothrow) Package(this, packagesDirectory, 1495 st.st_dev, st.st_ino); 1496 if (package == NULL) 1497 RETURN_ERROR(B_NO_MEMORY); 1498 BReference<Package> packageReference(package, true); 1499 1500 status_t error = package->Init(name); 1501 if (error != B_OK) 1502 return error; 1503 1504 error = package->Load(fPackageSettings); 1505 if (error != B_OK) 1506 return error; 1507 1508 _package = packageReference.Detach(); 1509 return B_OK; 1510 } 1511 1512 1513 status_t 1514 Volume::_ChangeActivation(ActivationChangeRequest& request) 1515 { 1516 uint32 itemCount = request.CountItems(); 1517 if (itemCount == 0) 1518 return B_OK; 1519 1520 // first check the request 1521 int32 newPackageCount = 0; 1522 int32 oldPackageCount = 0; 1523 { 1524 VolumeReadLocker volumeLocker(this); 1525 1526 for (uint32 i = 0; i < itemCount; i++) { 1527 PackageFSActivationChangeItem* item = request.ItemAt(i); 1528 if (item->parentDeviceID != fPackagesDirectory->DeviceID() 1529 || item->parentDirectoryID != fPackagesDirectory->NodeID()) { 1530 ERROR("Volume::_ChangeActivation(): mismatching packages " 1531 "directory\n"); 1532 RETURN_ERROR(B_MISMATCHED_VALUES); 1533 } 1534 1535 Package* package = _FindPackage(item->name); 1536 // TODO: We should better look up the package by node_ref! 1537 if (item->type == PACKAGE_FS_ACTIVATE_PACKAGE) { 1538 if (package != NULL) { 1539 ERROR("Volume::_ChangeActivation(): package to activate " 1540 "already activated: \"%s\"\n", item->name); 1541 RETURN_ERROR(B_NAME_IN_USE); 1542 } 1543 newPackageCount++; 1544 } else if (item->type == PACKAGE_FS_DEACTIVATE_PACKAGE) { 1545 if (package == NULL) { 1546 ERROR("Volume::_ChangeActivation(): package to deactivate " 1547 "not found: \"%s\"\n", item->name); 1548 RETURN_ERROR(B_NAME_NOT_FOUND); 1549 } 1550 oldPackageCount++; 1551 } else if (item->type == PACKAGE_FS_REACTIVATE_PACKAGE) { 1552 if (package == NULL) { 1553 ERROR("Volume::_ChangeActivation(): package to reactivate " 1554 "not found: \"%s\"\n", item->name); 1555 RETURN_ERROR(B_NAME_NOT_FOUND); 1556 } 1557 oldPackageCount++; 1558 newPackageCount++; 1559 } else 1560 RETURN_ERROR(B_BAD_VALUE); 1561 } 1562 } 1563 1564 INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32 1565 " old packages\n", newPackageCount, oldPackageCount); 1566 1567 // Things look good so far -- allocate reference arrays for the packages to 1568 // add and remove. 1569 BReference<Package>* newPackageReferences 1570 = new(std::nothrow) BReference<Package>[newPackageCount]; 1571 if (newPackageReferences == NULL) 1572 RETURN_ERROR(B_NO_MEMORY); 1573 ArrayDeleter<BReference<Package> > newPackageReferencesDeleter( 1574 newPackageReferences); 1575 1576 BReference<Package>* oldPackageReferences 1577 = new(std::nothrow) BReference<Package>[oldPackageCount]; 1578 if (oldPackageReferences == NULL) 1579 RETURN_ERROR(B_NO_MEMORY); 1580 ArrayDeleter<BReference<Package> > oldPackageReferencesDeleter( 1581 oldPackageReferences); 1582 1583 // load all new packages 1584 int32 newPackageIndex = 0; 1585 for (uint32 i = 0; i < itemCount; i++) { 1586 PackageFSActivationChangeItem* item = request.ItemAt(i); 1587 1588 if (item->type != PACKAGE_FS_ACTIVATE_PACKAGE 1589 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) { 1590 continue; 1591 } 1592 1593 Package* package; 1594 status_t error = _LoadPackage(fPackagesDirectory, item->name, package); 1595 if (error != B_OK) { 1596 ERROR("Volume::_ChangeActivation(): failed to load package " 1597 "\"%s\"\n", item->name); 1598 RETURN_ERROR(error); 1599 } 1600 1601 newPackageReferences[newPackageIndex++].SetTo(package, true); 1602 } 1603 1604 // apply the changes 1605 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 1606 VolumeWriteLocker volumeLocker(this); 1607 // TODO: Add a change counter to Volume, so we can easily check whether 1608 // everything is still the same. 1609 1610 // remove the old packages 1611 int32 oldPackageIndex = 0; 1612 for (uint32 i = 0; i < itemCount; i++) { 1613 PackageFSActivationChangeItem* item = request.ItemAt(i); 1614 1615 if (item->type != PACKAGE_FS_DEACTIVATE_PACKAGE 1616 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) { 1617 continue; 1618 } 1619 1620 Package* package = _FindPackage(item->name); 1621 // TODO: We should better look up the package by node_ref! 1622 oldPackageReferences[oldPackageIndex++].SetTo(package); 1623 _RemovePackageContent(package, NULL, true); 1624 _RemovePackage(package); 1625 1626 INFORM("package \"%s\" deactivated\n", package->FileName().Data()); 1627 } 1628 // TODO: Since package removal cannot fail, consider adding the new packages 1629 // first. The reactivation case may make that problematic, since two packages 1630 // with the same name would be active after activating the new one. Check! 1631 1632 // add the new packages 1633 status_t error = B_OK; 1634 for (newPackageIndex = 0; newPackageIndex < newPackageCount; 1635 newPackageIndex++) { 1636 Package* package = newPackageReferences[newPackageIndex]; 1637 _AddPackage(package); 1638 1639 // add the package to the node tree 1640 error = _AddPackageContent(package, true); 1641 if (error != B_OK) { 1642 _RemovePackage(package); 1643 break; 1644 } 1645 INFORM("package \"%s\" activated\n", package->FileName().Data()); 1646 } 1647 1648 // Try to roll back the changes, if an error occurred. 1649 if (error != B_OK) { 1650 for (int32 i = newPackageIndex - 1; i >= 0; i--) { 1651 Package* package = newPackageReferences[i]; 1652 _RemovePackageContent(package, NULL, true); 1653 _RemovePackage(package); 1654 } 1655 1656 for (int32 i = oldPackageCount - 1; i >= 0; i--) { 1657 Package* package = oldPackageReferences[i]; 1658 _AddPackage(package); 1659 1660 if (_AddPackageContent(package, true) != B_OK) { 1661 // nothing we can do here 1662 ERROR("Volume::_ChangeActivation(): failed to roll back " 1663 "deactivation of package \"%s\" after error\n", 1664 package->FileName().Data()); 1665 _RemovePackage(package); 1666 } 1667 } 1668 } 1669 1670 return error; 1671 } 1672 1673 1674 status_t 1675 Volume::_InitMountType(const char* mountType) 1676 { 1677 if (mountType == NULL) 1678 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM; 1679 else if (strcmp(mountType, "system") == 0) 1680 fMountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM; 1681 else if (strcmp(mountType, "home") == 0) 1682 fMountType = PACKAGE_FS_MOUNT_TYPE_HOME; 1683 else if (strcmp(mountType, "custom") == 0) 1684 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM; 1685 else 1686 RETURN_ERROR(B_BAD_VALUE); 1687 1688 return B_OK; 1689 } 1690 1691 1692 status_t 1693 Volume::_CreateShineThroughDirectory(Directory* parent, const char* name, 1694 Directory*& _directory) 1695 { 1696 ShineThroughDirectory* directory = new(std::nothrow) ShineThroughDirectory( 1697 fNextNodeID++); 1698 if (directory == NULL) 1699 RETURN_ERROR(B_NO_MEMORY); 1700 BReference<ShineThroughDirectory> directoryReference(directory, true); 1701 1702 String nameString; 1703 if (!nameString.SetTo(name)) 1704 RETURN_ERROR(B_NO_MEMORY); 1705 1706 status_t error = directory->Init(parent, nameString); 1707 if (error != B_OK) 1708 RETURN_ERROR(error); 1709 1710 parent->AddChild(directory); 1711 1712 fNodes.Insert(directory); 1713 directoryReference.Detach(); 1714 // we keep the initial node reference for the table 1715 1716 _directory = directory; 1717 return B_OK; 1718 } 1719 1720 1721 status_t 1722 Volume::_CreateShineThroughDirectories(const char* shineThroughSetting) 1723 { 1724 // get the directories to map 1725 const char* const* directories = NULL; 1726 1727 if (shineThroughSetting == NULL) { 1728 // nothing specified -- derive from mount type 1729 switch (fMountType) { 1730 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 1731 case PACKAGE_FS_MOUNT_TYPE_HOME: 1732 directories = kShineThroughDirectories; 1733 break; 1734 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 1735 return B_OK; 1736 case PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT: 1737 return B_BAD_VALUE; 1738 } 1739 } else if (strcmp(shineThroughSetting, "system") == 0) 1740 directories = kShineThroughDirectories; 1741 else if (strcmp(shineThroughSetting, "home") == 0) 1742 directories = kShineThroughDirectories; 1743 else if (strcmp(shineThroughSetting, "none") == 0) 1744 directories = NULL; 1745 else 1746 RETURN_ERROR(B_BAD_VALUE); 1747 1748 if (directories == NULL) 1749 return B_OK; 1750 1751 // iterate through the directory list and create the directories 1752 while (const char* directoryName = *(directories++)) { 1753 // create the directory 1754 Directory* directory; 1755 status_t error = _CreateShineThroughDirectory(fRootDirectory, 1756 directoryName, directory); 1757 if (error != B_OK) 1758 RETURN_ERROR(error); 1759 } 1760 1761 return B_OK; 1762 } 1763 1764 1765 status_t 1766 Volume::_PublishShineThroughDirectories() 1767 { 1768 // Iterate through the root directory children and bind the shine-through 1769 // directories to the respective mount point subdirectories. 1770 Node* nextNode; 1771 for (Node* node = fRootDirectory->FirstChild(); node != NULL; 1772 node = nextNode) { 1773 nextNode = fRootDirectory->NextChild(node); 1774 1775 // skip anything but shine-through directories 1776 ShineThroughDirectory* directory 1777 = dynamic_cast<ShineThroughDirectory*>(node); 1778 if (directory == NULL) 1779 continue; 1780 1781 const char* directoryName = directory->Name(); 1782 1783 // look up the mount point subdirectory 1784 struct vnode* vnode; 1785 status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID, 1786 fMountPoint.nodeID, directoryName, &vnode); 1787 if (error != B_OK) { 1788 dprintf("packagefs: Failed to get shine-through directory \"%s\": " 1789 "%s\n", directoryName, strerror(error)); 1790 _RemoveNode(directory); 1791 continue; 1792 } 1793 VnodePutter vnodePutter(vnode); 1794 1795 // stat it 1796 struct stat st; 1797 error = vfs_stat_vnode(vnode, &st); 1798 if (error != B_OK) { 1799 dprintf("packagefs: Failed to stat shine-through directory \"%s\": " 1800 "%s\n", directoryName, strerror(error)); 1801 _RemoveNode(directory); 1802 continue; 1803 } 1804 1805 if (!S_ISDIR(st.st_mode)) { 1806 dprintf("packagefs: Shine-through entry \"%s\" is not a " 1807 "directory\n", directoryName); 1808 _RemoveNode(directory); 1809 continue; 1810 } 1811 1812 // publish the vnode, so the VFS will find it without asking us 1813 directory->AcquireReference(); 1814 error = PublishVNode(directory); 1815 if (error != B_OK) { 1816 directory->ReleaseReference(); 1817 _RemoveNode(directory); 1818 RETURN_ERROR(error); 1819 } 1820 1821 // bind the directory 1822 error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id, 1823 directory->ID()); 1824 1825 PutVNode(directory->ID()); 1826 // release our reference again -- on success 1827 // vfs_bind_mount_directory() got one 1828 1829 if (error != B_OK) 1830 RETURN_ERROR(error); 1831 } 1832 1833 return B_OK; 1834 } 1835 1836 1837 status_t 1838 Volume::_AddPackageLinksDirectory() 1839 { 1840 // called when mounting, so we don't need to lock the volume 1841 1842 PackageLinksDirectory* packageLinksDirectory 1843 = fPackageFSRoot->GetPackageLinksDirectory(); 1844 1845 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1846 NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory); 1847 1848 packageLinksDirectory->SetParent(fRootDirectory); 1849 fRootDirectory->AddChild(packageLinksDirectory); 1850 1851 _AddPackageLinksNode(packageLinksDirectory); 1852 1853 packageLinksDirectory->SetListener(this); 1854 1855 return B_OK; 1856 } 1857 1858 1859 void 1860 Volume::_RemovePackageLinksDirectory() 1861 { 1862 PackageLinksDirectory* packageLinksDirectory 1863 = fPackageFSRoot->GetPackageLinksDirectory(); 1864 1865 VolumeWriteLocker volumeLocker(this); 1866 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1867 NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory); 1868 1869 if (packageLinksDirectory->Parent() == fRootDirectory) { 1870 packageLinksDirectory->SetListener(NULL); 1871 fRootDirectory->RemoveChild(packageLinksDirectory); 1872 packageLinksDirectory->SetParent(NULL); 1873 } 1874 } 1875 1876 1877 void 1878 Volume::_AddPackageLinksNode(Node* node) 1879 { 1880 node->SetID(fNextNodeID++); 1881 1882 fNodes.Insert(node); 1883 node->AcquireReference(); 1884 1885 // If this is a directory, recursively add descendants. The directory tree 1886 // for the package links isn't deep, so we can do recursion. 1887 if (Directory* directory = dynamic_cast<Directory*>(node)) { 1888 for (Node* child = directory->FirstChild(); child != NULL; 1889 child = directory->NextChild(child)) { 1890 NodeWriteLocker childWriteLocker(child); 1891 _AddPackageLinksNode(child); 1892 } 1893 } 1894 } 1895 1896 1897 void 1898 Volume::_RemovePackageLinksNode(Node* node) 1899 { 1900 // If this is a directory, recursively remove descendants. The directory 1901 // tree for the package links isn't deep, so we can do recursion. 1902 if (Directory* directory = dynamic_cast<Directory*>(node)) { 1903 for (Node* child = directory->FirstChild(); child != NULL; 1904 child = directory->NextChild(child)) { 1905 NodeWriteLocker childWriteLocker(child); 1906 _RemovePackageLinksNode(child); 1907 } 1908 } 1909 1910 fNodes.Remove(node); 1911 node->ReleaseReference(); 1912 } 1913 1914 1915 inline Volume* 1916 Volume::_SystemVolumeIfNotSelf() const 1917 { 1918 if (Volume* systemVolume = fPackageFSRoot->SystemVolume()) 1919 return systemVolume == this ? NULL : systemVolume; 1920 return NULL; 1921 } 1922 1923 1924 void 1925 Volume::_NotifyNodeAdded(Node* node) 1926 { 1927 Node* key = node; 1928 1929 for (int i = 0; i < 2; i++) { 1930 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1931 NodeListener* last = listener->PreviousNodeListener(); 1932 1933 while (true) { 1934 NodeListener* next = listener->NextNodeListener(); 1935 1936 listener->NodeAdded(node); 1937 1938 if (listener == last) 1939 break; 1940 1941 listener = next; 1942 } 1943 } 1944 1945 key = NULL; 1946 } 1947 } 1948 1949 1950 void 1951 Volume::_NotifyNodeRemoved(Node* node) 1952 { 1953 Node* key = node; 1954 1955 for (int i = 0; i < 2; i++) { 1956 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1957 NodeListener* last = listener->PreviousNodeListener(); 1958 1959 while (true) { 1960 NodeListener* next = listener->NextNodeListener(); 1961 1962 listener->NodeRemoved(node); 1963 1964 if (listener == last) 1965 break; 1966 1967 listener = next; 1968 } 1969 } 1970 1971 key = NULL; 1972 } 1973 } 1974 1975 1976 void 1977 Volume::_NotifyNodeChanged(Node* node, uint32 statFields, 1978 const OldNodeAttributes& oldAttributes) 1979 { 1980 Node* key = node; 1981 1982 for (int i = 0; i < 2; i++) { 1983 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1984 NodeListener* last = listener->PreviousNodeListener(); 1985 1986 while (true) { 1987 NodeListener* next = listener->NextNodeListener(); 1988 1989 listener->NodeChanged(node, statFields, oldAttributes); 1990 1991 if (listener == last) 1992 break; 1993 1994 listener = next; 1995 } 1996 } 1997 1998 key = NULL; 1999 } 2000 } 2001