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