diff --git a/src/add-ons/kernel/file_systems/packagefs/Jamfile b/src/add-ons/kernel/file_systems/packagefs/Jamfile index 9dade89c75..34f2d68ee5 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Jamfile +++ b/src/add-ons/kernel/file_systems/packagefs/Jamfile @@ -18,6 +18,7 @@ HAIKU_PACKAGE_FS_SOURCES = GlobalFactory.cpp kernel_interface.cpp Node.cpp + NodeListener.cpp Package.cpp PackageDirectory.cpp PackageDomain.cpp diff --git a/src/add-ons/kernel/file_systems/packagefs/NodeListener.cpp b/src/add-ons/kernel/file_systems/packagefs/NodeListener.cpp new file mode 100644 index 0000000000..bd5b826a16 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/NodeListener.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "NodeListener.h" + + +NodeListener::NodeListener() + : + fPrevious(this), + fNext(this), + fNode(NOT_LISTENING_NODE) +{ +} + + +NodeListener::~NodeListener() +{ +} + + +void +NodeListener::NodeAdded(Node* node) +{ +} + + +void +NodeListener::NodeRemoved(Node* node) +{ +} + + +void +NodeListener::NodeChanged(Node* node, uint32 statFields) +{ +} diff --git a/src/add-ons/kernel/file_systems/packagefs/NodeListener.h b/src/add-ons/kernel/file_systems/packagefs/NodeListener.h new file mode 100644 index 0000000000..5c21c95d12 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/NodeListener.h @@ -0,0 +1,115 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef NODE_LISTENER_H +#define NODE_LISTENER_H + + +#include +#include + + +class Node; + + +#define NOT_LISTENING_NODE ((Node*)~(addr_t)0) + + +class NodeListener { +public: + NodeListener(); + virtual ~NodeListener(); + + virtual void NodeAdded(Node* node); + virtual void NodeRemoved(Node* node); + virtual void NodeChanged(Node* node, uint32 statFields); + + void StartedListening(Node* node) + { fNode = node; } + void StoppedListening() + { fNode = NOT_LISTENING_NODE; } + bool IsListening() const + { return fNode != NOT_LISTENING_NODE; } + Node* ListenedNode() const + { return fNode; } + + inline void AddNodeListener(NodeListener* listener); + inline NodeListener* RemoveNodeListener(); + + NodeListener* PreviousNodeListener() const + { return fPrevious; } + NodeListener* NextNodeListener() const + { return fNext; } + + NodeListener*& NodeListenerHashLink() + { return fHashLink; } + +private: + NodeListener* fHashLink; + NodeListener* fPrevious; + NodeListener* fNext; + Node* fNode; +}; + + +inline void +NodeListener::AddNodeListener(NodeListener* listener) +{ + listener->fPrevious = this; + listener->fNext = fNext; + + fNext->fPrevious = listener; + fNext = listener; +} + + +inline NodeListener* +NodeListener::RemoveNodeListener() +{ + if (fNext == this) + return NULL; + + NodeListener* next = fNext; + + fPrevious->fNext = next; + next->fPrevious = fPrevious; + + fPrevious = fNext = this; + + return next; +} + + + +struct NodeListenerHashDefinition { + typedef Node* KeyType; + typedef NodeListener ValueType; + + size_t HashKey(Node* key) const + { + return (size_t)key; + } + + size_t Hash(const NodeListener* value) const + { + return HashKey(value->ListenedNode()); + } + + bool Compare(Node* key, const NodeListener* value) const + { + return key == value->ListenedNode(); + } + + NodeListener*& GetLink(NodeListener* value) const + { + return value->NodeListenerHashLink(); + } +}; + + +typedef DoublyLinkedList NodeListenerList; +typedef BOpenHashTable NodeListenerHashTable; + + +#endif // NODE_LISTENER_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp index ba772f23cb..2a3cf330da 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp @@ -52,6 +52,10 @@ using BPackageKit::BHPKG::BPrivate::PackageReaderImpl; // node ID of the root directory static const ino_t kRootDirectoryID = 1; +static const uint32 kAllStatFields = 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; + // shine-through directories const char* const kSystemShineThroughDirectories[] = { "packages", NULL @@ -482,11 +486,16 @@ Volume::~Volume() status_t Volume::Mount(const char* parameterString) { - // init the node table + // init the hash tables status_t error = fNodes.Init(); if (error != B_OK) RETURN_ERROR(error); + error = fNodeListeners.Init(); + if (error != B_OK) + RETURN_ERROR(error); + + // get the mount parameters const char* packages = NULL; const char* volumeName = NULL; const char* mountType = NULL; @@ -605,6 +614,42 @@ Volume::Unmount() } +void +Volume::AddNodeListener(NodeListener* listener, Node* node) +{ + ASSERT(!listener->IsListening()); + + listener->StartedListening(node); + + if (NodeListener* list = fNodeListeners.Lookup(node)) + list->AddNodeListener(listener); + else + fNodeListeners.Insert(listener); +} + + +void +Volume::RemoveNodeListener(NodeListener* listener) +{ + ASSERT(listener->IsListening()); + + Node* node = listener->ListenedNode(); + + if (NodeListener* next = listener->RemoveNodeListener()) { + // list not empty yet -- if we removed the head, add a new head to the + // hash table + NodeListener* list = fNodeListeners.Lookup(node); + if (list == listener) { + fNodeListeners.Remove(listener); + fNodeListeners.Insert(next); + } + } else + fNodeListeners.Remove(listener); + + listener->StoppedListening(); +} + + status_t Volume::GetVNode(ino_t nodeID, Node*& _node) { @@ -662,6 +707,7 @@ Volume::PackageLinkNodeAdded(Node* node) _AddPackageLinksNode(node); notify_entry_created(ID(), node->Parent()->ID(), node->Name(), node->ID()); + _NotifyNodeAdded(node); } @@ -671,6 +717,7 @@ Volume::PackageLinkNodeRemoved(Node* node) _RemovePackageLinksNode(node); notify_entry_removed(ID(), node->Parent()->ID(), node->Name(), node->ID()); + _NotifyNodeRemoved(node); } @@ -678,6 +725,7 @@ void Volume::PackageLinkNodeChanged(Node* node, uint32 statFields) { notify_stat_changed(ID(), node->ID(), statFields); + _NotifyNodeChanged(node, statFields); } @@ -1069,6 +1117,11 @@ Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode, RETURN_ERROR(error); } + if (newNode) + _NotifyNodeAdded(node); + else + _NotifyNodeChanged(node, kAllStatFields); + if (notify) { if (newNode) { notify_entry_created(ID(), directory->ID(), node->Name(), @@ -1078,10 +1131,7 @@ Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode, // 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); + notify_stat_changed(ID(), node->ID(), kAllStatFields); // TODO: Actually the attributes might change, too! } else { notify_entry_removed(ID(), directory->ID(), node->Name(), @@ -1133,6 +1183,11 @@ Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode, } } + if (nodeRemoved) + _NotifyNodeRemoved(node); + else + _NotifyNodeChanged(node, kAllStatFields); + if (!notify) return; @@ -1144,10 +1199,7 @@ Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode, // 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); + notify_stat_changed(ID(), node->ID(), kAllStatFields); // TODO: Actually the attributes might change, too! } else { notify_entry_removed(ID(), directory->ID(), node->Name(), @@ -1556,3 +1608,81 @@ Volume::_SystemVolumeIfNotSelf() const return systemVolume == this ? NULL : systemVolume; return NULL; } + + +void +Volume::_NotifyNodeAdded(Node* node) +{ + Node* key = node; + + for (int i = 0; i < 2; i++) { + if (NodeListener* listener = fNodeListeners.Lookup(key)) { + NodeListener* last = listener->PreviousNodeListener(); + + while (true) { + NodeListener* next = listener->NextNodeListener(); + + listener->NodeAdded(node); + + if (listener == last) + break; + + listener = next; + } + } + + key = NULL; + } +} + + +void +Volume::_NotifyNodeRemoved(Node* node) +{ + Node* key = node; + + for (int i = 0; i < 2; i++) { + if (NodeListener* listener = fNodeListeners.Lookup(key)) { + NodeListener* last = listener->PreviousNodeListener(); + + while (true) { + NodeListener* next = listener->NextNodeListener(); + + listener->NodeRemoved(node); + + if (listener == last) + break; + + listener = next; + } + } + + key = NULL; + } +} + + +void +Volume::_NotifyNodeChanged(Node* node, uint32 statFields) +{ + Node* key = node; + + for (int i = 0; i < 2; i++) { + if (NodeListener* listener = fNodeListeners.Lookup(key)) { + NodeListener* last = listener->PreviousNodeListener(); + + while (true) { + NodeListener* next = listener->NextNodeListener(); + + listener->NodeChanged(node, statFields); + + if (listener == last) + break; + + listener = next; + } + } + + key = NULL; + } +} diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.h b/src/add-ons/kernel/file_systems/packagefs/Volume.h index c293aab9ed..6dc1fd4c23 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.h +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.h @@ -15,12 +15,12 @@ #include #include "Node.h" +#include "NodeListener.h" #include "PackageDomain.h" #include "PackageLinksListener.h" class Directory; -class Node; class PackageFSRoot; class UnpackingNode; @@ -66,6 +66,11 @@ public: Node* FindNode(ino_t nodeID) const { return fNodes.Lookup(nodeID); } + // node listeners -- volume must be write-locked + void AddNodeListener(NodeListener* listener, + Node* node); + void RemoveNodeListener(NodeListener* listener); + // VFS wrappers status_t GetVNode(ino_t nodeID, Node*& _node); status_t PutVNode(ino_t nodeID); @@ -162,6 +167,11 @@ private: inline Volume* _SystemVolumeIfNotSelf() const; + void _NotifyNodeAdded(Node* node); + void _NotifyNodeRemoved(Node* node); + void _NotifyNodeChanged(Node* node, + uint32 statFields); + private: mutable rw_lock fLock; fs_volume* fFSVolume; @@ -177,6 +187,7 @@ private: } fMountPoint; NodeIDHashTable fNodes; + NodeListenerHashTable fNodeListeners; JobList fJobQueue; mutex fJobQueueLock;