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


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34172 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-11-21 15:43:37 +00:00
parent b7b67e723a
commit d73033ce64
10 changed files with 649 additions and 61 deletions
@@ -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<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()
{
@@ -41,6 +41,7 @@ public:
virtual off_t FileSize() const;
virtual status_t AddPackageNode(PackageNode* packageNode);
virtual void RemovePackageNode(PackageNode* packageNode);
virtual PackageNode* GetPackageNode();
@@ -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<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()
{
@@ -27,6 +27,7 @@ public:
virtual off_t FileSize() const;
virtual status_t AddPackageNode(PackageNode* packageNode);
virtual void RemovePackageNode(PackageNode* packageNode);
virtual PackageNode* GetPackageNode();
@@ -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;
@@ -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 @@ PackageDomain::Init(const char* path)
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 @@ PackageDomain::RemovePackage(Package* package)
fPackages.Remove(package);
package->ReleaseReference();
}
Package*
PackageDomain::FindPackage(const char* name) const
{
return fPackages.Lookup(name);
}
@@ -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;
};
@@ -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
@@ -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 @@ 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<PackageDomain> packageDomainDeleter(packageDomain);
BReference<PackageDomain> 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<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 @@ 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<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 @@ 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<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 @@ 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<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)
return;
BReference<Package> 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);
}
@@ -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<Job> JobList;
typedef DoublyLinkedList<PackageDomain> 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;