[haiku-commits] r34172 - haiku/trunk/src/add-ons/kernel/file_systems/packagefs

  • From: ingo_weinhold@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Sat, 21 Nov 2009 16:43:37 +0100 (CET)

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 ...]

Other related posts:

  • » [haiku-commits] r34172 - haiku/trunk/src/add-ons/kernel/file_systems/packagefs - ingo_weinhold