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