diff --git a/src/add-ons/kernel/file_systems/packagefs/Directory.cpp b/src/add-ons/kernel/file_systems/packagefs/Directory.cpp index c990bc4668..eda9f0eb7e 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Directory.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Directory.cpp @@ -6,13 +6,93 @@ #include "Directory.h" +#include "DebugSupport.h" +#include "PackageDirectory.h" + Directory::Directory(ino_t id) : Node(id) { + fMode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; } + Directory::~Directory() { + Node* child = fChildTable.Clear(true); + while (child != NULL) { + Node* next = child->NameHashTableNext(); + child->ReleaseReference(); + child = next; + } +} + + +status_t +Directory::Init(Directory* parent, const char* name) +{ + status_t error = fChildTable.Init(); + if (error != B_OK) + return error; + + return Node::Init(parent, name); +} + + +status_t +Directory::AddPackageNode(PackageNode* packageNode) +{ + if (!S_ISDIR(packageNode->Mode())) + return B_BAD_VALUE; + +// TODO:... + + return B_OK; +} + + +void +Directory::AddChild(Node* node) +{ + fChildTable.Insert(node); + fChildList.Add(node); +} + + +void +Directory::RemoveChild(Node* node) +{ + Node* nextNode = fChildList.GetNext(node); + + fChildTable.Remove(node); + fChildList.Remove(node); + + // adjust directory iterators pointing to the removed child + for (DirectoryIteratorList::Iterator it = fIterators.GetIterator(); + DirectoryIterator* iterator = it.Next();) { + if (iterator->node == node) + iterator->node = nextNode; + } +} + + +Node* +Directory::FindChild(const char* name) +{ + return fChildTable.Lookup(name); +} + + +void +Directory::AddDirectoryIterator(DirectoryIterator* iterator) +{ + fIterators.Add(iterator); +} + + +void +Directory::RemoveDirectoryIterator(DirectoryIterator* iterator) +{ + fIterators.Remove(iterator); } diff --git a/src/add-ons/kernel/file_systems/packagefs/Directory.h b/src/add-ons/kernel/file_systems/packagefs/Directory.h index 2fcde8e48c..32038c5e74 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Directory.h +++ b/src/add-ons/kernel/file_systems/packagefs/Directory.h @@ -9,11 +9,60 @@ #include "Node.h" + +struct DirectoryIterator : DoublyLinkedListLinkImpl { + Node* node; + + DirectoryIterator() + : + node(NULL) + { + } +}; + +typedef DoublyLinkedList DirectoryIteratorList; + + class Directory : public Node { public: Directory(ino_t id); virtual ~Directory(); + + virtual status_t Init(Directory* parent, const char* name); + + virtual status_t AddPackageNode(PackageNode* packageNode); + + void AddChild(Node* node); + void RemoveChild(Node* node); + Node* FindChild(const char* name); + + inline Node* FirstChild() const; + inline Node* NextChild(Node* node) const; + + void AddDirectoryIterator( + DirectoryIterator* iterator); + void RemoveDirectoryIterator( + DirectoryIterator* iterator); + +private: + NodeNameHashTable fChildTable; + NodeList fChildList; + DirectoryIteratorList fIterators; }; +Node* +Directory::FirstChild() const +{ + return fChildList.First(); +} + + +Node* +Directory::NextChild(Node* node) const +{ + return fChildList.GetNext(node); +} + + #endif // DIRECTORY_H diff --git a/src/add-ons/kernel/file_systems/packagefs/File.cpp b/src/add-ons/kernel/file_systems/packagefs/File.cpp new file mode 100644 index 0000000000..0d7731d059 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/File.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "File.h" + +#include "PackageFile.h" + + +File::File(ino_t id) + : + Node(id) +{ + fMode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; +} + + +File::~File() +{ +} + + +status_t +File::Init(Directory* parent, const char* name) +{ + return Node::Init(parent, name); +} + + +status_t +File::AddPackageNode(PackageNode* packageNode) +{ + if (!S_ISREG(packageNode->Mode())) + return B_BAD_VALUE; + +// TODO:... + + return B_OK; +} diff --git a/src/add-ons/kernel/file_systems/packagefs/File.h b/src/add-ons/kernel/file_systems/packagefs/File.h new file mode 100644 index 0000000000..b8c378aeff --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/File.h @@ -0,0 +1,23 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef FILE_H +#define FILE_H + + +#include "Node.h" + + +class File : public Node { +public: + File(ino_t id); + virtual ~File(); + + virtual status_t Init(Directory* parent, const char* name); + + virtual status_t AddPackageNode(PackageNode* packageNode); +}; + + +#endif // FILE_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Jamfile b/src/add-ons/kernel/file_systems/packagefs/Jamfile index ee74cecb02..e9fb6dc740 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Jamfile +++ b/src/add-ons/kernel/file_systems/packagefs/Jamfile @@ -1,21 +1,56 @@ SubDir HAIKU_TOP src add-ons kernel file_systems packagefs ; -UsePrivateHeaders shared ; +UseLibraryHeaders zlib ; +UsePrivateKernelHeaders ; +UsePrivateHeaders haiku_package shared ; + +DEFINES += B_ENABLE_INCOMPLETE_POSIX_AT_SUPPORT ; + # TODO: Remove when it is complete! HAIKU_PACKAGE_FS_SOURCES = DebugSupport.cpp Directory.cpp + File.cpp kernel_interface.cpp Node.cpp + Package.cpp + PackageDirectory.cpp + PackageDomain.cpp + PackageFile.cpp + PackageNode.cpp + PackageSymlink.cpp + Symlink.cpp Volume.cpp ; +HAIKU_PACKAGE_FS_PACKAGE_READER_SOURCES = + DataOutput.cpp + DataReader.cpp + ErrorOutput.cpp + PackageEntryAttribute.cpp + PackageData.cpp + PackageDataReader.cpp + PackageEntry.cpp + PackageReader.cpp + + # compression + ZlibCompressionBase.cpp + ZlibDecompressor.cpp +; + + +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src bin package ] ; +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src bin package compression ] ; + KernelAddon packagefs - : $(HAIKU_PACKAGE_FS_SOURCES) - : $(HAIKU_STATIC_LIBSUPC++) + : + $(HAIKU_PACKAGE_FS_SOURCES) + $(HAIKU_PACKAGE_FS_PACKAGE_READER_SOURCES) + + : $(HAIKU_STATIC_LIBSUPC++) libz.a ; diff --git a/src/add-ons/kernel/file_systems/packagefs/Node.cpp b/src/add-ons/kernel/file_systems/packagefs/Node.cpp index 8ef4857c01..da617b6902 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Node.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Node.cpp @@ -6,14 +6,39 @@ #include "Node.h" +#include +#include + +#include "DebugSupport.h" + Node::Node(ino_t id) : - fID(id) + fID(id), + fParent(NULL), + fName(NULL), + fUserID(0), + fGroupID(0) { + rw_lock_init(&fLock, "packagefs node"); } Node::~Node() { +PRINT("%p->Node::~Node()\n", this); + free(fName); + rw_lock_destroy(&fLock); +} + + +status_t +Node::Init(Directory* parent, const char* name) +{ + fParent = parent; + fName = strdup(name); + if (fName == NULL) + RETURN_ERROR(B_NO_MEMORY); + + return B_OK; } diff --git a/src/add-ons/kernel/file_systems/packagefs/Node.h b/src/add-ons/kernel/file_systems/packagefs/Node.h index c51ad5da4c..793e3c9bcc 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Node.h +++ b/src/add-ons/kernel/file_systems/packagefs/Node.h @@ -8,17 +8,145 @@ #include +#include +#include -class Node { +#include +#include +#include +#include + + +class Directory; +class PackageNode; + + +class Node : public BReferenceable, public DoublyLinkedListLinkImpl { public: Node(ino_t id); virtual ~Node(); - ino_t ID() const { return fID; } + inline bool ReadLock(); + inline void ReadUnlock(); + inline bool WriteLock(); + inline void WriteUnlock(); -private: + ino_t ID() const { return fID; } + Directory* Parent() const { return fParent; } + const char* Name() const { return fName; } + + Node*& NameHashTableNext() + { return fNameHashTableNext; } + Node*& IDHashTableNext() + { return fIDHashTableNext; } + + mode_t Mode() const { return fMode; } + uid_t UserID() const { return fUserID; } + gid_t GroupID() const { return fGroupID; } + + virtual status_t Init(Directory* parent, const char* name); + + virtual status_t AddPackageNode(PackageNode* packageNode) = 0; + +protected: + rw_lock fLock; ino_t fID; + Directory* fParent; + char* fName; + Node* fNameHashTableNext; + Node* fIDHashTableNext; + mode_t fMode; + uid_t fUserID; + gid_t fGroupID; }; +bool +Node::ReadLock() +{ + return rw_lock_read_lock(&fLock) == B_OK; +} + + +void +Node::ReadUnlock() +{ + rw_lock_read_unlock(&fLock); +} + + +bool +Node::WriteLock() +{ + return rw_lock_write_lock(&fLock) == B_OK; +} + + +void +Node::WriteUnlock() +{ + rw_lock_write_unlock(&fLock); +} + + +struct NodeNameHashDefinition { + typedef const char* KeyType; + typedef Node ValueType; + + size_t HashKey(const char* key) const + { + return hash_hash_string(key); + } + + size_t Hash(const Node* value) const + { + return HashKey(value->Name()); + } + + bool Compare(const char* key, const Node* value) const + { + return strcmp(value->Name(), key) == 0; + } + + Node*& GetLink(Node* value) const + { + return value->NameHashTableNext(); + } +}; + + +struct NodeIDHashDefinition { + typedef ino_t KeyType; + typedef Node ValueType; + + size_t HashKey(ino_t key) const + { + return (uint64)(key >> 32) ^ (uint32)key; + } + + size_t Hash(const Node* value) const + { + return HashKey(value->ID()); + } + + bool Compare(ino_t key, const Node* value) const + { + return value->ID() == key; + } + + Node*& GetLink(Node* value) const + { + return value->IDHashTableNext(); + } +}; + +typedef DoublyLinkedList NodeList; + +typedef BOpenHashTable NodeNameHashTable; +typedef BOpenHashTable NodeIDHashTable; + +typedef AutoLocker > NodeReadLocker; +typedef AutoLocker > NodeWriteLocker; + + #endif // NODE_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Package.cpp b/src/add-ons/kernel/file_systems/packagefs/Package.cpp new file mode 100644 index 0000000000..c567afec2d --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Package.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Package.h" + +#include +#include + +#include "DebugSupport.h" + + +Package::Package(PackageDomain* domain, dev_t deviceID, ino_t nodeID) + : + fDomain(domain), + fName(NULL), + fNodeID(nodeID), + fDeviceID(deviceID) +{ +} + + +Package::~Package() +{ + while (PackageNode* node = fNodes.RemoveHead()) + delete node; + + free(fName); +} + + +status_t +Package::Init(const char* name) +{ + fName = strdup(name); + if (fName == NULL) + RETURN_ERROR(B_NO_MEMORY); + + return B_OK; +} + + +void +Package::AddNode(PackageNode* node) +{ + fNodes.Add(node); +} diff --git a/src/add-ons/kernel/file_systems/packagefs/Package.h b/src/add-ons/kernel/file_systems/packagefs/Package.h new file mode 100644 index 0000000000..87a9c12add --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Package.h @@ -0,0 +1,75 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_H +#define PACKAGE_H + + +#include +#include + +#include "PackageNode.h" + + +class PackageDomain; + + +class Package { +public: + Package(PackageDomain* domain, dev_t deviceID, + ino_t nodeID); + ~Package(); + + status_t Init(const char* name); + + PackageDomain* Domain() const { return fDomain; } + const char* Name() const { return fName; } + + Package*& HashTableNext() { return fHashTableNext; } + + void AddNode(PackageNode* node); + + const PackageNodeList& Nodes() const + { return fNodes; } + +private: + PackageDomain* fDomain; + char* fName; + Package* fHashTableNext; + ino_t fNodeID; + dev_t fDeviceID; + PackageNodeList fNodes; +}; + + +struct PackageHashDefinition { + typedef const char* KeyType; + typedef Package ValueType; + + size_t HashKey(const char* key) const + { + return hash_hash_string(key); + } + + size_t Hash(const Package* value) const + { + return HashKey(value->Name()); + } + + bool Compare(const char* key, const Package* value) const + { + return strcmp(value->Name(), key) == 0; + } + + Package*& GetLink(Package* value) const + { + return value->HashTableNext(); + } +}; + + +typedef BOpenHashTable PackageHashTable; + + +#endif // PACKAGE_H diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageDirectory.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageDirectory.cpp new file mode 100644 index 0000000000..e551dbe217 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageDirectory.cpp @@ -0,0 +1,33 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageDirectory.h" + + +PackageDirectory::PackageDirectory() +{ +} + + +PackageDirectory::~PackageDirectory() +{ + while (PackageNode* child = fChildren.RemoveHead()) + delete child; +} + + +void +PackageDirectory::AddChild(PackageNode* node) +{ + fChildren.Add(node); +} + + +void +PackageDirectory::RemoveChild(PackageNode* node) +{ + fChildren.Remove(node); +} diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageDirectory.h b/src/add-ons/kernel/file_systems/packagefs/PackageDirectory.h new file mode 100644 index 0000000000..21a25c8cc3 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageDirectory.h @@ -0,0 +1,45 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_DIRECTORY_H +#define PACKAGE_DIRECTORY_H + + +#include "PackageNode.h" + + +class PackageDirectory : public PackageNode { +public: + PackageDirectory(); + virtual ~PackageDirectory(); + + void AddChild(PackageNode* node); + void RemoveChild(PackageNode* node); + + inline PackageNode* FirstChild() const; + inline PackageNode* NextChild(PackageNode* node) const; + + const PackageNodeList& Children() const + { return fChildren; } + +private: + PackageNodeList fChildren; +}; + + +PackageNode* +PackageDirectory::FirstChild() const +{ + return fChildren.First(); +} + + +PackageNode* +PackageDirectory::NextChild(PackageNode* node) const +{ + return fChildren.GetNext(node); +} + + +#endif // PACKAGE_DIRECTORY_H diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp new file mode 100644 index 0000000000..8fbd52e223 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageDomain.h" + +#include +#include +#include +#include + +#include "DebugSupport.h" + + +PackageDomain::PackageDomain() + : + fPath(NULL), + fDirFD(-1) +{ +} + + +PackageDomain::~PackageDomain() +{ + Package* package = fPackages.Clear(true); + while (package != NULL) { + Package* next = package->HashTableNext(); + delete package; + package = next; + } + + if (fDirFD >= 0) + close(fDirFD); + + free(fPath); +} + + +status_t +PackageDomain::Init(const char* path) +{ + fPath = strdup(path); + if (fPath == NULL) + RETURN_ERROR(B_NO_MEMORY); + + status_t error = fPackages.Init(); + if (error != B_OK) + return error; + + return B_OK; +} + + +status_t +PackageDomain::Prepare() +{ + fDirFD = open(fPath, O_RDONLY); + if (fDirFD < 0) { + ERROR("Failed to open package domain \"%s\"\n", strerror(errno)); + return errno; + } + + return B_OK; +} + + +void +PackageDomain::AddPackage(Package* package) +{ + fPackages.Insert(package); +} + + +void +PackageDomain::RemovePackage(Package* package) +{ + fPackages.Remove(package); +} diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h new file mode 100644 index 0000000000..0956142c8e --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h @@ -0,0 +1,38 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_DOMAIN_H +#define PACKAGE_DOMAIN_H + + +#include + +#include "Package.h" + + +class PackageDomain : public DoublyLinkedListLinkImpl { +public: + PackageDomain(); + ~PackageDomain(); + + const char* Path() const { return fPath; } + int DirectoryFD() { return fDirFD; } + + status_t Init(const char* path); + status_t Prepare(); + + void AddPackage(Package* package); + void RemovePackage(Package* package); + + const PackageHashTable& Packages() const + { return fPackages; } + +private: + char* fPath; + int fDirFD; + PackageHashTable fPackages; +}; + + +#endif // PACKAGE_DOMAIN_H diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageFile.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageFile.cpp new file mode 100644 index 0000000000..07883d5bb8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageFile.cpp @@ -0,0 +1,17 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageFile.h" + + +PackageFile::PackageFile() +{ +} + + +PackageFile::~PackageFile() +{ +} diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageFile.h b/src/add-ons/kernel/file_systems/packagefs/PackageFile.h new file mode 100644 index 0000000000..07ff1a4612 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageFile.h @@ -0,0 +1,19 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_FILE_H +#define PACKAGE_FILE_H + + +#include "PackageNode.h" + + +class PackageFile : public PackageNode { +public: + PackageFile(); + virtual ~PackageFile(); +}; + + +#endif // PACKAGE_FILE_H diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageNode.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageNode.cpp new file mode 100644 index 0000000000..56df511ed1 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageNode.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageNode.h" + +#include +#include + +#include "DebugSupport.h" + + +PackageNode::PackageNode() + : + fParent(NULL), + fName(NULL) +{ +} + + +PackageNode::~PackageNode() +{ + free(fName); +} + + +status_t +PackageNode::Init(PackageDirectory* parent, const char* name) +{ + fParent = parent; + fName = strdup(name); + if (fName == NULL) + RETURN_ERROR(B_NO_MEMORY); + + return B_OK; +} diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageNode.h b/src/add-ons/kernel/file_systems/packagefs/PackageNode.h new file mode 100644 index 0000000000..5db12f1632 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageNode.h @@ -0,0 +1,41 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_NODE_H +#define PACKAGE_NODE_H + + +#include + +#include + + +class PackageDirectory; + + +class PackageNode : public SinglyLinkedListLinkImpl { +public: + PackageNode(); + virtual ~PackageNode(); + + PackageDirectory* Parent() const { return fParent; } + const char* Name() const { return fName; } + + virtual status_t Init(PackageDirectory* parent, + const char* name); + + void SetMode(mode_t mode) { fMode = mode; } + mode_t Mode() const { return fMode; } + +private: + PackageDirectory* fParent; + char* fName; + mode_t fMode; +}; + + +typedef SinglyLinkedList PackageNodeList; + + +#endif // PACKAGE_NODE_H diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageSymlink.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageSymlink.cpp new file mode 100644 index 0000000000..53c4de1e1f --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageSymlink.cpp @@ -0,0 +1,17 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageSymlink.h" + + +PackageSymlink::PackageSymlink() +{ +} + + +PackageSymlink::~PackageSymlink() +{ +} diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageSymlink.h b/src/add-ons/kernel/file_systems/packagefs/PackageSymlink.h new file mode 100644 index 0000000000..62d9e94c43 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageSymlink.h @@ -0,0 +1,19 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_SYMLINK_H +#define PACKAGE_SYMLINK_H + + +#include "PackageNode.h" + + +class PackageSymlink : public PackageNode { +public: + PackageSymlink(); + virtual ~PackageSymlink(); +}; + + +#endif // PACKAGE_SYMLINK_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Symlink.cpp b/src/add-ons/kernel/file_systems/packagefs/Symlink.cpp new file mode 100644 index 0000000000..3e812df8ee --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Symlink.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Symlink.h" + +#include "PackageSymlink.h" + + +Symlink::Symlink(ino_t id) + : + Node(id) +{ + fMode = S_IFLNK | S_IRUSR | S_IRGRP | S_IROTH; +} + + +Symlink::~Symlink() +{ +} + + +status_t +Symlink::Init(Directory* parent, const char* name) +{ + return Node::Init(parent, name); +} + + +status_t +Symlink::AddPackageNode(PackageNode* packageNode) +{ + if (!S_ISLNK(packageNode->Mode())) + return B_BAD_VALUE; + +// TODO:... + + return B_OK; +} diff --git a/src/add-ons/kernel/file_systems/packagefs/Symlink.h b/src/add-ons/kernel/file_systems/packagefs/Symlink.h new file mode 100644 index 0000000000..989915c2cd --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Symlink.h @@ -0,0 +1,23 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef SYMLINK_H +#define SYMLINK_H + + +#include "Node.h" + + +class Symlink : public Node { +public: + Symlink(ino_t id); + virtual ~Symlink(); + + virtual status_t Init(Directory* parent, const char* name); + + virtual status_t AddPackageNode(PackageNode* packageNode); +}; + + +#endif // SYMLINK_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp index 1bbe0ff6a0..338fca6a96 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp @@ -6,43 +6,257 @@ #include "Volume.h" +#include +#include +#include +#include +#include + #include +#include + +#include + +#include "ErrorOutput.h" +#include "FDCloser.h" +#include "PackageEntry.h" +#include "PackageReader.h" + +#include "DebugSupport.h" #include "Directory.h" +#include "File.h" #include "kernel_interface.h" +#include "PackageDirectory.h" +#include "PackageFile.h" +#include "PackageSymlink.h" +#include "Symlink.h" // node ID of the root directory static const ino_t kRootDirectoryID = 1; +// #pragma mark - Job + + +struct Volume::Job : DoublyLinkedListLinkImpl { + Job(Volume* volume) + : + fVolume(volume) + { + } + + virtual ~Job() + { + } + + virtual void Do() = 0; + +protected: + Volume* fVolume; +}; + + +// #pragma mark - AddPackageDomainJob + + +struct Volume::AddPackageDomainJob : Job { + AddPackageDomainJob(Volume* volume, PackageDomain* domain) + : + Job(volume), + fDomain(domain) + { + } + + virtual ~AddPackageDomainJob() + { + } + + virtual void Do() + { + fVolume->_AddPackageDomain(fDomain); + fDomain = NULL; + } + +private: + PackageDomain* fDomain; +}; + + +// #pragma mark - PackageLoaderErrorOutput + + +struct Volume::PackageLoaderErrorOutput : ErrorOutput { + PackageLoaderErrorOutput(Package* package) + : + fPackage(package) + { + } + + virtual void PrintErrorVarArgs(const char* format, va_list args) + { +// TODO:... + } + +private: + Package* fPackage; +}; + + +// #pragma mark - PackageLoaderContentHandler + + +struct Volume::PackageLoaderContentHandler : PackageContentHandler { + PackageLoaderContentHandler(Package* package) + : + fPackage(package), + fErrorOccurred(false) + { + } + + status_t Init() + { + return B_OK; + } + + virtual status_t HandleEntry(PackageEntry* entry) + { + if (fErrorOccurred) + return B_OK; + + PackageDirectory* parentDir = NULL; + if (entry->Parent() != NULL) { + parentDir = dynamic_cast( + (PackageNode*)entry->Parent()->UserToken()); + if (parentDir == NULL) + RETURN_ERROR(B_BAD_DATA); + } + + // create the package node + PackageNode* node; + if (S_ISREG(entry->Mode())) { + node = new(std::nothrow) PackageFile; + } else if (S_ISLNK(entry->Mode())) { + node = new(std::nothrow) PackageSymlink; + } else if (S_ISDIR(entry->Mode())) { + node = new(std::nothrow) PackageDirectory; + } else + RETURN_ERROR(B_BAD_DATA); + + if (node == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter nodeDeleter(node); + + status_t error = node->Init(parentDir, entry->Name()); + if (error != B_OK) + RETURN_ERROR(error); + + node->SetMode(entry->Mode()); + + // add it to the parent directory + if (parentDir != NULL) + parentDir->AddChild(node); + else + fPackage->AddNode(node); + + nodeDeleter.Detach(); + entry->SetUserToken(node); + + return B_OK; + } + + virtual status_t HandleEntryAttribute(PackageEntry* entry, + PackageEntryAttribute* attribute) + { + // TODO:... + return B_OK; + } + + virtual status_t HandleEntryDone(PackageEntry* entry) + { + return B_OK; + } + + virtual void HandleErrorOccurred() + { + fErrorOccurred = true; + } + +private: + Package* fPackage; + bool fErrorOccurred; +}; + + +// #pragma mark - Volume + + Volume::Volume(fs_volume* fsVolume) : fFSVolume(fsVolume), - fRootDirectory(NULL) + fRootDirectory(NULL), + fPackageLoader(-1), + fNextNodeID(kRootDirectoryID + 1), + fTerminating(false) { + rw_lock_init(&fLock, "packagefs volume"); + mutex_init(&fJobQueueLock, "packagefs volume job queue"); + fJobQueueCondition.Init(this, "packagefs volume job queue"); } Volume::~Volume() { - delete fRootDirectory; + _TerminatePackageLoader(); + + while (PackageDomain* packageDomain = fPackageDomains.RemoveHead()) + delete packageDomain; + + if (fRootDirectory != NULL) + fRootDirectory->ReleaseReference(); + + mutex_destroy(&fJobQueueLock); + rw_lock_destroy(&fLock); } status_t Volume::Mount() { + // init the node table + status_t error = fNodes.Init(); + if (error != B_OK) + RETURN_ERROR(error); + // create the root node fRootDirectory = new(std::nothrow) Directory(kRootDirectoryID); if (fRootDirectory == NULL) - return B_NO_MEMORY; + RETURN_ERROR(B_NO_MEMORY); + fNodes.Insert(fRootDirectory); - // publish it - status_t error = publish_vnode(fFSVolume, fRootDirectory->ID(), - fRootDirectory, &gPackageFSVnodeOps, S_IFDIR, 0); + // create default package domains +// TODO: Get them from the mount parameters instead! + error = _AddInitialPackageDomain("/boot/common/packages"); if (error != B_OK) + RETURN_ERROR(error); + + // spawn package loader thread + fPackageLoader = spawn_kernel_thread(&_PackageLoaderEntry, + "package loader", B_NORMAL_PRIORITY, this); + if (fPackageLoader < 0) + RETURN_ERROR(fPackageLoader); + + // publish the root node + fRootDirectory->AcquireReference(); + error = PublishVNode(fRootDirectory); + if (error != B_OK) { + fRootDirectory->ReleaseReference(); return error; + } + + // run the package loader + resume_thread(fPackageLoader); return B_OK; } @@ -51,4 +265,342 @@ Volume::Mount() void Volume::Unmount() { + _TerminatePackageLoader(); +} + + +status_t +Volume::GetVNode(ino_t nodeID, Node*& _node) +{ + return get_vnode(fFSVolume, nodeID, (void**)&_node); +} + + +status_t +Volume::PublishVNode(Node* node) +{ + return publish_vnode(fFSVolume, node->ID(), node, &gPackageFSVnodeOps, + node->Mode() & S_IFMT, 0); +} + + +status_t +Volume::AddPackageDomain(const char* path) +{ + PackageDomain* packageDomain = new(std::nothrow) PackageDomain; + if (packageDomain == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter packageDomainDeleter(packageDomain); + + status_t error = packageDomain->Init(path); + if (error != B_OK) + return error; + + Job* job = new(std::nothrow) AddPackageDomainJob(this, packageDomain); + if (job == NULL) + RETURN_ERROR(B_NO_MEMORY); + packageDomainDeleter.Detach(); + + _PushJob(job); + + return B_OK; +} + + +/*static*/ status_t +Volume::_PackageLoaderEntry(void* data) +{ + return ((Volume*)data)->_PackageLoader(); +} + + +status_t +Volume::_PackageLoader() +{ + while (!fTerminating) { + MutexLocker jobQueueLocker(fJobQueueLock); + + Job* job = fJobQueue.RemoveHead(); + if (job == NULL) { + // no job yet -- wait for someone notifying us + ConditionVariableEntry waitEntry; + fJobQueueCondition.Add(&waitEntry); + jobQueueLocker.Unlock(); + waitEntry.Wait(); + continue; + } + + // do the job + jobQueueLocker.Unlock(); + job->Do(); + delete job; + } + + return B_OK; +} + + +void +Volume::_TerminatePackageLoader() +{ + fTerminating = true; + + if (fPackageLoader >= 0) { + MutexLocker jobQueueLocker(fJobQueueLock); + fJobQueueCondition.NotifyOne(); + jobQueueLocker.Unlock(); + + wait_for_thread(fPackageLoader, NULL); + fPackageLoader = -1; + } + + // empty the job queue + while (Job* job = fJobQueue.RemoveHead()) + delete job; +} + + +void +Volume::_PushJob(Job* job) +{ + MutexLocker jobQueueLocker(fJobQueueLock); + fJobQueue.Add(job); +} + + +status_t +Volume::_AddInitialPackageDomain(const char* path) +{ + PackageDomain* domain = new(std::nothrow) PackageDomain; + if (domain == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter domainDeleter(domain); + + status_t error = domain->Init(path); + if (error != B_OK) + return error; + + return _AddPackageDomain(domainDeleter.Detach()); +} + + +status_t +Volume::_AddPackageDomain(PackageDomain* domain) +{ + ObjectDeleter domainDeleter(domain); + + // prepare the package domain + status_t error = domain->Prepare(); + 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) { + ERROR("Failed to open package domain directory \"%s\": %s\n", + domain->Path(), strerror(errno)); + return errno; + } + CObjectDeleter dirCloser(dir, closedir); + + while (dirent* entry = readdir(dir)) { + // skip "." and ".." + 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); + ObjectDeleter packageDeleter(package); + + status_t error = package->Init(entry->d_name); + if (error != B_OK) + return error; + + error = _LoadPackage(package); + if (error != B_OK) + continue; + + domain->AddPackage(packageDeleter.Detach()); + } + + // add the packages to the node tree + VolumeWriteLocker volumeLocker(this); + for (PackageHashTable::Iterator it = domain->Packages().GetIterator(); + Package* package = it.Next();) { + error = _AddPackageContent(package); + if (error != B_OK) { +// TODO: Remove the already added packages! + return error; + } + } + + fPackageDomains.Add(domainDeleter.Detach()); + return B_OK; +} + + +status_t +Volume::_LoadPackage(Package* package) +{ + // open package file + int fd = openat(package->Domain()->DirectoryFD(), package->Name(), + O_RDONLY); + if (fd < 0) { + ERROR("Failed to open package file \"%s\"\n", package->Name()); + return errno; + } +// TODO: Verify that it's still the same file. + FDCloser fdCloser(fd); + + // open package + PackageLoaderErrorOutput errorOutput(package); + PackageReader packageReader(&errorOutput); + status_t error = packageReader.Init(fd, false); + if (error != B_OK) + return error; + + // parse content + PackageLoaderContentHandler handler(package); + error = handler.Init(); + if (error != B_OK) + return error; + + error = packageReader.ParseContent(&handler); + if (error != B_OK) + return error; + + return B_OK; +} + + +status_t +Volume::_AddPackageContent(Package* package) +{ + for (PackageNodeList::Iterator it = package->Nodes().GetIterator(); + PackageNode* node = it.Next();) { + status_t error = _AddPackageContentRootNode(package, node); + if (error != B_OK) { +// TODO: Remove already added nodes! + return error; + } + } +// TODO: Recursively add the nodes! + + return B_OK; +} + + +status_t +Volume::_AddPackageContentRootNode(Package* package, PackageNode* packageNode) +{ + Directory* directory = fRootDirectory; + directory->WriteLock(); + + do { + Node* node; + status_t error = _AddPackageNode(directory, packageNode, node); + if (error != B_OK) { +// TODO: Remove already added nodes! + return error; + } + + // recursive into directory + if (PackageDirectory* packageDirectory + = dynamic_cast(packageNode)) { + if (packageDirectory->FirstChild() != NULL) { + directory = dynamic_cast(node); + packageNode = packageDirectory->FirstChild(); + directory->WriteLock(); + continue; + } + } + + // continue with the next available (descendent's) sibling + do { + PackageDirectory* packageDirectory = packageNode->Parent(); + if (PackageNode* sibling + = packageDirectory->NextChild(packageNode)) { + 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); + } while (packageNode != NULL); + + return B_OK; +} + + +status_t +Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode, + Node*& _node) +{ + 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; + } + + status_t error = node->AddPackageNode(packageNode); + if (error != B_OK) + return error; +// TODO: Remove the node, if created before! + + _node = node; + return B_OK; +} + + +status_t +Volume::_CreateNode(mode_t mode, Directory* parent, const char* name, + Node*& _node) +{ + Node* node; + if (S_ISREG(mode)) + node = new(std::nothrow) File(fNextNodeID++); + else if (S_ISLNK(mode)) + node = new(std::nothrow) Symlink(fNextNodeID++); + else if (S_ISDIR(mode)) + node = new(std::nothrow) Directory(fNextNodeID++); + else + RETURN_ERROR(B_UNSUPPORTED); + + if (node == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter nodeDeleter(node); + + status_t error = node->Init(parent, name); + if (error != B_OK) + return error; + + parent->AddChild(node); + + fNodes.Insert(node); + // we keep the initial node reference for this table + + _node = nodeDeleter.Detach(); + return B_OK; } diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.h b/src/add-ons/kernel/file_systems/packagefs/Volume.h index 997eaa3e23..cfae90427b 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.h +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.h @@ -8,8 +8,17 @@ #include +#include +#include +#include +#include + +#include "Node.h" +#include "PackageDomain.h" + class Directory; +class Node; class Volume { @@ -17,15 +26,105 @@ public: Volume(fs_volume* fsVolume); ~Volume(); + inline bool ReadLock(); + inline void ReadUnlock(); + inline bool WriteLock(); + inline void WriteUnlock(); + + fs_volume* FSVolume() const { return fFSVolume; } + dev_t ID() const { return fFSVolume->id; } + Directory* RootDirectory() const { return fRootDirectory; } + status_t Mount(); void Unmount(); - Directory* RootDirectory() const { return fRootDirectory; } + Node* FindNode(ino_t nodeID) const + { return fNodes.Lookup(nodeID); } + + // VFS wrappers + status_t GetVNode(ino_t nodeID, Node*& _node); + status_t PublishVNode(Node* node); + + status_t AddPackageDomain(const char* path); private: + struct Job; + struct AddPackageDomainJob; + struct PackageLoaderErrorOutput; + struct PackageLoaderContentHandler; + + typedef DoublyLinkedList JobList; + typedef DoublyLinkedList PackageDomainList; + +private: + static status_t _PackageLoaderEntry(void* data); + status_t _PackageLoader(); + + void _TerminatePackageLoader(); + + void _PushJob(Job* job); + + status_t _AddInitialPackageDomain(const char* path); + status_t _AddPackageDomain(PackageDomain* domain); + status_t _LoadPackage(Package* package); + status_t _AddPackageContent(Package* package); + status_t _AddPackageContentRootNode(Package* package, + PackageNode* node); + status_t _AddPackageNode(Directory* directory, + PackageNode* packageNode, Node*& _node); + + status_t _CreateNode(mode_t mode, Directory* parent, + const char* name, Node*& _node); + +private: + rw_lock fLock; fs_volume* fFSVolume; Directory* fRootDirectory; + thread_id fPackageLoader; + PackageDomainList fPackageDomains; + + NodeIDHashTable fNodes; + + JobList fJobQueue; + mutex fJobQueueLock; + ConditionVariable fJobQueueCondition; + + ino_t fNextNodeID; + + volatile bool fTerminating; }; +bool +Volume::ReadLock() +{ + return rw_lock_read_lock(&fLock) == B_OK; +} + + +void +Volume::ReadUnlock() +{ + rw_lock_read_unlock(&fLock); +} + + +bool +Volume::WriteLock() +{ + return rw_lock_write_lock(&fLock) == B_OK; +} + + +void +Volume::WriteUnlock() +{ + rw_lock_write_unlock(&fLock); +} + + +typedef AutoLocker > VolumeReadLocker; +typedef AutoLocker > VolumeWriteLocker; + + #endif // VOLUME_H diff --git a/src/add-ons/kernel/file_systems/packagefs/kernel_interface.cpp b/src/add-ons/kernel/file_systems/packagefs/kernel_interface.cpp index 2a55911313..d0cf4e4921 100644 --- a/src/add-ons/kernel/file_systems/packagefs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/kernel_interface.cpp @@ -6,6 +6,8 @@ #include "kernel_interface.h" +#include + #include #include @@ -22,6 +24,38 @@ static const uint32 kOptimalIOSize = 64 * 1024; +// #pragma mark - helper functions + + +static bool +is_user_in_group(gid_t gid) +{ + gid_t groups[NGROUPS_MAX]; + int groupCount = getgroups(NGROUPS_MAX, groups); + for (int i = 0; i < groupCount; i++) { + if (gid == groups[i]) + return true; + } + + return (gid == getegid()); +} + + +static bool +set_dirent_name(struct dirent* buffer, size_t bufferSize, const char* name, + size_t nameLen) +{ + size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer; + if (length > bufferSize) + return false; + + memcpy(buffer->d_name, name, nameLen); + buffer->d_name[nameLen] = '\0'; + buffer->d_reclen = length; + return true; +} + + // #pragma mark - Volume @@ -92,7 +126,34 @@ packagefs_lookup(fs_volume* fsVolume, fs_vnode* fsDir, const char* entryName, FUNCTION("volume: %p, dir: %p (%lld), entry: \"%s\"\n", volume, dir, dir->ID(), entryName); - return B_UNSUPPORTED; + if (!S_ISDIR(dir->Mode())) + return B_NOT_A_DIRECTORY; + + // resolve "." + if (strcmp(entryName, ".") == 0) { + Node* node; + *_vnid = dir->ID(); + return volume->GetVNode(*_vnid, node); + } + + // resolve ".." + if (strcmp(entryName, "..") == 0) { + Node* node; + *_vnid = dir->Parent()->ID(); + return volume->GetVNode(*_vnid, node); + } + + // resolve normal entries -- look up the node + NodeReadLocker dirLocker(dir); + Node* node = dynamic_cast(dir)->FindChild(entryName); + if (node == NULL) + return B_ENTRY_NOT_FOUND; + BReference nodeReference(node); + dirLocker.Unlock(); + + // get the vnode reference + *_vnid = node->ID(); + RETURN_ERROR(volume->GetVNode(*_vnid, node)); } @@ -104,14 +165,33 @@ packagefs_get_vnode(fs_volume* fsVolume, ino_t vnid, fs_vnode* fsNode, FUNCTION("volume: %p, vnid: %lld\n", volume, vnid); - return B_UNSUPPORTED; + VolumeReadLocker volumeLocker(volume); + Node* node = volume->FindNode(vnid); + if (node == NULL) + return B_ENTRY_NOT_FOUND; + node->AcquireReference(); + + fsNode->private_node = node; + fsNode->ops = &gPackageFSVnodeOps; + *_type = node->Mode() & S_IFMT; + *_flags = 0; + + return B_OK; } static status_t -packagefs_put_vnode(fs_volume* fs, fs_vnode* _node, bool reenter) +packagefs_put_vnode(fs_volume* fsVolume, fs_vnode* fsNode, bool reenter) { - return B_UNSUPPORTED; + Volume* volume = (Volume*)fsVolume->private_volume; + Node* node = (Node*)fsNode->private_node; + + FUNCTION("volume: %p, node: %p\n", volume, node); + TOUCH(volume); + + node->ReleaseReference(); + + return B_OK; } @@ -119,7 +199,7 @@ packagefs_put_vnode(fs_volume* fs, fs_vnode* _node, bool reenter) static status_t -packagefs_read_symlink(fs_volume* fs, fs_vnode* _node, char* buffer, +packagefs_read_symlink(fs_volume* fsVolume, fs_vnode* fsNode, char* buffer, size_t* bufferSize) { return B_UNSUPPORTED; @@ -127,9 +207,46 @@ packagefs_read_symlink(fs_volume* fs, fs_vnode* _node, char* buffer, static status_t -packagefs_access(fs_volume* fs, fs_vnode* _node, int mode) +packagefs_access(fs_volume* fsVolume, fs_vnode* fsNode, int mode) { - return B_UNSUPPORTED; + Volume* volume = (Volume*)fsVolume->private_volume; + Node* node = (Node*)fsNode->private_node; + TOUCH(volume); + + FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID()); + TOUCH(volume); + + // write access requested? + if (mode & W_OK) + return B_READ_ONLY_DEVICE; + + // get node permissions + int userPermissions = (node->Mode() & S_IRWXU) >> 6; + int groupPermissions = (node->Mode() & S_IRWXG) >> 3; + int otherPermissions = node->Mode() & S_IRWXO; + + // get the permissions for this uid/gid + int permissions = 0; + uid_t uid = geteuid(); + + if (uid == 0) { + // user is root + // root has always read/write permission, but at least one of the + // X bits must be set for execute permission + permissions = userPermissions | groupPermissions | otherPermissions + | S_IROTH | S_IWOTH; + } else if (uid == node->UserID()) { + // user is node owner + permissions = userPermissions; + } else if (is_user_in_group(node->GroupID())) { + // user is in owning group + permissions = groupPermissions; + } else { + // user is one of the others + permissions = otherPermissions; + } + + return (mode & ~permissions) ? B_NOT_ALLOWED : B_OK; } @@ -140,9 +257,10 @@ packagefs_read_stat(fs_volume* fsVolume, fs_vnode* fsNode, struct stat* st) Node* node = (Node*)fsNode->private_node; FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID()); + TOUCH(volume); // TODO: Fill in correctly! - st->st_mode = S_IFDIR; + st->st_mode = node->Mode(); st->st_nlink = 1; st->st_uid = 0; st->st_gid = 0; @@ -161,23 +279,35 @@ packagefs_read_stat(fs_volume* fsVolume, fs_vnode* fsNode, struct stat* st) static status_t -packagefs_open(fs_volume* fs, fs_vnode* _node, int openMode, void** cookie) +packagefs_open(fs_volume* fsVolume, fs_vnode* fsNode, int openMode, + void** _cookie) { - return B_UNSUPPORTED; + Volume* volume = (Volume*)fsVolume->private_volume; + Node* node = (Node*)fsNode->private_node; + + FUNCTION("volume: %p, node: %p (%lld), openMode %#x\n", volume, node, + node->ID(), openMode); + TOUCH(volume); + TOUCH(node); + +// TODO: Implement it for real! + *_cookie = NULL; + + return B_OK; } static status_t packagefs_close(fs_volume* fs, fs_vnode* _node, void* cookie) { - return B_UNSUPPORTED; + return B_OK; } static status_t packagefs_free_cookie(fs_volume* fs, fs_vnode* _node, void* cookie) { - return B_UNSUPPORTED; + return B_OK; } @@ -192,39 +322,218 @@ packagefs_read(fs_volume* fs, fs_vnode* _node, void* cookie, off_t pos, // #pragma mark - Directories +struct DirectoryCookie : DirectoryIterator { + Directory* directory; + int32 state; + bool registered; + + DirectoryCookie(Directory* directory) + : + directory(directory), + state(0), + registered(false) + { + Rewind(); + } + + ~DirectoryCookie() + { + if (registered) + directory->RemoveDirectoryIterator(this); + } + + void Rewind() + { + if (registered) + directory->RemoveDirectoryIterator(this); + registered = false; + + state = 0; + node = directory; + } + + Node* Current(const char*& _name) const + { + if (node == NULL) + return NULL; + + if (state == 0) + _name = "."; + else if (state == 1) + _name = ".."; + else + _name = node->Name(); + + return node; + } + + Node* Next() + { + if (state == 0) { + state = 1; + node = directory->Parent(); + if (node == NULL) + node = directory; + return node; + } + + if (state == 1) { + node = directory->FirstChild(); + state = 2; + } else { + if (node != NULL) + node = directory->NextChild(node); + } + + if (node == NULL) { + if (registered) { + directory->RemoveDirectoryIterator(this); + registered = false; + } + + return NULL; + } + + if (!registered) { + directory->AddDirectoryIterator(this); + registered = true; + } + + return node; + } +}; + + static status_t -packagefs_open_dir(fs_volume* fs, fs_vnode* _node, void** cookie) +packagefs_open_dir(fs_volume* fsVolume, fs_vnode* fsNode, void** _cookie) { - return B_UNSUPPORTED; + Volume* volume = (Volume*)fsVolume->private_volume; + Node* node = (Node*)fsNode->private_node; + + FUNCTION("volume: %p, node: %p (%lld)\n", volume, node, node->ID()); + + if (!S_ISDIR(node->Mode())) + return B_NOT_A_DIRECTORY; + + Directory* dir = dynamic_cast(node); + + // create a cookie + DirectoryCookie* cookie = new(std::nothrow) DirectoryCookie(dir); + if (cookie == NULL) + RETURN_ERROR(B_NO_MEMORY); + + *_cookie = cookie; + return B_OK; } static status_t -packagefs_close_dir(fs_volume* fs, fs_vnode* _node, void* cookie) +packagefs_close_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* cookie) { - return B_UNSUPPORTED; + return B_OK; } static status_t -packagefs_free_dir_cookie(fs_volume* fs, fs_vnode* _node, void* cookie) +packagefs_free_dir_cookie(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie) { - return B_UNSUPPORTED; + Volume* volume = (Volume*)fsVolume->private_volume; + Node* node = (Node*)fsNode->private_node; + DirectoryCookie* cookie = (DirectoryCookie*)_cookie; + + FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node, + node->ID(), cookie); + TOUCH(volume); + TOUCH(node); + + NodeWriteLocker dirLocker(node); + delete cookie; + + return B_OK; } static status_t -packagefs_read_dir(fs_volume* fs, fs_vnode* _node, void* cookie, - struct dirent* buffer, size_t bufferSize, uint32* count) +packagefs_read_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie, + struct dirent* buffer, size_t bufferSize, uint32* _count) { - return B_UNSUPPORTED; + Volume* volume = (Volume*)fsVolume->private_volume; + Node* node = (Node*)fsNode->private_node; + DirectoryCookie* cookie = (DirectoryCookie*)_cookie; + + FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node, + node->ID(), cookie); + TOUCH(volume); + TOUCH(node); + + NodeWriteLocker dirLocker(cookie->directory); + + uint32 maxCount = *_count; + uint32 count = 0; + + dirent* previousEntry = NULL; + + const char* name; + while (Node* child = cookie->Current(name)) { + // don't read more entries than requested + if (count >= maxCount) + break; + + // align the buffer for subsequent entries + if (count > 0) { + addr_t offset = (addr_t)buffer % 8; + if (offset > 0) { + offset = 8 - offset; + if (bufferSize <= offset) + break; + + previousEntry->d_reclen += offset; + buffer = (dirent*)((addr_t)buffer + offset); + bufferSize -= offset; + } + } + + // fill in the entry name -- checks whether the entry fits into the + // buffer + if (!set_dirent_name(buffer, bufferSize, name, strlen(name))) { + if (count == 0) + RETURN_ERROR(B_BUFFER_OVERFLOW); + break; + } + + // fill in the other data + buffer->d_dev = volume->ID(); + buffer->d_ino = child->ID(); + + count++; + previousEntry = buffer; + bufferSize -= buffer->d_reclen; + buffer = (dirent*)((addr_t)buffer + buffer->d_reclen); + + cookie->Next(); + } + + *_count = count; + return B_OK; } static status_t -packagefs_rewind_dir(fs_volume* fs, fs_vnode* _node, void* cookie) +packagefs_rewind_dir(fs_volume* fsVolume, fs_vnode* fsNode, void* _cookie) { - return B_UNSUPPORTED; + Volume* volume = (Volume*)fsVolume->private_volume; + Node* node = (Node*)fsNode->private_node; + DirectoryCookie* cookie = (DirectoryCookie*)_cookie; + + FUNCTION("volume: %p, node: %p (%lld), cookie: %p\n", volume, node, + node->ID(), cookie); + TOUCH(volume); + TOUCH(node); + + NodeWriteLocker dirLocker(node); + cookie->Rewind(); + + return B_OK; } @@ -292,7 +601,7 @@ fs_vnode_ops gPackageFSVnodeOps = { &packagefs_lookup, NULL, // get_vnode_name, &packagefs_put_vnode, - NULL, // remove_vnode, + &packagefs_put_vnode, // remove_vnode -- same as put_vnode /* VM file access */ NULL, // can_page, diff --git a/src/add-ons/kernel/file_systems/packagefs/userland/Jamfile b/src/add-ons/kernel/file_systems/packagefs/userland/Jamfile index ee007ddd74..feddc1f52c 100644 --- a/src/add-ons/kernel/file_systems/packagefs/userland/Jamfile +++ b/src/add-ons/kernel/file_systems/packagefs/userland/Jamfile @@ -1,13 +1,23 @@ SubDir HAIKU_TOP src add-ons kernel file_systems packagefs userland ; +UseLibraryHeaders zlib ; +UsePrivateKernelHeaders ; +UsePrivateHeaders haiku_package shared ; + +DEFINES += B_ENABLE_INCOMPLETE_POSIX_AT_SUPPORT ; + # TODO: Remove when it is complete! + + SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ; - - -UsePrivateHeaders shared ; +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src bin package ] ; +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src bin package compression ] ; Addon packagefs - : $(HAIKU_PACKAGE_FS_SOURCES) - : libuserlandfs_haiku_kernel.so $(TARGET_LIBSUPC++) + : + $(HAIKU_PACKAGE_FS_SOURCES) + $(HAIKU_PACKAGE_FS_PACKAGE_READER_SOURCES) + + : libuserlandfs_haiku_kernel.so $(TARGET_LIBSUPC++) libz.a ;