* 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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user