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