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