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