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 dprintf("packagefs: Failed to add node \"%s\" of package \"%s\": %s\n", 1243 packageNode->Name().Data(), packageNode->GetPackage()->Name().Data(), 1244 strerror(error)); 1245 1246 // Remove the node, if created before. If the node was created to 1247 // replace the previous node, send out notifications instead. 1248 if (newNode) { 1249 if (oldNode != NULL) { 1250 _NotifyNodeAdded(node); 1251 if (notify) { 1252 notify_entry_removed(ID(), directory->ID(), oldNode->Name(), 1253 oldNode->ID()); 1254 notify_entry_created(ID(), directory->ID(), node->Name(), 1255 node->ID()); 1256 } 1257 } else 1258 _RemoveNode(node); 1259 } 1260 RETURN_ERROR(error); 1261 } 1262 1263 if (newNode) { 1264 _NotifyNodeAdded(node); 1265 } else if (packageNode == unpackingNode->GetPackageNode()) { 1266 _NotifyNodeChanged(node, kAllStatFields, 1267 OldUnpackingNodeAttributes(oldPackageNode)); 1268 } 1269 1270 if (notify) { 1271 if (newNode) { 1272 if (oldNode != NULL) { 1273 notify_entry_removed(ID(), directory->ID(), oldNode->Name(), 1274 oldNode->ID()); 1275 } 1276 notify_entry_created(ID(), directory->ID(), node->Name(), 1277 node->ID()); 1278 } else if (packageNode == unpackingNode->GetPackageNode()) { 1279 // The new package node has become the one representing the node. 1280 // Send stat changed notification for directories and entry 1281 // removed + created notifications for files and symlinks. 1282 notify_stat_changed(ID(), directory->ID(), node->ID(), 1283 kAllStatFields); 1284 // TODO: Actually the attributes might change, too! 1285 } 1286 } 1287 1288 _node = node; 1289 return B_OK; 1290 } 1291 1292 1293 void 1294 Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode, 1295 Node* node, bool notify) 1296 { 1297 UnpackingNode* unpackingNode = dynamic_cast<UnpackingNode*>(node); 1298 if (unpackingNode == NULL) 1299 return; 1300 1301 BReference<Node> nodeReference(node); 1302 NodeWriteLocker nodeWriteLocker(node); 1303 1304 PackageNode* headPackageNode = unpackingNode->GetPackageNode(); 1305 bool nodeRemoved = false; 1306 Node* newNode = NULL; 1307 1308 BReference<Node> newNodeReference; 1309 NodeWriteLocker newNodeWriteLocker; 1310 1311 // If this is the last package node of the node, remove it completely. 1312 if (unpackingNode->IsOnlyPackageNode(packageNode)) { 1313 // Notify before removing the node. Otherwise the indices might not 1314 // find the node anymore. 1315 _NotifyNodeRemoved(node); 1316 1317 unpackingNode->PrepareForRemoval(); 1318 1319 _RemoveNodeAndVNode(node); 1320 nodeRemoved = true; 1321 } else if (packageNode == headPackageNode) { 1322 // The node does at least have one more package node, but the one to be 1323 // removed is the head. Unless it's a directory, we replace the node 1324 // with a completely new one and let the old one die. This is necessary 1325 // to avoid surprises for clients that have opened/mapped the node. 1326 if (S_ISDIR(packageNode->Mode())) { 1327 unpackingNode->RemovePackageNode(packageNode, ID()); 1328 _NotifyNodeChanged(node, kAllStatFields, 1329 OldUnpackingNodeAttributes(headPackageNode)); 1330 } else { 1331 // create a new node and transfer the package nodes to it 1332 UnpackingNode* newUnpackingNode; 1333 status_t error = unpackingNode->CloneTransferPackageNodes( 1334 fNextNodeID++, newUnpackingNode); 1335 if (error == B_OK) { 1336 // remove the package node 1337 newUnpackingNode->RemovePackageNode(packageNode, ID()); 1338 1339 // remove the old node 1340 _NotifyNodeRemoved(node); 1341 _RemoveNodeAndVNode(node); 1342 1343 // add the new node 1344 newNode = newUnpackingNode->GetNode(); 1345 newNodeReference.SetTo(newNode); 1346 newNodeWriteLocker.SetTo(newNode, false); 1347 1348 directory->AddChild(newNode); 1349 fNodes.Insert(newNode); 1350 _NotifyNodeAdded(newNode); 1351 } else { 1352 // There's nothing we can do. Remove the node completely. 1353 _NotifyNodeRemoved(node); 1354 1355 unpackingNode->PrepareForRemoval(); 1356 1357 _RemoveNodeAndVNode(node); 1358 nodeRemoved = true; 1359 } 1360 } 1361 } else { 1362 // The package node to remove is not the head of the node. This change 1363 // doesn't have any visible effect. 1364 unpackingNode->RemovePackageNode(packageNode, ID()); 1365 } 1366 1367 if (!notify) 1368 return; 1369 1370 // send notifications 1371 if (nodeRemoved) { 1372 notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID()); 1373 } else if (packageNode == headPackageNode) { 1374 // The removed package node was the one representing the node. 1375 // Send stat changed notification for directories and entry 1376 // removed + created notifications for files and symlinks. 1377 if (S_ISDIR(packageNode->Mode())) { 1378 notify_stat_changed(ID(), directory->ID(), node->ID(), 1379 kAllStatFields); 1380 // TODO: Actually the attributes might change, too! 1381 } else { 1382 notify_entry_removed(ID(), directory->ID(), node->Name(), 1383 node->ID()); 1384 notify_entry_created(ID(), directory->ID(), newNode->Name(), 1385 newNode->ID()); 1386 } 1387 } 1388 } 1389 1390 1391 status_t 1392 Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const String& name, 1393 UnpackingNode*& _node) 1394 { 1395 UnpackingNode* unpackingNode; 1396 if (S_ISREG(mode) || S_ISLNK(mode)) 1397 unpackingNode = new(std::nothrow) UnpackingLeafNode(fNextNodeID++); 1398 else if (S_ISDIR(mode)) 1399 unpackingNode = new(std::nothrow) UnpackingDirectory(fNextNodeID++); 1400 else 1401 RETURN_ERROR(B_UNSUPPORTED); 1402 1403 if (unpackingNode == NULL) 1404 RETURN_ERROR(B_NO_MEMORY); 1405 1406 Node* node = unpackingNode->GetNode(); 1407 BReference<Node> nodeReference(node, true); 1408 1409 status_t error = node->Init(parent, name); 1410 if (error != B_OK) 1411 RETURN_ERROR(error); 1412 1413 parent->AddChild(node); 1414 1415 fNodes.Insert(node); 1416 nodeReference.Detach(); 1417 // we keep the initial node reference for the table 1418 1419 _node = unpackingNode; 1420 return B_OK; 1421 } 1422 1423 1424 void 1425 Volume::_RemoveNode(Node* node) 1426 { 1427 // remove from parent 1428 Directory* parent = node->Parent(); 1429 parent->RemoveChild(node); 1430 1431 // remove from node table 1432 fNodes.Remove(node); 1433 node->ReleaseReference(); 1434 } 1435 1436 1437 void 1438 Volume::_RemoveNodeAndVNode(Node* node) 1439 { 1440 // If the node is known to the VFS, we get the vnode, remove it, and put it, 1441 // so that the VFS will discard it as soon as possible (i.e. now, if no one 1442 // else is using it). 1443 NodeWriteLocker nodeWriteLocker(node); 1444 1445 // Remove the node from its parent and the volume. This makes the node 1446 // inaccessible via the get_vnode() and lookup() hooks. 1447 _RemoveNode(node); 1448 1449 bool getVNode = node->IsKnownToVFS(); 1450 1451 nodeWriteLocker.Unlock(); 1452 1453 // Get a vnode reference, if the node is already known to the VFS. 1454 Node* dummyNode; 1455 if (getVNode && GetVNode(node->ID(), dummyNode) == B_OK) { 1456 // TODO: There still is a race condition here which we can't avoid 1457 // without more help from the VFS. Right after we drop the write 1458 // lock a vnode for the node could be discarded by the VFS. At that 1459 // point another thread trying to get the vnode by ID would create 1460 // a vnode, mark it busy and call our get_vnode() hook. It would 1461 // block since we (i.e. the package loader thread executing this 1462 // method) still have the volume write lock. Our get_vnode() call 1463 // would block, since it finds the vnode marked busy. It times out 1464 // eventually, but until then a good deal of FS operations might 1465 // block as well due to us holding the volume lock and probably 1466 // several node locks as well. A get_vnode*() variant (e.g. 1467 // get_vnode_etc() with flags parameter) that wouldn't block and 1468 // only get the vnode, if already loaded and non-busy, would be 1469 // perfect here. 1470 RemoveVNode(node->ID()); 1471 PutVNode(node->ID()); 1472 } 1473 } 1474 1475 1476 status_t 1477 Volume::_LoadPackage(PackagesDirectory* packagesDirectory, const char* name, 1478 Package*& _package) 1479 { 1480 // Find the package -- check the specified packages directory and iterate 1481 // toward the newer states. 1482 struct stat st; 1483 for (;;) { 1484 if (packagesDirectory == NULL) 1485 return B_ENTRY_NOT_FOUND; 1486 1487 if (fstatat(packagesDirectory->DirectoryFD(), name, &st, 0) == 0) { 1488 // check whether the entry is a file 1489 if (!S_ISREG(st.st_mode)) 1490 return B_BAD_VALUE; 1491 break; 1492 } 1493 1494 packagesDirectory = fPackagesDirectories.GetPrevious(packagesDirectory); 1495 } 1496 1497 // create a package 1498 Package* package = new(std::nothrow) Package(this, packagesDirectory, 1499 st.st_dev, st.st_ino); 1500 if (package == NULL) 1501 RETURN_ERROR(B_NO_MEMORY); 1502 BReference<Package> packageReference(package, true); 1503 1504 status_t error = package->Init(name); 1505 if (error != B_OK) 1506 return error; 1507 1508 error = package->Load(fPackageSettings); 1509 if (error != B_OK) 1510 return error; 1511 1512 _package = packageReference.Detach(); 1513 return B_OK; 1514 } 1515 1516 1517 status_t 1518 Volume::_ChangeActivation(ActivationChangeRequest& request) 1519 { 1520 uint32 itemCount = request.CountItems(); 1521 if (itemCount == 0) 1522 return B_OK; 1523 1524 // first check the request 1525 int32 newPackageCount = 0; 1526 int32 oldPackageCount = 0; 1527 { 1528 VolumeReadLocker volumeLocker(this); 1529 1530 for (uint32 i = 0; i < itemCount; i++) { 1531 PackageFSActivationChangeItem* item = request.ItemAt(i); 1532 if (item->parentDeviceID != fPackagesDirectory->DeviceID() 1533 || item->parentDirectoryID != fPackagesDirectory->NodeID()) { 1534 ERROR("Volume::_ChangeActivation(): mismatching packages " 1535 "directory\n"); 1536 RETURN_ERROR(B_MISMATCHED_VALUES); 1537 } 1538 1539 Package* package = _FindPackage(item->name); 1540 // TODO: We should better look up the package by node_ref! 1541 if (item->type == PACKAGE_FS_ACTIVATE_PACKAGE) { 1542 if (package != NULL) { 1543 ERROR("Volume::_ChangeActivation(): package to activate " 1544 "already activated: \"%s\"\n", item->name); 1545 RETURN_ERROR(B_NAME_IN_USE); 1546 } 1547 newPackageCount++; 1548 } else if (item->type == PACKAGE_FS_DEACTIVATE_PACKAGE) { 1549 if (package == NULL) { 1550 ERROR("Volume::_ChangeActivation(): package to deactivate " 1551 "not found: \"%s\"\n", item->name); 1552 RETURN_ERROR(B_NAME_NOT_FOUND); 1553 } 1554 oldPackageCount++; 1555 } else if (item->type == PACKAGE_FS_REACTIVATE_PACKAGE) { 1556 if (package == NULL) { 1557 ERROR("Volume::_ChangeActivation(): package to reactivate " 1558 "not found: \"%s\"\n", item->name); 1559 RETURN_ERROR(B_NAME_NOT_FOUND); 1560 } 1561 oldPackageCount++; 1562 newPackageCount++; 1563 } else 1564 RETURN_ERROR(B_BAD_VALUE); 1565 } 1566 } 1567 1568 INFORM("Volume::_ChangeActivation(): %" B_PRId32 " new packages, %" B_PRId32 1569 " old packages\n", newPackageCount, oldPackageCount); 1570 1571 // Things look good so far -- allocate reference arrays for the packages to 1572 // add and remove. 1573 BReference<Package>* newPackageReferences 1574 = new(std::nothrow) BReference<Package>[newPackageCount]; 1575 if (newPackageReferences == NULL) 1576 RETURN_ERROR(B_NO_MEMORY); 1577 ArrayDeleter<BReference<Package> > newPackageReferencesDeleter( 1578 newPackageReferences); 1579 1580 BReference<Package>* oldPackageReferences 1581 = new(std::nothrow) BReference<Package>[oldPackageCount]; 1582 if (oldPackageReferences == NULL) 1583 RETURN_ERROR(B_NO_MEMORY); 1584 ArrayDeleter<BReference<Package> > oldPackageReferencesDeleter( 1585 oldPackageReferences); 1586 1587 // load all new packages 1588 int32 newPackageIndex = 0; 1589 for (uint32 i = 0; i < itemCount; i++) { 1590 PackageFSActivationChangeItem* item = request.ItemAt(i); 1591 1592 if (item->type != PACKAGE_FS_ACTIVATE_PACKAGE 1593 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) { 1594 continue; 1595 } 1596 1597 Package* package; 1598 status_t error = _LoadPackage(fPackagesDirectory, item->name, package); 1599 if (error != B_OK) { 1600 ERROR("Volume::_ChangeActivation(): failed to load package " 1601 "\"%s\"\n", item->name); 1602 RETURN_ERROR(error); 1603 } 1604 1605 newPackageReferences[newPackageIndex++].SetTo(package, true); 1606 } 1607 1608 // apply the changes 1609 VolumeWriteLocker systemVolumeLocker(_SystemVolumeIfNotSelf()); 1610 VolumeWriteLocker volumeLocker(this); 1611 // TODO: Add a change counter to Volume, so we can easily check whether 1612 // everything is still the same. 1613 1614 // remove the old packages 1615 int32 oldPackageIndex = 0; 1616 for (uint32 i = 0; i < itemCount; i++) { 1617 PackageFSActivationChangeItem* item = request.ItemAt(i); 1618 1619 if (item->type != PACKAGE_FS_DEACTIVATE_PACKAGE 1620 && item->type != PACKAGE_FS_REACTIVATE_PACKAGE) { 1621 continue; 1622 } 1623 1624 Package* package = _FindPackage(item->name); 1625 // TODO: We should better look up the package by node_ref! 1626 oldPackageReferences[oldPackageIndex++].SetTo(package); 1627 _RemovePackageContent(package, NULL, true); 1628 _RemovePackage(package); 1629 1630 INFORM("package \"%s\" deactivated\n", package->FileName().Data()); 1631 } 1632 // TODO: Since package removal cannot fail, consider adding the new packages 1633 // first. The reactivation case may make that problematic, since two packages 1634 // with the same name would be active after activating the new one. Check! 1635 1636 // add the new packages 1637 status_t error = B_OK; 1638 for (newPackageIndex = 0; newPackageIndex < newPackageCount; 1639 newPackageIndex++) { 1640 Package* package = newPackageReferences[newPackageIndex]; 1641 _AddPackage(package); 1642 1643 // add the package to the node tree 1644 error = _AddPackageContent(package, true); 1645 if (error != B_OK) { 1646 _RemovePackage(package); 1647 break; 1648 } 1649 INFORM("package \"%s\" activated\n", package->FileName().Data()); 1650 } 1651 1652 // Try to roll back the changes, if an error occurred. 1653 if (error != B_OK) { 1654 for (int32 i = newPackageIndex - 1; i >= 0; i--) { 1655 Package* package = newPackageReferences[i]; 1656 _RemovePackageContent(package, NULL, true); 1657 _RemovePackage(package); 1658 } 1659 1660 for (int32 i = oldPackageCount - 1; i >= 0; i--) { 1661 Package* package = oldPackageReferences[i]; 1662 _AddPackage(package); 1663 1664 if (_AddPackageContent(package, true) != B_OK) { 1665 // nothing we can do here 1666 ERROR("Volume::_ChangeActivation(): failed to roll back " 1667 "deactivation of package \"%s\" after error\n", 1668 package->FileName().Data()); 1669 _RemovePackage(package); 1670 } 1671 } 1672 } 1673 1674 return error; 1675 } 1676 1677 1678 status_t 1679 Volume::_InitMountType(const char* mountType) 1680 { 1681 if (mountType == NULL) 1682 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM; 1683 else if (strcmp(mountType, "system") == 0) 1684 fMountType = PACKAGE_FS_MOUNT_TYPE_SYSTEM; 1685 else if (strcmp(mountType, "home") == 0) 1686 fMountType = PACKAGE_FS_MOUNT_TYPE_HOME; 1687 else if (strcmp(mountType, "custom") == 0) 1688 fMountType = PACKAGE_FS_MOUNT_TYPE_CUSTOM; 1689 else 1690 RETURN_ERROR(B_BAD_VALUE); 1691 1692 return B_OK; 1693 } 1694 1695 1696 status_t 1697 Volume::_CreateShineThroughDirectory(Directory* parent, const char* name, 1698 Directory*& _directory) 1699 { 1700 ShineThroughDirectory* directory = new(std::nothrow) ShineThroughDirectory( 1701 fNextNodeID++); 1702 if (directory == NULL) 1703 RETURN_ERROR(B_NO_MEMORY); 1704 BReference<ShineThroughDirectory> directoryReference(directory, true); 1705 1706 String nameString; 1707 if (!nameString.SetTo(name)) 1708 RETURN_ERROR(B_NO_MEMORY); 1709 1710 status_t error = directory->Init(parent, nameString); 1711 if (error != B_OK) 1712 RETURN_ERROR(error); 1713 1714 parent->AddChild(directory); 1715 1716 fNodes.Insert(directory); 1717 directoryReference.Detach(); 1718 // we keep the initial node reference for the table 1719 1720 _directory = directory; 1721 return B_OK; 1722 } 1723 1724 1725 status_t 1726 Volume::_CreateShineThroughDirectories(const char* shineThroughSetting) 1727 { 1728 // get the directories to map 1729 const char* const* directories = NULL; 1730 1731 if (shineThroughSetting == NULL) { 1732 // nothing specified -- derive from mount type 1733 switch (fMountType) { 1734 case PACKAGE_FS_MOUNT_TYPE_SYSTEM: 1735 case PACKAGE_FS_MOUNT_TYPE_HOME: 1736 directories = kShineThroughDirectories; 1737 break; 1738 case PACKAGE_FS_MOUNT_TYPE_CUSTOM: 1739 return B_OK; 1740 case PACKAGE_FS_MOUNT_TYPE_ENUM_COUNT: 1741 return B_BAD_VALUE; 1742 } 1743 } else if (strcmp(shineThroughSetting, "system") == 0) 1744 directories = kShineThroughDirectories; 1745 else if (strcmp(shineThroughSetting, "home") == 0) 1746 directories = kShineThroughDirectories; 1747 else if (strcmp(shineThroughSetting, "none") == 0) 1748 directories = NULL; 1749 else 1750 RETURN_ERROR(B_BAD_VALUE); 1751 1752 if (directories == NULL) 1753 return B_OK; 1754 1755 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1756 1757 // iterate through the directory list and create the directories 1758 while (const char* directoryName = *(directories++)) { 1759 // create the directory 1760 Directory* directory; 1761 status_t error = _CreateShineThroughDirectory(fRootDirectory, 1762 directoryName, directory); 1763 if (error != B_OK) 1764 RETURN_ERROR(error); 1765 } 1766 1767 return B_OK; 1768 } 1769 1770 1771 status_t 1772 Volume::_PublishShineThroughDirectories() 1773 { 1774 // Iterate through the root directory children and bind the shine-through 1775 // directories to the respective mount point subdirectories. 1776 Node* nextNode; 1777 for (Node* node = fRootDirectory->FirstChild(); node != NULL; 1778 node = nextNode) { 1779 nextNode = fRootDirectory->NextChild(node); 1780 1781 // skip anything but shine-through directories 1782 ShineThroughDirectory* directory 1783 = dynamic_cast<ShineThroughDirectory*>(node); 1784 if (directory == NULL) 1785 continue; 1786 1787 const char* directoryName = directory->Name(); 1788 1789 // look up the mount point subdirectory 1790 struct vnode* vnode; 1791 status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID, 1792 fMountPoint.nodeID, directoryName, &vnode); 1793 if (error != B_OK) { 1794 dprintf("packagefs: Failed to get shine-through directory \"%s\": " 1795 "%s\n", directoryName, strerror(error)); 1796 _RemoveNode(directory); 1797 continue; 1798 } 1799 VnodePutter vnodePutter(vnode); 1800 1801 // stat it 1802 struct stat st; 1803 error = vfs_stat_vnode(vnode, &st); 1804 if (error != B_OK) { 1805 dprintf("packagefs: Failed to stat shine-through directory \"%s\": " 1806 "%s\n", directoryName, strerror(error)); 1807 _RemoveNode(directory); 1808 continue; 1809 } 1810 1811 if (!S_ISDIR(st.st_mode)) { 1812 dprintf("packagefs: Shine-through entry \"%s\" is not a " 1813 "directory\n", directoryName); 1814 _RemoveNode(directory); 1815 continue; 1816 } 1817 1818 // publish the vnode, so the VFS will find it without asking us 1819 directory->AcquireReference(); 1820 error = PublishVNode(directory); 1821 if (error != B_OK) { 1822 directory->ReleaseReference(); 1823 _RemoveNode(directory); 1824 RETURN_ERROR(error); 1825 } 1826 1827 // bind the directory 1828 error = vfs_bind_mount_directory(st.st_dev, st.st_ino, fFSVolume->id, 1829 directory->ID()); 1830 1831 PutVNode(directory->ID()); 1832 // release our reference again -- on success 1833 // vfs_bind_mount_directory() got one 1834 1835 if (error != B_OK) 1836 RETURN_ERROR(error); 1837 } 1838 1839 return B_OK; 1840 } 1841 1842 1843 status_t 1844 Volume::_AddPackageLinksDirectory() 1845 { 1846 // called when mounting, so we don't need to lock the volume 1847 1848 PackageLinksDirectory* packageLinksDirectory 1849 = fPackageFSRoot->GetPackageLinksDirectory(); 1850 1851 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1852 NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory); 1853 1854 packageLinksDirectory->SetParent(fRootDirectory); 1855 fRootDirectory->AddChild(packageLinksDirectory); 1856 1857 _AddPackageLinksNode(packageLinksDirectory); 1858 1859 packageLinksDirectory->SetListener(this); 1860 1861 return B_OK; 1862 } 1863 1864 1865 void 1866 Volume::_RemovePackageLinksDirectory() 1867 { 1868 PackageLinksDirectory* packageLinksDirectory 1869 = fPackageFSRoot->GetPackageLinksDirectory(); 1870 1871 VolumeWriteLocker volumeLocker(this); 1872 NodeWriteLocker rootDirectoryWriteLocker(fRootDirectory); 1873 NodeWriteLocker packageLinksDirectoryWriteLocker(packageLinksDirectory); 1874 1875 if (packageLinksDirectory->Parent() == fRootDirectory) { 1876 packageLinksDirectory->SetListener(NULL); 1877 fRootDirectory->RemoveChild(packageLinksDirectory); 1878 packageLinksDirectory->SetParent(NULL); 1879 } 1880 } 1881 1882 1883 void 1884 Volume::_AddPackageLinksNode(Node* node) 1885 { 1886 node->SetID(fNextNodeID++); 1887 1888 fNodes.Insert(node); 1889 node->AcquireReference(); 1890 1891 // If this is a directory, recursively add descendants. The directory tree 1892 // for the package links isn't deep, so we can do recursion. 1893 if (Directory* directory = dynamic_cast<Directory*>(node)) { 1894 for (Node* child = directory->FirstChild(); child != NULL; 1895 child = directory->NextChild(child)) { 1896 NodeWriteLocker childWriteLocker(child); 1897 _AddPackageLinksNode(child); 1898 } 1899 } 1900 } 1901 1902 1903 void 1904 Volume::_RemovePackageLinksNode(Node* node) 1905 { 1906 // If this is a directory, recursively remove descendants. The directory 1907 // tree for the package links isn't deep, so we can do recursion. 1908 if (Directory* directory = dynamic_cast<Directory*>(node)) { 1909 for (Node* child = directory->FirstChild(); child != NULL; 1910 child = directory->NextChild(child)) { 1911 NodeWriteLocker childWriteLocker(child); 1912 _RemovePackageLinksNode(child); 1913 } 1914 } 1915 1916 fNodes.Remove(node); 1917 node->ReleaseReference(); 1918 } 1919 1920 1921 inline Volume* 1922 Volume::_SystemVolumeIfNotSelf() const 1923 { 1924 if (Volume* systemVolume = fPackageFSRoot->SystemVolume()) 1925 return systemVolume == this ? NULL : systemVolume; 1926 return NULL; 1927 } 1928 1929 1930 void 1931 Volume::_NotifyNodeAdded(Node* node) 1932 { 1933 Node* key = node; 1934 1935 for (int i = 0; i < 2; i++) { 1936 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1937 NodeListener* last = listener->PreviousNodeListener(); 1938 1939 while (true) { 1940 NodeListener* next = listener->NextNodeListener(); 1941 1942 listener->NodeAdded(node); 1943 1944 if (listener == last) 1945 break; 1946 1947 listener = next; 1948 } 1949 } 1950 1951 key = NULL; 1952 } 1953 } 1954 1955 1956 void 1957 Volume::_NotifyNodeRemoved(Node* node) 1958 { 1959 Node* key = node; 1960 1961 for (int i = 0; i < 2; i++) { 1962 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1963 NodeListener* last = listener->PreviousNodeListener(); 1964 1965 while (true) { 1966 NodeListener* next = listener->NextNodeListener(); 1967 1968 listener->NodeRemoved(node); 1969 1970 if (listener == last) 1971 break; 1972 1973 listener = next; 1974 } 1975 } 1976 1977 key = NULL; 1978 } 1979 } 1980 1981 1982 void 1983 Volume::_NotifyNodeChanged(Node* node, uint32 statFields, 1984 const OldNodeAttributes& oldAttributes) 1985 { 1986 Node* key = node; 1987 1988 for (int i = 0; i < 2; i++) { 1989 if (NodeListener* listener = fNodeListeners.Lookup(key)) { 1990 NodeListener* last = listener->PreviousNodeListener(); 1991 1992 while (true) { 1993 NodeListener* next = listener->NextNodeListener(); 1994 1995 listener->NodeChanged(node, statFields, oldAttributes); 1996 1997 if (listener == last) 1998 break; 1999 2000 listener = next; 2001 } 2002 } 2003 2004 key = NULL; 2005 } 2006 } 2007