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 ::RootDirectory(kRootDirectoryID, st.st_mtim); 403 if (fRootDirectory == NULL) 404 RETURN_ERROR(B_NO_MEMORY); 405 fRootDirectory->Init(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_WRITE_LOCKED_RW_LOCK(&fLock); 572 ASSERT(!listener->IsListening()); 573 574 listener->StartedListening(node); 575 576 if (NodeListener* list = fNodeListeners.Lookup(node)) 577 list->AddNodeListener(listener); 578 else 579 fNodeListeners.Insert(listener); 580 } 581 582 583 void 584 Volume::RemoveNodeListener(NodeListener* listener) 585 { 586 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); 587 ASSERT(listener->IsListening()); 588 589 Node* node = listener->ListenedNode(); 590 591 if (NodeListener* next = listener->RemoveNodeListener()) { 592 // list not empty yet -- if we removed the head, add a new head to the 593 // hash table 594 NodeListener* list = fNodeListeners.Lookup(node); 595 if (list == listener) { 596 fNodeListeners.Remove(listener); 597 fNodeListeners.Insert(next); 598 } 599 } else 600 fNodeListeners.Remove(listener); 601 602 listener->StoppedListening(); 603 } 604 605 606 void 607 Volume::AddQuery(Query* query) 608 { 609 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); 610 fQueries.Add(query); 611 } 612 613 614 void 615 Volume::RemoveQuery(Query* query) 616 { 617 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); 618 fQueries.Remove(query); 619 } 620 621 622 void 623 Volume::UpdateLiveQueries(Node* node, const char* attribute, int32 type, 624 const void* oldKey, size_t oldLength, const void* newKey, 625 size_t newLength) 626 { 627 for (QueryList::Iterator it = fQueries.GetIterator(); 628 Query* query = it.Next();) { 629 query->LiveUpdate(node, attribute, type, oldKey, oldLength, newKey, 630 newLength); 631 } 632 } 633 634 635 status_t 636 Volume::GetVNode(ino_t nodeID, Node*& _node) 637 { 638 return get_vnode(fFSVolume, nodeID, (void**)&_node); 639 } 640 641 642 status_t 643 Volume::PutVNode(ino_t nodeID) 644 { 645 return put_vnode(fFSVolume, nodeID); 646 } 647 648 649 status_t 650 Volume::RemoveVNode(ino_t nodeID) 651 { 652 return remove_vnode(fFSVolume, nodeID); 653 } 654 655 656 status_t 657 Volume::PublishVNode(Node* node) 658 { 659 return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps, 660 node->Mode() & S_IFMT, 0); 661 } 662 663 664 void 665 Volume::PackageLinkNodeAdded(Node* node) 666 { 667 _AddPackageLinksNode(node); 668 669 notify_entry_created(ID(), node->Parent()->ID(), node->Name(), node->ID()); 670 _NotifyNodeAdded(node); 671 } 672 673 674 void 675 Volume::PackageLinkNodeRemoved(Node* node) 676 { 677 _RemovePackageLinksNode(node); 678 679 notify_entry_removed(ID(), node->Parent()->ID(), node->Name(), node->ID()); 680 _NotifyNodeRemoved(node); 681 } 682 683 684 void 685 Volume::PackageLinkNodeChanged(Node* node, uint32 statFields, 686 const OldNodeAttributes& oldAttributes) 687 { 688 Directory* parent = node->Parent(); 689 notify_stat_changed(ID(), parent != NULL ? parent->ID() : -1, node->ID(), 690 statFields); 691 _NotifyNodeChanged(node, statFields, oldAttributes); 692 } 693 694 695 status_t 696 Volume::_LoadOldPackagesStates(const char* packagesState) 697 { 698 // open and stat the admininistrative dir 699 int fd = openat(fPackagesDirectory->DirectoryFD(), 700 kAdministrativeDirectoryName, O_RDONLY); 701 if (fd < 0) { 702 ERROR("Failed to open administrative directory: %s\n", strerror(errno)); 703 RETURN_ERROR(errno); 704 } 705 706 struct stat adminDirStat; 707 if (fstat(fd, &adminDirStat) < 0) { 708 ERROR("Failed to fstat() administrative directory: %s\n", 709 strerror(errno)); 710 RETURN_ERROR(errno); 711 } 712 713 // iterate through the "administrative" dir 714 DirCloser dir(fdopendir(fd)); 715 if (!dir.IsSet()) { 716 ERROR("Failed to open administrative directory: %s\n", strerror(errno)); 717 RETURN_ERROR(errno); 718 } 719 720 while (dirent* entry = readdir(dir.Get())) { 721 if (strncmp(entry->d_name, "state_", 6) != 0 722 || strcmp(entry->d_name, packagesState) < 0) { 723 continue; 724 } 725 726 PackagesDirectory* packagesDirectory 727 = new(std::nothrow) PackagesDirectory; 728 status_t error = packagesDirectory->InitOldState(adminDirStat.st_dev, 729 adminDirStat.st_ino, entry->d_name); 730 if (error != B_OK) { 731 delete packagesDirectory; 732 continue; 733 } 734 735 fPackagesDirectories.Add(packagesDirectory); 736 fPackagesDirectoriesByNodeRef.Insert(packagesDirectory); 737 738 INFORM("added old packages dir state \"%s\"\n", 739 packagesDirectory->StateName().Data()); 740 } 741 742 // sort the packages directories by state age 743 fPackagesDirectories.Sort(&PackagesDirectory::IsNewer); 744 745 return B_OK; 746 } 747 748 749 status_t 750 Volume::_AddInitialPackages() 751 { 752 PackagesDirectory* packagesDirectory = fPackagesDirectories.Last(); 753 INFORM("Adding packages from \"%s\"\n", packagesDirectory->Path()); 754 755 // try reading the activation file of the oldest state 756 status_t error = _AddInitialPackagesFromActivationFile(packagesDirectory); 757 if (error != B_OK && packagesDirectory != fPackagesDirectory) { 758 WARN("Loading packages from old state \"%s\" failed. Loading packages " 759 "from latest state.\n", packagesDirectory->StateName().Data()); 760 761 // remove all packages already added 762 { 763 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 764 VolumeWriteLocker volumeLocker(this); 765 _RemoveAllPackages(); 766 } 767 768 // remove the old states 769 while (fPackagesDirectories.Last() != fPackagesDirectory) 770 fPackagesDirectories.RemoveTail()->ReleaseReference(); 771 772 // try reading the activation file of the latest state 773 packagesDirectory = fPackagesDirectory; 774 error = _AddInitialPackagesFromActivationFile(packagesDirectory); 775 } 776 777 if (error != B_OK) { 778 INFORM("Loading packages from activation file failed. Loading all " 779 "packages in packages directory.\n"); 780 781 // remove all packages already added 782 { 783 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 784 VolumeWriteLocker volumeLocker(this); 785 _RemoveAllPackages(); 786 } 787 788 // read the whole directory 789 error = _AddInitialPackagesFromDirectory(); 790 if (error != B_OK) 791 RETURN_ERROR(error); 792 } 793 794 // add the packages to the node tree 795 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 796 VolumeWriteLocker volumeLocker(this); 797 for (PackageFileNameHashTable::Iterator it = fPackages.GetIterator(); 798 Package* package = it.Next();) { 799 error = _AddPackageContent(package, false); 800 if (error != B_OK) { 801 for (it.Rewind(); Package* activePackage = it.Next();) { 802 if (activePackage == package) 803 break; 804 _RemovePackageContent(activePackage, NULL, false); 805 } 806 RETURN_ERROR(error); 807 } 808 } 809 810 return B_OK; 811 } 812 813 814 status_t 815 Volume::_AddInitialPackagesFromActivationFile( 816 PackagesDirectory* packagesDirectory) 817 { 818 // try reading the activation file 819 FileDescriptorCloser fd(openat(packagesDirectory->DirectoryFD(), 820 packagesDirectory == fPackagesDirectory 821 ? kActivationFilePath : kActivationFileName, 822 O_RDONLY)); 823 if (!fd.IsSet()) { 824 INFORM("Failed to open packages activation file: %s\n", 825 strerror(errno)); 826 RETURN_ERROR(errno); 827 } 828 829 // read the whole file into memory to simplify things 830 struct stat st; 831 if (fstat(fd.Get(), &st) != 0) { 832 ERROR("Failed to stat packages activation file: %s\n", 833 strerror(errno)); 834 RETURN_ERROR(errno); 835 } 836 837 if (st.st_size > (off_t)kMaxActivationFileSize) { 838 ERROR("The packages activation file is too big.\n"); 839 RETURN_ERROR(B_BAD_DATA); 840 } 841 842 char* fileContent = (char*)malloc(st.st_size + 1); 843 if (fileContent == NULL) 844 RETURN_ERROR(B_NO_MEMORY); 845 MemoryDeleter fileContentDeleter(fileContent); 846 847 ssize_t bytesRead = read(fd.Get(), fileContent, st.st_size); 848 if (bytesRead < 0) { 849 ERROR("Failed to read packages activation file: %s\n", strerror(errno)); 850 RETURN_ERROR(errno); 851 } 852 853 if (bytesRead != st.st_size) { 854 ERROR("Failed to read whole packages activation file\n"); 855 RETURN_ERROR(B_ERROR); 856 } 857 858 // null-terminate to simplify parsing 859 fileContent[st.st_size] = '\0'; 860 861 // parse the file and add the respective packages 862 const char* packageName = fileContent; 863 char* const fileContentEnd = fileContent + st.st_size; 864 while (packageName < fileContentEnd) { 865 char* packageNameEnd = strchr(packageName, '\n'); 866 if (packageNameEnd == NULL) 867 packageNameEnd = fileContentEnd; 868 869 // skip empty lines 870 if (packageName == packageNameEnd) { 871 packageName++; 872 continue; 873 } 874 *packageNameEnd = '\0'; 875 876 if (packageNameEnd - packageName >= B_FILE_NAME_LENGTH) { 877 ERROR("Invalid packages activation file content.\n"); 878 RETURN_ERROR(B_BAD_DATA); 879 } 880 881 status_t error = _LoadAndAddInitialPackage(packagesDirectory, 882 packageName); 883 if (error != B_OK) 884 RETURN_ERROR(error); 885 886 packageName = packageNameEnd + 1; 887 } 888 889 return B_OK; 890 } 891 892 893 status_t 894 Volume::_AddInitialPackagesFromDirectory() 895 { 896 // iterate through the dir and create packages 897 int fd = openat(fPackagesDirectory->DirectoryFD(), ".", O_RDONLY); 898 if (fd < 0) { 899 ERROR("Failed to open packages directory: %s\n", strerror(errno)); 900 RETURN_ERROR(errno); 901 } 902 903 DirCloser dir(fdopendir(fd)); 904 if (!dir.IsSet()) { 905 ERROR("Failed to open packages directory \"%s\": %s\n", 906 fPackagesDirectory->Path(), strerror(errno)); 907 RETURN_ERROR(errno); 908 } 909 910 while (dirent* entry = readdir(dir.Get())) { 911 // skip "." and ".." 912 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) 913 continue; 914 915 // also skip any entry without a ".hpkg" extension 916 size_t nameLength = strlen(entry->d_name); 917 if (nameLength < 5 918 || memcmp(entry->d_name + nameLength - 5, ".hpkg", 5) != 0) { 919 continue; 920 } 921 922 _LoadAndAddInitialPackage(fPackagesDirectory, entry->d_name); 923 } 924 925 return B_OK; 926 } 927 928 929 status_t 930 Volume::_LoadAndAddInitialPackage(PackagesDirectory* packagesDirectory, 931 const char* name) 932 { 933 Package* package; 934 status_t error = _LoadPackage(packagesDirectory, name, package); 935 if (error != B_OK) { 936 ERROR("Failed to load package \"%s\": %s\n", name, strerror(error)); 937 RETURN_ERROR(error); 938 } 939 BReference<Package> packageReference(package, true); 940 941 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 942 VolumeWriteLocker volumeLocker(this); 943 _AddPackage(package); 944 945 return B_OK; 946 } 947 948 949 inline void 950 Volume::_AddPackage(Package* package) 951 { 952 fPackages.Insert(package); 953 package->AcquireReference(); 954 } 955 956 957 inline void 958 Volume::_RemovePackage(Package* package) 959 { 960 fPackages.Remove(package); 961 package->ReleaseReference(); 962 } 963 964 965 void 966 Volume::_RemoveAllPackages() 967 { 968 Package* package = fPackages.Clear(true); 969 while (package != NULL) { 970 Package* next = package->FileNameHashTableNext(); 971 package->ReleaseReference(); 972 package = next; 973 } 974 } 975 976 977 inline Package* 978 Volume::_FindPackage(const char* fileName) const 979 { 980 return fPackages.Lookup(fileName); 981 } 982 983 984 status_t 985 Volume::_AddPackageContent(Package* package, bool notify) 986 { 987 // Open the package. We don't need the FD here, but this is an optimization. 988 // The attribute indices may want to read the package nodes' attributes and 989 // the package file would be opened and closed for each attribute instance. 990 // Since Package keeps and shares the FD as long as at least one party has 991 // the package open, we prevent that. 992 int fd = package->Open(); 993 if (fd < 0) 994 RETURN_ERROR(fd); 995 PackageCloser packageCloser(package); 996 997 status_t error = fPackageFSRoot->AddPackage(package); 998 if (error != B_OK) 999 RETURN_ERROR(error); 1000 1001 for (PackageNodeList::ConstIterator it = package->Nodes().GetIterator(); 1002 PackageNode* node = it.Next();) { 1003 // skip over ".PackageInfo" file, it isn't part of the package content 1004 if (strcmp(node->Name(), 1005 BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) { 1006 continue; 1007 } 1008 error = _AddPackageContentRootNode(package, node, notify); 1009 if (error != B_OK) { 1010 _RemovePackageContent(package, node, notify); 1011 RETURN_ERROR(error); 1012 } 1013 } 1014 1015 return B_OK; 1016 } 1017 1018 1019 void 1020 Volume::_RemovePackageContent(Package* package, PackageNode* endNode, 1021 bool notify) 1022 { 1023 PackageNode* node = package->Nodes().Head(); 1024 while (node != NULL) { 1025 if (node == endNode) 1026 break; 1027 1028 PackageNode* nextNode = package->Nodes().GetNext(node); 1029 1030 // skip over ".PackageInfo" file, it isn't part of the package content 1031 if (strcmp(node->Name(), 1032 BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME) != 0) { 1033 _RemovePackageContentRootNode(package, node, NULL, notify); 1034 } 1035 1036 node = nextNode; 1037 } 1038 1039 fPackageFSRoot->RemovePackage(package); 1040 } 1041 1042 1043 /*! This method recursively iterates through the descendents of the given 1044 package root node and adds all package nodes to the node tree in 1045 pre-order. 1046 Due to limited kernel stack space we avoid deep recursive function calls 1047 and rather use the package node stack implied by the tree. 1048 */ 1049 status_t 1050 Volume::_AddPackageContentRootNode(Package* package, 1051 PackageNode* rootPackageNode, bool notify) 1052 { 1053 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); 1054 1055 PackageNode* packageNode = rootPackageNode; 1056 Directory* directory = fRootDirectory; 1057 directory->WriteLock(); 1058 1059 do { 1060 Node* node; 1061 status_t error = _AddPackageNode(directory, packageNode, notify, node); 1062 // returns B_OK with a NULL node, when skipping the node 1063 if (error != B_OK) { 1064 // unlock all directories 1065 while (directory != NULL) { 1066 directory->WriteUnlock(); 1067 directory = directory->Parent(); 1068 } 1069 1070 // remove the added package nodes 1071 _RemovePackageContentRootNode(package, rootPackageNode, packageNode, 1072 notify); 1073 RETURN_ERROR(error); 1074 } 1075 1076 // recurse into directory, unless we're supposed to skip the node 1077 if (node != NULL) { 1078 if (PackageDirectory* packageDirectory 1079 = dynamic_cast<PackageDirectory*>(packageNode)) { 1080 if (packageDirectory->FirstChild() != NULL) { 1081 directory = dynamic_cast<Directory*>(node); 1082 packageNode = packageDirectory->FirstChild(); 1083 directory->WriteLock(); 1084 continue; 1085 } 1086 } 1087 } 1088 1089 // continue with the next available (ancestors's) sibling 1090 do { 1091 PackageDirectory* packageDirectory = packageNode->Parent(); 1092 PackageNode* sibling = packageDirectory != NULL 1093 ? packageDirectory->NextChild(packageNode) : NULL; 1094 1095 if (sibling != NULL) { 1096 packageNode = sibling; 1097 break; 1098 } 1099 1100 // no more siblings -- go back up the tree 1101 packageNode = packageDirectory; 1102 directory->WriteUnlock(); 1103 directory = directory->Parent(); 1104 // the parent is still locked, so this is safe 1105 } while (packageNode != NULL); 1106 } while (packageNode != NULL); 1107 1108 ASSERT(directory == NULL); 1109 1110 return B_OK; 1111 } 1112 1113 1114 /*! Recursively iterates through the descendents of the given package root node 1115 and removes all package nodes from the node tree in post-order, until 1116 encountering \a endPackageNode (if non-null). 1117 Due to limited kernel stack space we avoid deep recursive function calls 1118 and rather use the package node stack implied by the tree. 1119 */ 1120 void 1121 Volume::_RemovePackageContentRootNode(Package* package, 1122 PackageNode* rootPackageNode, PackageNode* endPackageNode, bool notify) 1123 { 1124 ASSERT_WRITE_LOCKED_RW_LOCK(&fLock); 1125 1126 PackageNode* packageNode = rootPackageNode; 1127 Directory* directory = fRootDirectory; 1128 directory->WriteLock(); 1129 1130 do { 1131 if (packageNode == endPackageNode) { 1132 // unlock all directories 1133 while (directory != NULL) { 1134 directory->WriteUnlock(); 1135 directory = directory->Parent(); 1136 } 1137 break; 1138 } 1139 1140 // recurse into directory 1141 if (PackageDirectory* packageDirectory 1142 = dynamic_cast<PackageDirectory*>(packageNode)) { 1143 if (packageDirectory->FirstChild() != NULL) { 1144 if (Directory* childDirectory = dynamic_cast<Directory*>( 1145 directory->FindChild(packageNode->Name()))) { 1146 directory = childDirectory; 1147 packageNode = packageDirectory->FirstChild(); 1148 directory->WriteLock(); 1149 continue; 1150 } 1151 } 1152 } 1153 1154 // continue with the next available (ancestors's) sibling 1155 do { 1156 PackageDirectory* packageDirectory = packageNode->Parent(); 1157 PackageNode* sibling = packageDirectory != NULL 1158 ? packageDirectory->NextChild(packageNode) : NULL; 1159 1160 // we're done with the node -- remove it 1161 _RemovePackageNode(directory, packageNode, 1162 directory->FindChild(packageNode->Name()), notify); 1163 1164 if (sibling != NULL) { 1165 packageNode = sibling; 1166 break; 1167 } 1168 1169 // no more siblings -- go back up the tree 1170 packageNode = packageDirectory; 1171 directory->WriteUnlock(); 1172 directory = directory->Parent(); 1173 // the parent is still locked, so this is safe 1174 } while (packageNode != NULL/* && packageNode != rootPackageNode*/); 1175 } while (packageNode != NULL/* && packageNode != rootPackageNode*/); 1176 1177 ASSERT(directory == NULL); 1178 } 1179 1180 1181 status_t 1182 Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode, 1183 bool notify, Node*& _node) 1184 { 1185 bool newNode = false; 1186 UnpackingNode* unpackingNode; 1187 Node* node = directory->FindChild(packageNode->Name()); 1188 PackageNode* oldPackageNode = NULL; 1189 1190 if (node != NULL) { 1191 unpackingNode = dynamic_cast<UnpackingNode*>(node); 1192 if (unpackingNode == NULL) { 1193 _node = NULL; 1194 return B_OK; 1195 } 1196 oldPackageNode = unpackingNode->GetPackageNode(); 1197 } else { 1198 status_t error = _CreateUnpackingNode(packageNode->Mode(), directory, 1199 packageNode->Name(), unpackingNode); 1200 if (error != B_OK) 1201 RETURN_ERROR(error); 1202 1203 node = unpackingNode->GetNode(); 1204 newNode = true; 1205 } 1206 1207 BReference<Node> nodeReference(node); 1208 NodeWriteLocker nodeWriteLocker(node); 1209 1210 BReference<Node> newNodeReference; 1211 NodeWriteLocker newNodeWriteLocker; 1212 Node* oldNode = NULL; 1213 1214 if (!newNode && !S_ISDIR(node->Mode()) && oldPackageNode != NULL 1215 && unpackingNode->WillBeFirstPackageNode(packageNode)) { 1216 // The package node we're going to add will represent the node, 1217 // replacing the current head package node. Since the node isn't a 1218 // directory, we must make sure that clients having opened or mapped the 1219 // node won't be surprised. So we create a new node and remove the 1220 // current one. 1221 // create a new node and transfer the package nodes to it 1222 UnpackingNode* newUnpackingNode; 1223 status_t error = unpackingNode->CloneTransferPackageNodes( 1224 fNextNodeID++, newUnpackingNode); 1225 if (error != B_OK) 1226 RETURN_ERROR(error); 1227 1228 // remove the old node 1229 _NotifyNodeRemoved(node); 1230 _RemoveNodeAndVNode(node); 1231 oldNode = node; 1232 1233 // add the new node 1234 unpackingNode = newUnpackingNode; 1235 node = unpackingNode->GetNode(); 1236 newNodeReference.SetTo(node); 1237 newNodeWriteLocker.SetTo(node, false); 1238 1239 directory->AddChild(node); 1240 fNodes.Insert(node); 1241 newNode = true; 1242 } 1243 1244 status_t error = unpackingNode->AddPackageNode(packageNode, ID()); 1245 if (error != B_OK) { 1246 dprintf("packagefs: Failed to add node \"%s\" of package \"%s\": %s\n", 1247 packageNode->Name().Data(), packageNode->GetPackage()->Name().Data(), 1248 strerror(error)); 1249 1250 // Remove the node, if created before. If the node was created to 1251 // replace the previous node, send out notifications instead. 1252 if (newNode) { 1253 if (oldNode != NULL) { 1254 _NotifyNodeAdded(node); 1255 if (notify) { 1256 notify_entry_removed(ID(), directory->ID(), oldNode->Name(), 1257 oldNode->ID()); 1258 notify_entry_created(ID(), directory->ID(), node->Name(), 1259 node->ID()); 1260 } 1261 } else 1262 _RemoveNode(node); 1263 } 1264 RETURN_ERROR(error); 1265 } 1266 1267 if (newNode) { 1268 _NotifyNodeAdded(node); 1269 } else if (packageNode == unpackingNode->GetPackageNode()) { 1270 _NotifyNodeChanged(node, kAllStatFields, 1271 OldUnpackingNodeAttributes(oldPackageNode)); 1272 } 1273 1274 if (notify) { 1275 if (newNode) { 1276 if (oldNode != NULL) { 1277 notify_entry_removed(ID(), directory->ID(), oldNode->Name(), 1278 oldNode->ID()); 1279 } 1280 notify_entry_created(ID(), directory->ID(), node->Name(), 1281 node->ID()); 1282 } else if (packageNode == unpackingNode->GetPackageNode()) { 1283 // The new package node has become the one representing the node. 1284 // Send stat changed notification for directories and entry 1285 // removed + created notifications for files and symlinks. 1286 notify_stat_changed(ID(), directory->ID(), node->ID(), 1287 kAllStatFields); 1288 // TODO: Actually the attributes might change, too! 1289 } 1290 } 1291 1292 _node = node; 1293 return B_OK; 1294 } 1295 1296 1297 void 1298 Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode, 1299 Node* node, bool notify) 1300 { 1301 UnpackingNode* unpackingNode = dynamic_cast<UnpackingNode*>(node); 1302 if (unpackingNode == NULL) 1303 return; 1304 1305 BReference<Node> nodeReference(node); 1306 NodeWriteLocker nodeWriteLocker(node); 1307 1308 PackageNode* headPackageNode = unpackingNode->GetPackageNode(); 1309 bool nodeRemoved = false; 1310 Node* newNode = NULL; 1311 1312 BReference<Node> newNodeReference; 1313 NodeWriteLocker newNodeWriteLocker; 1314 1315 // If this is the last package node of the node, remove it completely. 1316 if (unpackingNode->IsOnlyPackageNode(packageNode)) { 1317 // Notify before removing the node. Otherwise the indices might not 1318 // find the node anymore. 1319 _NotifyNodeRemoved(node); 1320 1321 unpackingNode->PrepareForRemoval(); 1322 1323 _RemoveNodeAndVNode(node); 1324 nodeRemoved = true; 1325 } else if (packageNode == headPackageNode) { 1326 // The node does at least have one more package node, but the one to be 1327 // removed is the head. Unless it's a directory, we replace the node 1328 // with a completely new one and let the old one die. This is necessary 1329 // to avoid surprises for clients that have opened/mapped the node. 1330 if (S_ISDIR(packageNode->Mode())) { 1331 unpackingNode->RemovePackageNode(packageNode, ID()); 1332 _NotifyNodeChanged(node, kAllStatFields, 1333 OldUnpackingNodeAttributes(headPackageNode)); 1334 } else { 1335 // create a new node and transfer the package nodes to it 1336 UnpackingNode* newUnpackingNode; 1337 status_t error = unpackingNode->CloneTransferPackageNodes( 1338 fNextNodeID++, newUnpackingNode); 1339 if (error == B_OK) { 1340 // remove the package node 1341 newUnpackingNode->RemovePackageNode(packageNode, ID()); 1342 1343 // remove the old node 1344 _NotifyNodeRemoved(node); 1345 _RemoveNodeAndVNode(node); 1346 1347 // add the new node 1348 newNode = newUnpackingNode->GetNode(); 1349 newNodeReference.SetTo(newNode); 1350 newNodeWriteLocker.SetTo(newNode, false); 1351 1352 directory->AddChild(newNode); 1353 fNodes.Insert(newNode); 1354 _NotifyNodeAdded(newNode); 1355 } else { 1356 // There's nothing we can do. Remove the node completely. 1357 _NotifyNodeRemoved(node); 1358 1359 unpackingNode->PrepareForRemoval(); 1360 1361 _RemoveNodeAndVNode(node); 1362 nodeRemoved = true; 1363 } 1364 } 1365 } else { 1366 // The package node to remove is not the head of the node. This change 1367 // doesn't have any visible effect. 1368 unpackingNode->RemovePackageNode(packageNode, ID()); 1369 } 1370 1371 if (!notify) 1372 return; 1373 1374 // send notifications 1375 if (nodeRemoved) { 1376 notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID()); 1377 } else if (packageNode == headPackageNode) { 1378 // The removed package node was the one representing the node. 1379 // Send stat changed notification for directories and entry 1380 // removed + created notifications for files and symlinks. 1381 if (S_ISDIR(packageNode->Mode())) { 1382 notify_stat_changed(ID(), directory->ID(), node->ID(), 1383 kAllStatFields); 1384 // TODO: Actually the attributes might change, too! 1385 } else { 1386 notify_entry_removed(ID(), directory->ID(), node->Name(), 1387 node->ID()); 1388 notify_entry_created(ID(), directory->ID(), newNode->Name(), 1389 newNode->ID()); 1390 } 1391 } 1392 } 1393 1394 1395 status_t 1396 Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const String& name, 1397 UnpackingNode*& _node) 1398 { 1399 UnpackingNode* unpackingNode; 1400 if (S_ISREG(mode) || S_ISLNK(mode)) 1401 unpackingNode = new UnpackingLeafNode(fNextNodeID++); 1402 else if (S_ISDIR(mode)) 1403 unpackingNode = new UnpackingDirectory(fNextNodeID++); 1404 else 1405 RETURN_ERROR(B_UNSUPPORTED); 1406 1407 if (unpackingNode == NULL) 1408 RETURN_ERROR(B_NO_MEMORY); 1409 1410 Node* node = unpackingNode->GetNode(); 1411 BReference<Node> nodeReference(node, true); 1412 1413 status_t error = node->Init(name); 1414 if (error != B_OK) 1415 RETURN_ERROR(error); 1416 1417 parent->AddChild(node); 1418 1419 fNodes.Insert(node); 1420 nodeReference.Detach(); 1421 // we keep the initial node reference for the table 1422 1423 _node = unpackingNode; 1424 return B_OK; 1425 } 1426 1427 1428 void 1429 Volume::_RemoveNode(Node* node) 1430 { 1431 // remove from parent 1432 Directory* parent = node->Parent(); 1433 parent->RemoveChild(node); 1434 1435 // remove from node table 1436 fNodes.Remove(node); 1437 node->ReleaseReference(); 1438 } 1439 1440 1441 void 1442 Volume::_RemoveNodeAndVNode(Node* node) 1443 { 1444 // If the node is known to the VFS, we get the vnode, remove it, and put it, 1445 // so that the VFS will discard it as soon as possible (i.e. now, if no one 1446 // else is using it). 1447 NodeWriteLocker nodeWriteLocker(node); 1448 1449 // Remove the node from its parent and the volume. This makes the node 1450 // inaccessible via the get_vnode() and lookup() hooks. 1451 _RemoveNode(node); 1452 1453 bool getVNode = node->IsKnownToVFS(); 1454 1455 nodeWriteLocker.Unlock(); 1456 1457 // Get a vnode reference, if the node is already known to the VFS. 1458 Node* dummyNode; 1459 if (getVNode && GetVNode(node->ID(), dummyNode) == B_OK) { 1460 // TODO: There still is a race condition here which we can't avoid 1461 // without more help from the VFS. Right after we drop the write 1462 // lock a vnode for the node could be discarded by the VFS. At that 1463 // point another thread trying to get the vnode by ID would create 1464 // a vnode, mark it busy and call our get_vnode() hook. It would 1465 // block since we (i.e. the package loader thread executing this 1466 // method) still have the volume write lock. Our get_vnode() call 1467 // would block, since it finds the vnode marked busy. It times out 1468 // eventually, but until then a good deal of FS operations might 1469 // block as well due to us holding the volume lock and probably 1470 // several node locks as well. A get_vnode*() variant (e.g. 1471 // get_vnode_etc() with flags parameter) that wouldn't block and 1472 // only get the vnode, if already loaded and non-busy, would be 1473 // perfect here. 1474 RemoveVNode(node->ID()); 1475 PutVNode(node->ID()); 1476 } 1477 } 1478 1479 1480 status_t 1481 Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name, 1482 Package*& _package) 1483 { 1484 // Find the package -- check the specified packages directory and iterate 1485 // toward the newer states. 1486 struct stat st; 1487 for (;;) { 1488 if (packagesDirectory == NULL) 1489 return B_ENTRY_NOT_FOUND; 1490 1491 if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) { 1492 // check whether the entry is a file 1493 if (!S_ISREG(st.st_mode)) 1494 return B_BAD_VALUE; 1495 break; 1496 } 1497 1498 packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory); 1499 } 1500 1501 // create a package 1502 Package* package = new(std::nothrow) Package(this, packagesDirectory, 1503 st.st_dev, st.st_ino); 1504 if (package == NULL) 1505 RETURN_ERROR(B_NO_MEMORY); 1506 BReference<Package> packageReference(package, true); 1507 1508 status_t error = package->Init(name); 1509 if (error != B_OK) 1510 return error; 1511 1512 error = package->Load(fPackageSettings); 1513 if (error != B_OK) 1514 return error; 1515 1516 _package = packageReference.Detach(); 1517 return B_OK; 1518 } 1519 1520 1521 status_t 1522 Volume::_ChangeActivation(ActivationChangeRequest& request) 1523 { 1524 uint32 itemCount = request.CountItems(); 1525 if (itemCount == 0) 1526 return B_OK; 1527 1528 // first check the request 1529 int32 newPackageCount = 0; 1530 int32 oldPackageCount = 0; 1531 { 1532 VolumeReadLocker volumeLocker(this); 1533 1534 for (uint32 i = 0; i < itemCount; i++) { 1535 PackageFSActivationChangeItem* item = request.ItemAt(i); 1536 if (item->parentDeviceID != fPackagesDirectory->DeviceID() 1537 || item->parentDirectoryID != fPackagesDirectory->NodeID()) { 1538 ERROR("Volume::_ChangeActivation(): mismatching packages " 1539 "directory\n"); 1540 RETURN_ERROR(B_MISMATCHED_VALUES); 1541 } 1542 1543 Package* package = _FindPackage(item->name); 1544 // TODO: We should better look up the package by node_ref! 1545 if (item->type == PACKAGE_FS_ACTIVATE_PACKAGE) { 1546 if (package != NULL) { 1547 ERROR("Volume::_ChangeActivation(): package to activate " 1548 "already activated: \"%s\"\n", item->name); 1549 RETURN_ERROR(B_NAME_IN_USE); 1550 } 1551 newPackageCount++; 1552 } else if (item->type == PACKAGE_FS_DEACTIVATE_PACKAGE) { 1553 if (package == NULL) { 1554 ERROR("Volume::_ChangeActivation(): package to deactivate " 1555 "not found: \"%s\"\n", item->name); 1556 RETURN_ERROR(B_NAME_NOT_FOUND); 1557 } 1558 oldPackageCount++; 1559 } else if (item->type == PACKAGE_FS_REACTIVATE_PACKAGE) { 1560 if (package == NULL) { 1561 ERROR("Volume::_ChangeActivation(): package to reactivate " 1562 "not found: \"%s\"\n", item->name); 1563 RETURN_ERROR(B_NAME_NOT_FOUND); 1564 } 1565 oldPackageCount++; 1566 newPackageCount++; 1567 } else 1568 RETURN_ERROR(B_BAD_VALUE); 1569 } 1570 } 1571 1572 INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32 1573 " old packages\n", newPackageCount, oldPackageCount); 1574 1575 // Things look good so far -- allocate reference arrays for the packages to 1576 // add and remove. 1577 BReference<Package>* newPackageReferences 1578 = new(std::nothrow) BReference<Package>[newPackageCount]; 1579 if (newPackageReferences == NULL) 1580 RETURN_ERROR(B_NO_MEMORY); 1581 ArrayDeleter<BReference<Package> > newPackageReferencesDeleter( 1582 newPackageReferences); 1583 1584 BReference<Package>* oldPackageReferences 1585 = new(std::nothrow) BReference<Package>[oldPackageCount]; 1586 if (oldPackageReferences == NULL) 1587 RETURN_ERROR(B_NO_MEMORY); 1588 ArrayDeleter<BReference<Package> > oldPackageReferencesDeleter( 1589 oldPackageReferences); 1590 1591 // load all new packages 1592 int32 newPackageIndex = 0; 1593 for (uint32 i = 0; i < itemCount; i++) { 1594 PackageFSActivationChangeItem* item = request.ItemAt(i); 1595 1596 if (item->type != PACKAGE_FS_ACTIVATE_PACKAGE 1597 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) { 1598 continue; 1599 } 1600 1601 Package* package; 1602 status_t error = _LoadPackage(fPackagesDirectory, item->name, package); 1603 if (error != B_OK) { 1604 ERROR("Volume::_ChangeActivation(): failed to load package " 1605 "\"%s\"\n", item->name); 1606 RETURN_ERROR(error); 1607 } 1608 1609 newPackageReferences[newPackageIndex++].SetTo(package, true); 1610 } 1611 1612 // apply the changes 1613 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 1614 VolumeWriteLocker volumeLocker(this); 1615 // TODO: Add a change counter to Volume, so we can easily check whether 1616 // everything is still the same. 1617 1618 // remove the old packages 1619 int32 oldPackageIndex = 0; 1620 for (uint32 i = 0; i < itemCount; i++) { 1621 PackageFSActivationChangeItem* item = request.ItemAt(i); 1622 1623 if (item->type != PACKAGE_FS_DEACTIVATE_PACKAGE 1624 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) { 1625 continue; 1626 } 1627 1628 Package* package = _FindPackage(item->name); 1629 // TODO: We should better look up the package by node_ref! 1630 oldPackageReferences[oldPackageIndex++].SetTo(package); 1631 _RemovePackageContent(package, NULL, true); 1632 _RemovePackage(package); 1633 1634 INFORM("package \"%s\" deactivated\n", package->FileName().Data()); 1635 } 1636 // TODO: Since package removal cannot fail, consider adding the new packages 1637 // first. The reactivation case may make that problematic, since two packages 1638 // with the same name would be active after activating the new one. Check! 1639 1640 // add the new packages 1641 status_t error = B_OK; 1642 for (newPackageIndex = 0; newPackageIndex < newPackageCount; 1643 newPackageIndex++) { 1644 Package* package = newPackageReferences[newPackageIndex]; 1645 _AddPackage(package); 1646 1647 // add the package to the node tree 1648 error = _AddPackageContent(package, true); 1649 if (error != B_OK) { 1650 _RemovePackage(package); 1651 break; 1652 } 1653 INFORM("package \"%s\" activated\n", package->FileName().Data()); 1654 } 1655 1656 // Try to roll back the changes, if an error occurred. 1657 if (error != B_OK) { 1658 for (int32 i = newPackageIndex - 1; i >= 0; i--) { 1659 Package* package = newPackageReferences[i]; 1660 _RemovePackageContent(package, NULL, true); 1661 _RemovePackage(package); 1662 } 1663 1664 for (int32 i = oldPackageCount - 1; i >= 0; i--) { 1665 Package* package = oldPackageReferences[i]; 1666 _AddPackage(package); 1667 1668 if (_AddPackageContent(package, true) != B_OK) { 1669 // nothing we can do here 1670 ERROR("Volume::_ChangeActivation(): failed to roll back " 1671 "deactivation of package \"%s\" after error\n", 1672 package->FileName().Data()); 1673 _RemovePackage(package); 1674 } 1675 } 1676 } 1677 1678 return error; 1679 } 1680 1681 1682 status_t 1683 Volume::_InitMountType(const char* mountType) 1684 { 1685 if (mountType == NULL) 1686 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM; 1687 else if (strcmp(mountType, "system") == 0) 1688 fMountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM; 1689 else if (strcmp(mountType, "home") == 0) 1690 fMountType = PACKAGE_FS_MOUNT_TYPE_HOME; 1691 else if (strcmp(mountType, "custom") == 0) 1692 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM; 1693 else 1694 RETURN_ERROR(B_BAD_VALUE); 1695 1696 return B_OK; 1697 } 1698 1699 1700 status_t 1701 Volume::_CreateShineThroughDirectory(Directory* parent, const char* name, 1702 Directory*& _directory) 1703 { 1704 ShineThroughDirectory* directory = new(std::nothrow) ShineThroughDirectory( 1705 fNextNodeID++); 1706 if (directory == NULL) 1707 RETURN_ERROR(B_NO_MEMORY); 1708 BReference<ShineThroughDirectory> directoryReference(directory, true); 1709 1710 String nameString; 1711 if (!nameString.SetTo(name)) 1712 RETURN_ERROR(B_NO_MEMORY); 1713 1714 status_t error = directory->Init(nameString); 1715 if (error != B_OK) 1716 RETURN_ERROR(error); 1717 1718 parent->AddChild(directory); 1719 1720 fNodes.Insert(directory); 1721 directoryReference.Detach(); 1722 // we keep the initial node reference for the table 1723 1724 _directory = directory; 1725 return B_OK; 1726 } 1727 1728 1729 status_t 1730 Volume::_CreateShineThroughDirectories(const char* shineThroughSetting) 1731 { 1732 // get the directories to map 1733 const char* const* directories = NULL; 1734 1735 if (shineThroughSetting == NULL) { 1736 // nothing specified -- derive from mount type 1737 switch (fMountType) { 1738 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 1739 case PACKAGE_FS_MOUNT_TYPE_HOME: 1740 directories = kShineThroughDirectories; 1741 break; 1742 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 1743 return B_OK; 1744 case PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT: 1745 return B_BAD_VALUE; 1746 } 1747 } else if (strcmp(shineThroughSetting, "system") == 0) 1748 directories = kShineThroughDirectories; 1749 else if (strcmp(shineThroughSetting, "home") == 0) 1750 directories = kShineThroughDirectories; 1751 else if (strcmp(shineThroughSetting, "none") == 0) 1752 directories = NULL; 1753 else 1754 RETURN_ERROR(B_BAD_VALUE); 1755 1756 if (directories == NULL) 1757 return B_OK; 1758 1759 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1760 1761 // iterate through the directory list and create the directories 1762 while (const char* directoryName = *(directories++)) { 1763 // create the directory 1764 Directory* directory; 1765 status_t error = _CreateShineThroughDirectory(fRootDirectory, 1766 directoryName, directory); 1767 if (error != B_OK) 1768 RETURN_ERROR(error); 1769 } 1770 1771 return B_OK; 1772 } 1773 1774 1775 status_t 1776 Volume::_PublishShineThroughDirectories() 1777 { 1778 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1779 1780 // Iterate through the root directory children and bind the shine-through 1781 // directories to the respective mount point subdirectories. 1782 Node* nextNode; 1783 for (Node* node = fRootDirectory->FirstChild(); node != NULL; 1784 node = nextNode) { 1785 nextNode = fRootDirectory->NextChild(node); 1786 1787 // skip anything but shine-through directories 1788 ShineThroughDirectory* directory 1789 = dynamic_cast<ShineThroughDirectory*>(node); 1790 if (directory == NULL) 1791 continue; 1792 1793 const char* directoryName = directory->Name(); 1794 1795 // look up the mount point subdirectory 1796 struct vnode* vnode; 1797 status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID, 1798 fMountPoint.nodeID, directoryName, &vnode); 1799 if (error != B_OK) { 1800 dprintf("packagefs: Failed to get shine-through directory \"%s\": " 1801 "%s\n", directoryName, strerror(error)); 1802 _RemoveNode(directory); 1803 continue; 1804 } 1805 VnodePutter vnodePutter(vnode); 1806 1807 // stat it 1808 struct stat st; 1809 error = vfs_stat_vnode(vnode, &st); 1810 if (error != B_OK) { 1811 dprintf("packagefs: Failed to stat shine-through directory \"%s\": " 1812 "%s\n", directoryName, strerror(error)); 1813 _RemoveNode(directory); 1814 continue; 1815 } 1816 1817 if (!S_ISDIR(st.st_mode)) { 1818 dprintf("packagefs: Shine-through entry \"%s\" is not a " 1819 "directory\n", directoryName); 1820 _RemoveNode(directory); 1821 continue; 1822 } 1823 1824 // publish the vnode, so the VFS will find it without asking us 1825 directory->AcquireReference(); 1826 error = PublishVNode(directory); 1827 if (error != B_OK) { 1828 directory->ReleaseReference(); 1829 _RemoveNode(directory); 1830 RETURN_ERROR(error); 1831 } 1832 1833 // bind the directory 1834 error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id, 1835 directory->ID()); 1836 1837 PutVNode(directory->ID()); 1838 // release our reference again -- on success 1839 // vfs_bind_mount_directory() got one 1840 1841 if (error != B_OK) 1842 RETURN_ERROR(error); 1843 } 1844 1845 return B_OK; 1846 } 1847 1848 1849 status_t 1850 Volume::_AddPackageLinksDirectory() 1851 { 1852 // called when mounting, so we don't need to lock the volume 1853 1854 PackageLinksDirectory* packageLinksDirectory 1855 = fPackageFSRoot->GetPackageLinksDirectory(); 1856 1857 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1858 NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory); 1859 1860 fRootDirectory->AddChild(packageLinksDirectory); 1861 1862 _AddPackageLinksNode(packageLinksDirectory); 1863 1864 packageLinksDirectory->SetListener(this); 1865 1866 return B_OK; 1867 } 1868 1869 1870 void 1871 Volume::_RemovePackageLinksDirectory() 1872 { 1873 PackageLinksDirectory* packageLinksDirectory 1874 = fPackageFSRoot->GetPackageLinksDirectory(); 1875 1876 VolumeWriteLocker volumeLocker(this); 1877 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1878 NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory); 1879 1880 if (packageLinksDirectory->Parent() == fRootDirectory) { 1881 packageLinksDirectory->SetListener(NULL); 1882 fRootDirectory->RemoveChild(packageLinksDirectory); 1883 } 1884 } 1885 1886 1887 void 1888 Volume::_AddPackageLinksNode(Node* node) 1889 { 1890 node->SetID(fNextNodeID++); 1891 1892 fNodes.Insert(node); 1893 node->AcquireReference(); 1894 1895 // If this is a directory, recursively add descendants. The directory tree 1896 // for the package links isn't deep, so we can do recursion. 1897 if (Directory* directory = dynamic_cast<Directory*>(node)) { 1898 for (Node* child = directory->FirstChild(); child != NULL; 1899 child = directory->NextChild(child)) { 1900 NodeWriteLocker childWriteLocker(child); 1901 _AddPackageLinksNode(child); 1902 } 1903 } 1904 } 1905 1906 1907 void 1908 Volume::_RemovePackageLinksNode(Node* node) 1909 { 1910 // If this is a directory, recursively remove descendants. The directory 1911 // tree for the package links isn't deep, so we can do recursion. 1912 if (Directory* directory = dynamic_cast<Directory*>(node)) { 1913 for (Node* child = directory->FirstChild(); child != NULL; 1914 child = directory->NextChild(child)) { 1915 NodeWriteLocker childWriteLocker(child); 1916 _RemovePackageLinksNode(child); 1917 } 1918 } 1919 1920 fNodes.Remove(node); 1921 node->ReleaseReference(); 1922 } 1923 1924 1925 inline Volume* 1926 Volume::_SystemVolumeIfNotSelf() const 1927 { 1928 if (Volume* systemVolume = fPackageFSRoot->SystemVolume()) 1929 return systemVolume == this ? NULL : systemVolume; 1930 return NULL; 1931 } 1932 1933 1934 void 1935 Volume::_NotifyNodeAdded(Node* node) 1936 { 1937 Node* key = node; 1938 1939 for (int i = 0; i < 2; i++) { 1940 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1941 NodeListener* last = listener->PreviousNodeListener(); 1942 1943 while (true) { 1944 NodeListener* next = listener->NextNodeListener(); 1945 1946 listener->NodeAdded(node); 1947 1948 if (listener == last) 1949 break; 1950 1951 listener = next; 1952 } 1953 } 1954 1955 key = NULL; 1956 } 1957 } 1958 1959 1960 void 1961 Volume::_NotifyNodeRemoved(Node* node) 1962 { 1963 Node* key = node; 1964 1965 for (int i = 0; i < 2; i++) { 1966 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1967 NodeListener* last = listener->PreviousNodeListener(); 1968 1969 while (true) { 1970 NodeListener* next = listener->NextNodeListener(); 1971 1972 listener->NodeRemoved(node); 1973 1974 if (listener == last) 1975 break; 1976 1977 listener = next; 1978 } 1979 } 1980 1981 key = NULL; 1982 } 1983 } 1984 1985 1986 void 1987 Volume::_NotifyNodeChanged(Node* node, uint32 statFields, 1988 const OldNodeAttributes& oldAttributes) 1989 { 1990 Node* key = node; 1991 1992 for (int i = 0; i < 2; i++) { 1993 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1994 NodeListener* last = listener->PreviousNodeListener(); 1995 1996 while (true) { 1997 NodeListener* next = listener->NextNodeListener(); 1998 1999 listener->NodeChanged(node, statFields, oldAttributes); 2000 2001 if (listener == last) 2002 break; 2003 2004 listener = next; 2005 } 2006 } 2007 2008 key = NULL; 2009 } 2010 } 2011