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