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