diff --git a/src/add-ons/kernel/file_systems/packagefs/Directory.cpp b/src/add-ons/kernel/file_systems/packagefs/Directory.cpp index cdcb55c35d..e8b11901d5 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Directory.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Directory.cpp @@ -7,6 +7,7 @@ #include "Directory.h" #include "DebugSupport.h" +#include "Utils.h" Directory::Directory(ino_t id) @@ -105,12 +106,43 @@ Directory::AddPackageNode(PackageNode* packageNode) PackageDirectory* packageDirectory = dynamic_cast(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(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() { diff --git a/src/add-ons/kernel/file_systems/packagefs/Directory.h b/src/add-ons/kernel/file_systems/packagefs/Directory.h index 2d643bc005..06d55abddb 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Directory.h +++ b/src/add-ons/kernel/file_systems/packagefs/Directory.h @@ -41,6 +41,7 @@ public: virtual off_t FileSize() const; virtual status_t AddPackageNode(PackageNode* packageNode); + virtual void RemovePackageNode(PackageNode* packageNode); virtual PackageNode* GetPackageNode(); diff --git a/src/add-ons/kernel/file_systems/packagefs/LeafNode.cpp b/src/add-ons/kernel/file_systems/packagefs/LeafNode.cpp index 6e90858d47..43804d67d2 100644 --- a/src/add-ons/kernel/file_systems/packagefs/LeafNode.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/LeafNode.cpp @@ -6,6 +6,8 @@ #include "LeafNode.h" +#include "Utils.h" + LeafNode::LeafNode(ino_t id) : @@ -96,12 +98,51 @@ LeafNode::AddPackageNode(PackageNode* packageNode) if (S_ISDIR(packageNode->Mode())) return B_BAD_VALUE; - fPackageNodes.Add(dynamic_cast(packageNode)); + PackageLeafNode* packageLeafNode + = dynamic_cast(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(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() { diff --git a/src/add-ons/kernel/file_systems/packagefs/LeafNode.h b/src/add-ons/kernel/file_systems/packagefs/LeafNode.h index 64f563cca0..d680e19273 100644 --- a/src/add-ons/kernel/file_systems/packagefs/LeafNode.h +++ b/src/add-ons/kernel/file_systems/packagefs/LeafNode.h @@ -27,6 +27,7 @@ public: virtual off_t FileSize() const; virtual status_t AddPackageNode(PackageNode* packageNode); + virtual void RemovePackageNode(PackageNode* packageNode); virtual PackageNode* GetPackageNode(); diff --git a/src/add-ons/kernel/file_systems/packagefs/Node.h b/src/add-ons/kernel/file_systems/packagefs/Node.h index 6f72eb4962..f02d9f05b1 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Node.h +++ b/src/add-ons/kernel/file_systems/packagefs/Node.h @@ -52,6 +52,7 @@ public: virtual off_t FileSize() const = 0; virtual status_t AddPackageNode(PackageNode* packageNode) = 0; + virtual void RemovePackageNode(PackageNode* packageNode) = 0; virtual PackageNode* GetPackageNode() = 0; diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp index f2627490f5..f060f2e89d 100644 --- a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp @@ -11,19 +11,34 @@ #include #include +#include + +#include + +#include +#include + #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 @@ PackageDomain::Init(const char* path) status_t -PackageDomain::Prepare() +PackageDomain::Prepare(NotificationListener* listener) { + ObjectDeleter 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 @@ PackageDomain::RemovePackage(Package* package) fPackages.Remove(package); package->ReleaseReference(); } + + +Package* +PackageDomain::FindPackage(const char* name) const +{ + return fPackages.Lookup(name); +} diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h index 0956142c8e..e203f39f36 100644 --- a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h +++ b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h @@ -6,31 +6,45 @@ #define PACKAGE_DOMAIN_H +#include + #include #include "Package.h" -class PackageDomain : public DoublyLinkedListLinkImpl { +class NotificationListener; + + +class PackageDomain : public BReferenceable, + public DoublyLinkedListLinkImpl { 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; }; diff --git a/src/add-ons/kernel/file_systems/packagefs/Utils.h b/src/add-ons/kernel/file_systems/packagefs/Utils.h new file mode 100644 index 0000000000..853792b017 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Utils.h @@ -0,0 +1,22 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * 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 diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp index 950332a65a..40edec2537 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp @@ -14,10 +14,15 @@ #include +#include #include +#include #include +#include +#include + #include "ErrorOutput.h" #include "FDCloser.h" #include "PackageEntry.h" @@ -67,15 +72,17 @@ struct Volume::AddPackageDomainJob : Job { 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 @@ private: }; +// #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 @@ private: }; +// #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 @@ Volume::~Volume() _TerminatePackageLoader(); while (PackageDomain* packageDomain = fPackageDomains.RemoveHead()) - delete packageDomain; + packageDomain->ReleaseReference(); // remove all nodes from the ID hash table Node* node = fNodes.Clear(true); @@ -319,6 +391,20 @@ Volume::GetVNode(ino_t nodeID, Node*& _node) } +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) { @@ -333,7 +419,7 @@ Volume::AddPackageDomain(const char* path) PackageDomain* packageDomain = new(std::nothrow) PackageDomain; if (packageDomain == NULL) RETURN_ERROR(B_NO_MEMORY); - ObjectDeleter packageDomainDeleter(packageDomain); + BReference packageDomainReference(packageDomain, true); status_t error = packageDomain->Init(path); if (error != B_OK) @@ -342,7 +428,6 @@ Volume::AddPackageDomain(const char* path) Job* job = new(std::nothrow) AddPackageDomainJob(this, packageDomain); if (job == NULL) RETURN_ERROR(B_NO_MEMORY); - packageDomainDeleter.Detach(); _PushJob(job); @@ -408,6 +493,7 @@ Volume::_PushJob(Job* job) { MutexLocker jobQueueLocker(fJobQueueLock); fJobQueue.Add(job); + fJobQueueCondition.NotifyOne(); } @@ -417,31 +503,33 @@ Volume::_AddInitialPackageDomain(const char* path) PackageDomain* domain = new(std::nothrow) PackageDomain; if (domain == NULL) RETURN_ERROR(B_NO_MEMORY); - ObjectDeleter domainDeleter(domain); + BReference 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 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 @@ Volume::_AddPackageDomain(PackageDomain* domain) 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 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 @@ Volume::_LoadPackage(Package* package) 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; } -status_t -Volume::_AddPackageContentRootNode(Package* package, PackageNode* packageNode) +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* 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 @@ Volume::_AddPackageContentRootNode(Package* package, PackageNode* packageNode) } } - // 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 @@ Volume::_AddPackageContentRootNode(Package* package, PackageNode* packageNode) } +/*! 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(packageNode)) { + if (packageDirectory->FirstChild() != NULL) { + directory = dynamic_cast( + 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 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 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 @@ Volume::_CreateNode(mode_t mode, Directory* parent, const char* name, _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 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) + return; + BReference packageReference(package); + + // remove the package + VolumeWriteLocker volumeLocker(this); + _RemovePackageContent(package, NULL, true); + domain->RemovePackage(package); +} + + +void +Volume::_DomainEntryMoved(PackageDomain* domain, dev_t deviceID, + ino_t fromDirectoryID, ino_t toDirectoryID, dev_t nodeDeviceID, + ino_t nodeID, const char* fromName, const char* name, bool notify) +{ + _DomainEntryRemoved(domain, deviceID, fromDirectoryID, nodeID, fromName, + notify); + _DomainEntryCreated(domain, deviceID, toDirectoryID, nodeID, name, true, + notify); +} diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.h b/src/add-ons/kernel/file_systems/packagefs/Volume.h index cfae90427b..99a9b8e501 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.h +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.h @@ -43,6 +43,8 @@ public: // VFS wrappers status_t GetVNode(ino_t nodeID, Node*& _node); + status_t PutVNode(ino_t nodeID); + status_t RemoveVNode(ino_t nodeID); status_t PublishVNode(Node* node); status_t AddPackageDomain(const char* path); @@ -50,8 +52,10 @@ public: private: struct Job; struct AddPackageDomainJob; + struct DomainDirectoryEventJob; struct PackageLoaderErrorOutput; struct PackageLoaderContentHandler; + struct DomainDirectoryListener; typedef DoublyLinkedList JobList; typedef DoublyLinkedList PackageDomainList; @@ -65,16 +69,49 @@ private: void _PushJob(Job* job); status_t _AddInitialPackageDomain(const char* path); - status_t _AddPackageDomain(PackageDomain* domain); + status_t _AddPackageDomain(PackageDomain* domain, + bool notify); status_t _LoadPackage(Package* package); - status_t _AddPackageContent(Package* package); + + status_t _AddPackageContent(Package* package, + bool notify); + void _RemovePackageContent(Package* package, + PackageNode* endNode, bool notify); + status_t _AddPackageContentRootNode(Package* package, - PackageNode* node); + PackageNode* node, bool notify); + void _RemovePackageContentRootNode(Package* package, + PackageNode* packageNode, + PackageNode* endPackageNode, bool notify); + status_t _AddPackageNode(Directory* directory, - PackageNode* packageNode, Node*& _node); + PackageNode* packageNode, bool notify, + Node*& _node); + void _RemovePackageNode(Directory* directory, + PackageNode* packageNode, Node* node, + bool notify); status_t _CreateNode(mode_t mode, Directory* parent, const char* name, Node*& _node); + // does *not* return a reference + void _RemoveNode(Node* node); + + void _DomainListenerEventOccurred( + PackageDomain* domain, + const KMessage* event); + void _DomainEntryCreated(PackageDomain* domain, + dev_t deviceID, ino_t directoryID, + ino_t nodeID, const char* name, + bool addContent, bool notify); + void _DomainEntryRemoved(PackageDomain* domain, + dev_t deviceID, ino_t directoryID, + ino_t nodeID, const char* name, + bool notify); + void _DomainEntryMoved(PackageDomain* domain, + dev_t deviceID, ino_t fromDirectoryID, + ino_t toDirectoryID, dev_t nodeDeviceID, + ino_t nodeID, const char* fromName, + const char* name, bool notify); private: rw_lock fLock;