Author: bonefish Date: 2009-11-21 16:43:37 +0100 (Sat, 21 Nov 2009) New Revision: 34172 Changeset: http://dev.haiku-os.org/changeset/34172/haiku Added: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Utils.h Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Directory.cpp haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Directory.h haiku/trunk/src/add-ons/kernel/file_systems/packagefs/LeafNode.cpp haiku/trunk/src/add-ons/kernel/file_systems/packagefs/LeafNode.h haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Node.h haiku/trunk/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp haiku/trunk/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Volume.cpp haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Volume.h Log: * Made PackageDomain BReferenceable. * LeafNode/Directory: We make sure that the added PackageNode with the newest modified time is always at the head of the list. This is the package node whose stat, attribute data and, in case of non-directory nodes, also content data will be returned. This results in a well-defined algorithm what happens when multiple directories contain the same file -- the newest file wins. * Resolved most TODOs regarding error handling when adding a package domain, package, or package node. * Volume::_AddPackageContentRootNode(): Fixed the back tracking loop. It would just continue with siblings of the given root node, causing them to be added more than once, leading to data structure corruption. * We now listen to the packages directory. When packages are added or removed their contents is automatically added/removed to the file system. We don't listen to the package files themselves yet. I.e. modifying an "installed" package in place respectively copying/downloading it there will probably not work correctly. Due to a limitation in the userlandfs the mechanism works correctly only when using the kernel module. When running the userland version, one better leaves the packages directory alone while packagefs is mounted. Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Directory.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Directory.cpp 2009-11-21 15:26:19 UTC (rev 34171) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Directory.cpp 2009-11-21 15:43:37 UTC (rev 34172) @@ -7,6 +7,7 @@ #include "Directory.h" #include "DebugSupport.h" +#include "Utils.h" Directory::Directory(ino_t id) @@ -105,12 +106,43 @@ PackageDirectory* packageDirectory = dynamic_cast<PackageDirectory*>(packageNode); - fPackageDirectories.Add(packageDirectory); + PackageDirectory* other = fPackageDirectories.Head(); + bool isNewest = other == NULL + || packageDirectory->ModifiedTime() > other->ModifiedTime(); + if (isNewest) + fPackageDirectories.Insert(other, packageDirectory); + else + fPackageDirectories.Add(packageDirectory); + return B_OK; } +void +Directory::RemovePackageNode(PackageNode* packageNode) +{ + bool isNewest = packageNode == fPackageDirectories.Head(); + fPackageDirectories.Remove(dynamic_cast<PackageDirectory*>(packageNode)); + + // when removing the newest node, we need to find the next node (the list + // is not sorted) + PackageDirectory* newestNode = fPackageDirectories.Head(); + if (isNewest && newestNode != NULL) { + PackageDirectoryList::Iterator it = fPackageDirectories.GetIterator(); + it.Next(); + // skip the first one + while (PackageDirectory* otherNode = it.Next()) { + if (otherNode->ModifiedTime() > newestNode->ModifiedTime()) + newestNode = otherNode; + } + + fPackageDirectories.Remove(newestNode); + fPackageDirectories.Insert(fPackageDirectories.Head(), newestNode); + } +} + + PackageNode* Directory::GetPackageNode() { Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Directory.h =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Directory.h 2009-11-21 15:26:19 UTC (rev 34171) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Directory.h 2009-11-21 15:43:37 UTC (rev 34172) @@ -41,6 +41,7 @@ virtual off_t FileSize() const; virtual status_t AddPackageNode(PackageNode* packageNode); + virtual void RemovePackageNode(PackageNode* packageNode); virtual PackageNode* GetPackageNode(); Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/LeafNode.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/LeafNode.cpp 2009-11-21 15:26:19 UTC (rev 34171) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/LeafNode.cpp 2009-11-21 15:43:37 UTC (rev 34172) @@ -6,7 +6,9 @@ #include "LeafNode.h" +#include "Utils.h" + LeafNode::LeafNode(ino_t id) : Node(id) @@ -96,12 +98,51 @@ if (S_ISDIR(packageNode->Mode())) return B_BAD_VALUE; - fPackageNodes.Add(dynamic_cast<PackageLeafNode*>(packageNode)); + PackageLeafNode* packageLeafNode + = dynamic_cast<PackageLeafNode*>(packageNode); + PackageLeafNode* headNode = fPackageNodes.Head(); + bool isNewest = headNode == NULL + || packageLeafNode->ModifiedTime() > headNode->ModifiedTime(); + + if (isNewest) { + fPackageNodes.Add(packageLeafNode); + } else { + // add after the head + fPackageNodes.RemoveHead(); + fPackageNodes.Add(packageLeafNode); + fPackageNodes.Add(headNode); + } + return B_OK; } +void +LeafNode::RemovePackageNode(PackageNode* packageNode) +{ + bool isNewest = packageNode == fPackageNodes.Head(); + fPackageNodes.Remove(dynamic_cast<PackageLeafNode*>(packageNode)); + + // when removing the newest node, we need to find the next node (the list + // is not sorted) + PackageLeafNode* newestNode = fPackageNodes.Head(); + if (isNewest && newestNode != NULL) { + PackageLeafNodeList::Iterator it = fPackageNodes.GetIterator(); + it.Next(); + // skip the first one + while (PackageLeafNode* otherNode = it.Next()) { + if (otherNode->ModifiedTime() > newestNode->ModifiedTime()) + newestNode = otherNode; + } + + // re-add the newest node to the head + fPackageNodes.Remove(newestNode); + fPackageNodes.Add(newestNode); + } +} + + PackageNode* LeafNode::GetPackageNode() { Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/LeafNode.h =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/LeafNode.h 2009-11-21 15:26:19 UTC (rev 34171) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/LeafNode.h 2009-11-21 15:43:37 UTC (rev 34172) @@ -27,6 +27,7 @@ virtual off_t FileSize() const; virtual status_t AddPackageNode(PackageNode* packageNode); + virtual void RemovePackageNode(PackageNode* packageNode); virtual PackageNode* GetPackageNode(); Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Node.h =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Node.h 2009-11-21 15:26:19 UTC (rev 34171) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Node.h 2009-11-21 15:43:37 UTC (rev 34172) @@ -52,6 +52,7 @@ virtual off_t FileSize() const = 0; virtual status_t AddPackageNode(PackageNode* packageNode) = 0; + virtual void RemovePackageNode(PackageNode* packageNode) = 0; virtual PackageNode* GetPackageNode() = 0; Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp 2009-11-21 15:26:19 UTC (rev 34171) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp 2009-11-21 15:43:37 UTC (rev 34172) @@ -11,19 +11,34 @@ #include <stdlib.h> #include <string.h> +#include <NodeMonitor.h> + +#include <AutoDeleter.h> + +#include <fs/node_monitor.h> +#include <Notifications.h> + #include "DebugSupport.h" PackageDomain::PackageDomain() : fPath(NULL), - fDirFD(-1) + fDirFD(-1), + fListener(NULL) { } PackageDomain::~PackageDomain() { + PRINT("PackageDomain::~PackageDomain()\n"); + + if (fListener != NULL) { + remove_node_listener(fDeviceID, fNodeID, *fListener); + delete fListener; + } + Package* package = fPackages.Clear(true); while (package != NULL) { Package* next = package->HashTableNext(); @@ -54,14 +69,29 @@ status_t -PackageDomain::Prepare() +PackageDomain::Prepare(NotificationListener* listener) { + ObjectDeleter<NotificationListener> listenerDeleter(listener); + fDirFD = open(fPath, O_RDONLY); if (fDirFD < 0) { ERROR("Failed to open package domain \"%s\"\n", strerror(errno)); return errno; } + struct stat st; + if (fstat(fDirFD, &st) < 0) + RETURN_ERROR(errno); + + fDeviceID = st.st_dev; + fNodeID = st.st_ino; + + status_t error = add_node_listener(fDeviceID, fNodeID, B_WATCH_DIRECTORY, + *listener); + if (error != B_OK) + RETURN_ERROR(error); + fListener = listenerDeleter.Detach(); + return B_OK; } @@ -80,3 +110,10 @@ fPackages.Remove(package); package->ReleaseReference(); } + + +Package* +PackageDomain::FindPackage(const char* name) const +{ + return fPackages.Lookup(name); +} Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h 2009-11-21 15:26:19 UTC (rev 34171) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h 2009-11-21 15:43:37 UTC (rev 34172) @@ -6,31 +6,45 @@ #define PACKAGE_DOMAIN_H +#include <Referenceable.h> + #include <util/DoublyLinkedList.h> #include "Package.h" -class PackageDomain : public DoublyLinkedListLinkImpl<PackageDomain> { +class NotificationListener; + + +class PackageDomain : public BReferenceable, + public DoublyLinkedListLinkImpl<PackageDomain> { public: PackageDomain(); ~PackageDomain(); const char* Path() const { return fPath; } int DirectoryFD() { return fDirFD; } + dev_t DeviceID() { return fDeviceID; } + ino_t NodeID() { return fNodeID; } status_t Init(const char* path); - status_t Prepare(); + status_t Prepare(NotificationListener* listener); + // takes over ownership of the listener void AddPackage(Package* package); void RemovePackage(Package* package); + Package* FindPackage(const char* name) const; + const PackageHashTable& Packages() const { return fPackages; } private: char* fPath; int fDirFD; + dev_t fDeviceID; + ino_t fNodeID; + NotificationListener* fListener; PackageHashTable fPackages; }; Added: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Utils.h =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Utils.h (rev 0) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Utils.h 2009-11-21 15:43:37 UTC (rev 34172) @@ -0,0 +1,22 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef UTILS_H +#define UTILS_H + + +inline bool operator<(const timespec& a, const timespec& b) +{ + return a.tv_sec < b.tv_sec + || (a.tv_sec == b.tv_sec && a.tv_nsec < b.tv_nsec); +} + + +inline bool operator>(const timespec& a, const timespec& b) +{ + return b < a; +} + + +#endif // UTILS_H Modified: haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Volume.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Volume.cpp 2009-11-21 15:26:19 UTC (rev 34171) +++ haiku/trunk/src/add-ons/kernel/file_systems/packagefs/Volume.cpp 2009-11-21 15:43:37 UTC (rev 34172) @@ -14,10 +14,15 @@ #include <new> +#include <AppDefs.h> #include <KernelExport.h> +#include <NodeMonitor.h> #include <AutoDeleter.h> +#include <Notifications.h> +#include <util/KMessage.h> + #include "ErrorOutput.h" #include "FDCloser.h" #include "PackageEntry.h" @@ -67,15 +72,17 @@ Job(volume), fDomain(domain) { + fDomain->AcquireReference(); } virtual ~AddPackageDomainJob() { + fDomain->ReleaseReference(); } virtual void Do() { - fVolume->_AddPackageDomain(fDomain); + fVolume->_AddPackageDomain(fDomain, true); fDomain = NULL; } @@ -84,6 +91,41 @@ }; +// #pragma mark - DomainDirectoryEventJob + + +struct Volume::DomainDirectoryEventJob : Job { + DomainDirectoryEventJob(Volume* volume, PackageDomain* domain) + : + Job(volume), + fDomain(domain), + fEvent(NULL) + { + fDomain->AcquireReference(); + } + + virtual ~DomainDirectoryEventJob() + { + fDomain->ReleaseReference(); + } + + status_t Init(const KMessage* event) + { + RETURN_ERROR(fEvent.SetTo(event->Buffer(), -1, + KMessage::KMESSAGE_CLONE_BUFFER)); + } + + virtual void Do() + { + fVolume->_DomainListenerEventOccurred(fDomain, &fEvent); + } + +private: + PackageDomain* fDomain; + KMessage fEvent; +}; + + // #pragma mark - PackageLoaderErrorOutput @@ -224,6 +266,36 @@ }; +// #pragma mark - DomainDirectoryListener + + +struct Volume::DomainDirectoryListener : NotificationListener { + DomainDirectoryListener(Volume* volume, PackageDomain* domain) + : + fVolume(volume), + fDomain(domain) + { + } + + virtual void EventOccurred(NotificationService& service, + const KMessage* event) + { + DomainDirectoryEventJob* job = new(std::nothrow) + DomainDirectoryEventJob(fVolume, fDomain); + if (job == NULL || job->Init(event)) { + delete job; + return; + } + + fVolume->_PushJob(job); + } + +private: + Volume* fVolume; + PackageDomain* fDomain; +}; + + // #pragma mark - Volume @@ -246,7 +318,7 @@ _TerminatePackageLoader(); while (PackageDomain* packageDomain = fPackageDomains.RemoveHead()) - delete packageDomain; + packageDomain->ReleaseReference(); // remove all nodes from the ID hash table Node* node = fNodes.Clear(true); @@ -320,6 +392,20 @@ status_t +Volume::PutVNode(ino_t nodeID) +{ + return put_vnode(fFSVolume, nodeID); +} + + +status_t +Volume::RemoveVNode(ino_t nodeID) +{ + return remove_vnode(fFSVolume, nodeID); +} + + +status_t Volume::PublishVNode(Node* node) { return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps, @@ -333,7 +419,7 @@ PackageDomain* packageDomain = new(std::nothrow) PackageDomain; if (packageDomain == NULL) RETURN_ERROR(B_NO_MEMORY); - ObjectDeleter<PackageDomain> packageDomainDeleter(packageDomain); + BReference<PackageDomain> packageDomainReference(packageDomain, true); status_t error = packageDomain->Init(path); if (error != B_OK) @@ -342,7 +428,6 @@ Job* job = new(std::nothrow) AddPackageDomainJob(this, packageDomain); if (job == NULL) RETURN_ERROR(B_NO_MEMORY); - packageDomainDeleter.Detach(); _PushJob(job); @@ -408,6 +493,7 @@ { MutexLocker jobQueueLocker(fJobQueueLock); fJobQueue.Add(job); + fJobQueueCondition.NotifyOne(); } @@ -417,31 +503,33 @@ PackageDomain* domain = new(std::nothrow) PackageDomain; if (domain == NULL) RETURN_ERROR(B_NO_MEMORY); - ObjectDeleter<PackageDomain> domainDeleter(domain); + BReference<PackageDomain> domainReference(domain, true); status_t error = domain->Init(path); if (error != B_OK) return error; - return _AddPackageDomain(domainDeleter.Detach()); + return _AddPackageDomain(domain, false); } status_t -Volume::_AddPackageDomain(PackageDomain* domain) +Volume::_AddPackageDomain(PackageDomain* domain, bool notify) { - ObjectDeleter<PackageDomain> domainDeleter(domain); + // create a directory listener + DomainDirectoryListener* listener = new(std::nothrow) + DomainDirectoryListener(this, domain); + if (listener == NULL) + RETURN_ERROR(B_NO_MEMORY); // prepare the package domain - status_t error = domain->Prepare(); + status_t error = domain->Prepare(listener); if (error != B_OK) { ERROR("Failed to prepare package domain \"%s\": %s\n", domain->Path(), strerror(errno)); return errno; } - // TODO: Start monitoring the directory! - // iterate through the dir and create packages DIR* dir = opendir(domain->Path()); if (dir == NULL) { @@ -456,44 +544,25 @@ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; - // check whether the entry is a file - struct stat st; - if (fstatat(domain->DirectoryFD(), entry->d_name, &st, - AT_SYMLINK_NOFOLLOW) < 0 - || !S_ISREG(st.st_mode)) { - continue; - } - - // create a package - Package* package = new(std::nothrow) Package(domain, st.st_dev, - st.st_ino); - if (package == NULL) - RETURN_ERROR(B_NO_MEMORY); - BReference<Package> packageReference(package, true); - - status_t error = package->Init(entry->d_name); - if (error != B_OK) - return error; - - error = _LoadPackage(package); - if (error != B_OK) - continue; - - domain->AddPackage(package); + _DomainEntryCreated(domain, domain->DeviceID(), domain->NodeID(), + -1, entry->d_name, false, notify); +// TODO: -1 node ID? } // add the packages to the node tree VolumeWriteLocker volumeLocker(this); for (PackageHashTable::Iterator it = domain->Packages().GetIterator(); Package* package = it.Next();) { - error = _AddPackageContent(package); + error = _AddPackageContent(package, notify); if (error != B_OK) { // TODO: Remove the already added packages! return error; } } - fPackageDomains.Add(domainDeleter.Detach()); + fPackageDomains.Add(domain); + domain->AcquireReference(); + return B_OK; } @@ -529,33 +598,64 @@ status_t -Volume::_AddPackageContent(Package* package) +Volume::_AddPackageContent(Package* package, bool notify) { for (PackageNodeList::Iterator it = package->Nodes().GetIterator(); PackageNode* node = it.Next();) { - status_t error = _AddPackageContentRootNode(package, node); + status_t error = _AddPackageContentRootNode(package, node, notify); if (error != B_OK) { -// TODO: Remove already added nodes! + _RemovePackageContent(package, node, notify); return error; } } -// TODO: Recursively add the nodes! return B_OK; } +void +Volume::_RemovePackageContent(Package* package, PackageNode* endNode, + bool notify) +{ + PackageNode* node = package->Nodes().Head(); + while (node != NULL) { + if (node == endNode) + break; + + PackageNode* nextNode = package->Nodes().GetNext(node); + _RemovePackageContentRootNode(package, node, NULL, notify); + node = nextNode; + } +} + + +/*! This method recursively iterates through the descendents of the given + package root node and adds all package nodes to the node tree in + pre-order. + Due to limited kernel stack space we avoid deep recursive function calls + and rather use the package node stack implied by the tree. +*/ status_t -Volume::_AddPackageContentRootNode(Package* package, PackageNode* packageNode) +Volume::_AddPackageContentRootNode(Package* package, + PackageNode* rootPackageNode, bool notify) { + PackageNode* packageNode = rootPackageNode; Directory* directory = fRootDirectory; directory->WriteLock(); do { Node* node; - status_t error = _AddPackageNode(directory, packageNode, node); + status_t error = _AddPackageNode(directory, packageNode, notify, node); if (error != B_OK) { -// TODO: Remove already added nodes! + // unlock all directories + while (directory != NULL) { + directory->WriteUnlock(); + directory = directory->Parent(); + } + + // remove the added package nodes + _RemovePackageContentRootNode(package, rootPackageNode, packageNode, + notify); return error; } @@ -570,11 +670,13 @@ } } - // continue with the next available (descendent's) sibling + // continue with the next available (ancestors's) sibling do { PackageDirectory* packageDirectory = packageNode->Parent(); - if (PackageNode* sibling - = packageDirectory->NextChild(packageNode)) { + PackageNode* sibling = packageDirectory != NULL + ? packageDirectory->NextChild(packageNode) : NULL; + + if (sibling != NULL) { packageNode = sibling; break; } @@ -591,28 +693,166 @@ } +/*! Recursively iterates through the descendents of the given package root node + and removes all package nodes from the node tree in post-order, until + encountering \a endPackageNode (if non-null). + Due to limited kernel stack space we avoid deep recursive function calls + and rather use the package node stack implied by the tree. +*/ +void +Volume::_RemovePackageContentRootNode(Package* package, + PackageNode* rootPackageNode, PackageNode* endPackageNode, bool notify) +{ + PackageNode* packageNode = rootPackageNode; + Directory* directory = fRootDirectory; + directory->WriteLock(); + + do { + if (packageNode == endPackageNode) + break; + + // recursive into directory + if (PackageDirectory* packageDirectory + = dynamic_cast<PackageDirectory*>(packageNode)) { + if (packageDirectory->FirstChild() != NULL) { + directory = dynamic_cast<Directory*>( + directory->FindChild(packageNode->Name())); + packageNode = packageDirectory->FirstChild(); + directory->WriteLock(); + continue; + } + } + + // continue with the next available (ancestors's) sibling + do { + PackageDirectory* packageDirectory = packageNode->Parent(); + PackageNode* sibling = packageDirectory != NULL + ? packageDirectory->NextChild(packageNode) : NULL; + + // we're done with the node -- remove it + _RemovePackageNode(directory, packageNode, + directory->FindChild(packageNode->Name()), notify); + + if (sibling != NULL) { + packageNode = sibling; + break; + } + + // no more siblings -- go back up the tree + packageNode = packageDirectory; + directory->WriteUnlock(); + directory = directory->Parent(); + // the parent is still locked, so this is safe + } while (packageNode != NULL/* && packageNode != rootPackageNode*/); + } while (packageNode != NULL/* && packageNode != rootPackageNode*/); +} + + status_t Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode, - Node*& _node) + bool notify, Node*& _node) { + bool newNode = false; Node* node = directory->FindChild(packageNode->Name()); if (node == NULL) { status_t error = _CreateNode(packageNode->Mode(), directory, packageNode->Name(), node); if (error != B_OK) return error; + newNode = true; } + BReference<Node> nodeReference(node); status_t error = node->AddPackageNode(packageNode); - if (error != B_OK) + if (error != B_OK) { + // remove the node, if created before + if (newNode) + _RemoveNode(node); return error; -// TODO: Remove the node, if created before! + } + if (notify) { + if (newNode) { + notify_entry_created(ID(), directory->ID(), node->Name(), + node->ID()); + } else if (packageNode == node->GetPackageNode()) { + // The new package node has become the one representing the node. + // Send stat changed notification for directories and entry + // removed + created notifications for files and symlinks. + if (S_ISDIR(packageNode->Mode())) { + notify_stat_changed(ID(), node->ID(), + B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE + | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME + | B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME); + // TODO: Actually the attributes might change, too! + } else { + notify_entry_removed(ID(), directory->ID(), node->Name(), + node->ID()); + notify_entry_created(ID(), directory->ID(), node->Name(), + node->ID()); + } + } + } + _node = node; return B_OK; } +void +Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode, + Node* node, bool notify) +{ + BReference<Node> nodeReference(node); + + PackageNode* headPackageNode = node->GetPackageNode(); + node->RemovePackageNode(packageNode); + + // If the node doesn't have any more package nodes attached, remove it + // completely. + bool nodeRemoved = false; + if (node->GetPackageNode() == NULL) { + // we get and put the vnode to notify the VFS + // TODO: We should probably only do that, if the node is known to the + // VFS in the first place. + Node* dummyNode; + bool gotVNode = GetVNode(node->ID(), dummyNode) == B_OK; + + _RemoveNode(node); + nodeRemoved = true; + + if (gotVNode) { + RemoveVNode(node->ID()); + PutVNode(node->ID()); + } + } + + if (!notify) + return; + + // send notifications + if (nodeRemoved) { + notify_entry_removed(ID(), directory->ID(), node->Name(), node->ID()); + } else if (packageNode == headPackageNode) { + // The new package node was the one representing the node. + // Send stat changed notification for directories and entry + // removed + created notifications for files and symlinks. + if (S_ISDIR(packageNode->Mode())) { + notify_stat_changed(ID(), node->ID(), + B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE + | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME + | B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME); + // TODO: Actually the attributes might change, too! + } else { + notify_entry_removed(ID(), directory->ID(), node->Name(), + node->ID()); + notify_entry_created(ID(), directory->ID(), node->Name(), + node->ID()); + } + } +} + + status_t Volume::_CreateNode(mode_t mode, Directory* parent, const char* name, Node*& _node) @@ -642,3 +882,165 @@ _node = node; return B_OK; } + + +void +Volume::_RemoveNode(Node* node) +{ + // remove from parent + Directory* parent = node->Parent(); + parent->RemoveChild(node); + + // remove from node table + fNodes.Remove(node); + node->ReleaseReference(); +} + + +void +Volume::_DomainListenerEventOccurred(PackageDomain* domain, + const KMessage* event) +{ + int32 opcode; + if (event->What() != B_NODE_MONITOR + || event->FindInt32("opcode", &opcode) != B_OK) { + return; + } + + switch (opcode) { + case B_ENTRY_CREATED: + { + int32 device; + int64 directory; + int64 node; + const char* name; + if (event->FindInt32("device", &device) == B_OK + && event->FindInt64("directory", &directory) == B_OK + && event->FindInt64("node", &node) == B_OK + && event->FindString("name", &name) == B_OK) { + _DomainEntryCreated(domain, device, directory, node, name, + true, true); + } + break; + } + + case B_ENTRY_REMOVED: + { + int32 device; + int64 directory; + int64 node; + const char* name; + if (event->FindInt32("device", &device) == B_OK + && event->FindInt64("directory", &directory) == B_OK + && event->FindInt64("node", &node) == B_OK + && event->FindString("name", &name) == B_OK) { + _DomainEntryRemoved(domain, device, directory, node, name, + true); + } + break; + } + + case B_ENTRY_MOVED: + { + int32 device; + int64 fromDirectory; + int64 toDirectory; + int32 nodeDevice; + int64 node; + const char* fromName; + const char* name; + if (event->FindInt32("device", &device) == B_OK + && event->FindInt64("from directory", &fromDirectory) == B_OK + && event->FindInt64("to directory", &toDirectory) == B_OK + && event->FindInt32("node device", &nodeDevice) == B_OK + && event->FindInt64("node", &node) == B_OK + && event->FindString("from name", &fromName) == B_OK + && event->FindString("name", &name) == B_OK) { + _DomainEntryMoved(domain, device, fromDirectory, toDirectory, + nodeDevice, node, fromName, name, true); + } + break; + } + + default: + break; + } +} + + +void +Volume::_DomainEntryCreated(PackageDomain* domain, dev_t deviceID, + ino_t directoryID, ino_t nodeID, const char* name, bool addContent, + bool notify) +{ + // let's see, if things look plausible + if (deviceID != domain->DeviceID() || directoryID != domain->NodeID() + || domain->FindPackage(name) != NULL) { + return; + } + + // check whether the entry is a file + struct stat st; + if (fstatat(domain->DirectoryFD(), name, &st, AT_SYMLINK_NOFOLLOW) < 0 + || !S_ISREG(st.st_mode)) { + return; + } + + // create a package + Package* package = new(std::nothrow) Package(domain, st.st_dev, st.st_ino); + if (package == NULL) + return; + BReference<Package> packageReference(package, true); + + status_t error = package->Init(name); + if (error != B_OK) + return; + + error = _LoadPackage(package); + if (error != B_OK) + return; + + VolumeWriteLocker volumeLocker(this); + domain->AddPackage(package); + + // add the package to the node tree + if (addContent) { + error = _AddPackageContent(package, notify); + if (error != B_OK) { + domain->RemovePackage(package); + return; + } + } +} + + +void +Volume::_DomainEntryRemoved(PackageDomain* domain, dev_t deviceID, + ino_t directoryID, ino_t nodeID, const char* name, bool notify) +{ + // let's see, if things look plausible + if (deviceID != domain->DeviceID() || directoryID != domain->NodeID()) + return; + + Package* package = domain->FindPackage(name); + if (package == NULL) [... truncated: 101 lines follow ...]