From 991ba9fddf31971e08b038b0f84ff165c8cb94b7 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 23 Jun 2011 16:27:24 +0200 Subject: [PATCH] WIP towards packagefs package links support * Introduce Version class representing a version. * Introduce Dependency and Resolvable class and add lists of either to Package. * Parse package attributes and add dependencies and resolvables to Package. * Add a mount type to Volume and add a respective mount parameter "mount-type" (values "system", "common", "home", "custom"). Also implies the shine-through type, if that's not given. * Introduce class PackageFamily which groups equally named and versioned packages. * Add class PackageFSRoot. Each instance represents a possible file system root (separate roots for different chroot environments). Tracks Volumes belonging to the same root and their packages. --- .../file_systems/packagefs/Dependency.cpp | 50 +++ .../file_systems/packagefs/Dependency.h | 54 +++ .../kernel/file_systems/packagefs/Jamfile | 18 +- .../kernel/file_systems/packagefs/Package.cpp | 50 +++ .../kernel/file_systems/packagefs/Package.h | 29 +- .../file_systems/packagefs/PackageDomain.cpp | 3 +- .../file_systems/packagefs/PackageDomain.h | 5 +- .../file_systems/packagefs/PackageFSRoot.cpp | 308 ++++++++++++++++++ .../file_systems/packagefs/PackageFSRoot.h | 109 +++++++ .../file_systems/packagefs/PackageFamily.cpp | 78 +++++ .../file_systems/packagefs/PackageFamily.h | 71 ++++ .../file_systems/packagefs/Resolvable.cpp | 40 +++ .../file_systems/packagefs/Resolvable.h | 38 +++ .../kernel/file_systems/packagefs/Version.cpp | 173 ++++++++++ .../kernel/file_systems/packagefs/Version.h | 44 +++ .../kernel/file_systems/packagefs/Volume.cpp | 211 ++++++++++-- .../kernel/file_systems/packagefs/Volume.h | 32 +- .../packagefs/kernel_interface.cpp | 10 + 18 files changed, 1293 insertions(+), 30 deletions(-) create mode 100644 src/add-ons/kernel/file_systems/packagefs/Dependency.cpp create mode 100644 src/add-ons/kernel/file_systems/packagefs/Dependency.h create mode 100644 src/add-ons/kernel/file_systems/packagefs/PackageFSRoot.cpp create mode 100644 src/add-ons/kernel/file_systems/packagefs/PackageFSRoot.h create mode 100644 src/add-ons/kernel/file_systems/packagefs/PackageFamily.cpp create mode 100644 src/add-ons/kernel/file_systems/packagefs/PackageFamily.h create mode 100644 src/add-ons/kernel/file_systems/packagefs/Resolvable.cpp create mode 100644 src/add-ons/kernel/file_systems/packagefs/Resolvable.h create mode 100644 src/add-ons/kernel/file_systems/packagefs/Version.cpp create mode 100644 src/add-ons/kernel/file_systems/packagefs/Version.h diff --git a/src/add-ons/kernel/file_systems/packagefs/Dependency.cpp b/src/add-ons/kernel/file_systems/packagefs/Dependency.cpp new file mode 100644 index 0000000000..4b8762caa2 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Dependency.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Dependency.h" + +#include +#include + +#include "Version.h" + + +Dependency::Dependency(Package* package) + : + fPackage(package), + fResolvable(NULL), + fName(NULL), + fVersion(NULL), + fVersionOperator(B_PACKAGE_RESOLVABLE_OP_EQUAL) +{ +} + + +Dependency::~Dependency() +{ + free(fName); + delete fVersion; +} + + +status_t +Dependency::Init(const char* name) +{ + fName = strdup(name); + if (fName == NULL) + return B_NO_MEMORY; + + return B_OK; +} + + +void +Dependency::SetVersionRequirement(BPackageResolvableOperator op, + Version* version) +{ + fVersionOperator = op; + fVersion = version; +} diff --git a/src/add-ons/kernel/file_systems/packagefs/Dependency.h b/src/add-ons/kernel/file_systems/packagefs/Dependency.h new file mode 100644 index 0000000000..e2027f8d1b --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Dependency.h @@ -0,0 +1,54 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef DEPENDENCY_H +#define DEPENDENCY_H + + +#include + +#include + +#include + + +class Package; +class Resolvable; +class Version; + + +using BPackageKit::BPackageResolvableOperator; + + +class Dependency : public BReferenceable, + public DoublyLinkedListLinkImpl { +public: + Dependency(Package* package); + virtual ~Dependency(); + + status_t Init(const char* name); + void SetVersionRequirement( + BPackageResolvableOperator op, + Version* version); + // version is optional; object takes over + // ownership + + void SetResolvable(::Resolvable* resolvable) + { fResolvable = resolvable; } + ::Resolvable* Resolvable() const + { return fResolvable; } + +private: + Package* fPackage; + ::Resolvable* fResolvable; + char* fName; + Version* fVersion; + BPackageResolvableOperator fVersionOperator; +}; + + +typedef DoublyLinkedList DependencyList; + + +#endif // DEPENDENCY_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Jamfile b/src/add-ons/kernel/file_systems/packagefs/Jamfile index c38e91f350..e07bf10a49 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Jamfile +++ b/src/add-ons/kernel/file_systems/packagefs/Jamfile @@ -5,13 +5,11 @@ UseLibraryHeaders zlib ; UsePrivateKernelHeaders ; UsePrivateHeaders shared ; -DEFINES += B_ENABLE_INCOMPLETE_POSIX_AT_SUPPORT ; - # TODO: Remove when it is complete! - HAIKU_PACKAGE_FS_SOURCES = BlockBufferCacheKernel.cpp DebugSupport.cpp + Dependency.cpp Directory.cpp GlobalFactory.cpp kernel_interface.cpp @@ -20,11 +18,15 @@ HAIKU_PACKAGE_FS_SOURCES = Package.cpp PackageDirectory.cpp PackageDomain.cpp + PackageFamily.cpp PackageFile.cpp + PackageFSRoot.cpp PackageLeafNode.cpp PackageNode.cpp PackageNodeAttribute.cpp PackageSymlink.cpp + Resolvable.cpp + Version.cpp Volume.cpp ; @@ -50,6 +52,11 @@ HAIKU_PACKAGE_FS_PACKAGE_READER_SOURCES = ; +local libSharedSources = + NaturalCompare.cpp +; + + SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg ] ; @@ -57,9 +64,14 @@ KernelAddon packagefs : $(HAIKU_PACKAGE_FS_SOURCES) $(HAIKU_PACKAGE_FS_PACKAGE_READER_SOURCES) + $(libSharedSources) : $(HAIKU_STATIC_LIBSUPC++) libz.a ; +SEARCH on [ FGristFiles $(libSharedSources) ] + += [ FDirName $(HAIKU_TOP) src kits shared ] ; + + HaikuSubInclude userland ; diff --git a/src/add-ons/kernel/file_systems/packagefs/Package.cpp b/src/add-ons/kernel/file_systems/packagefs/Package.cpp index dd170dbe21..0221ce1ed4 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Package.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Package.cpp @@ -16,12 +16,16 @@ #include "DebugSupport.h" #include "PackageDomain.h" +#include "Version.h" Package::Package(PackageDomain* domain, dev_t deviceID, ino_t nodeID) : fDomain(domain), fFileName(NULL), + fName(NULL), + fVersion(NULL), + fFamily(NULL), fFD(-1), fOpenCount(0), fNodeID(nodeID), @@ -36,7 +40,15 @@ Package::~Package() while (PackageNode* node = fNodes.RemoveHead()) node->ReleaseReference(); + while (Resolvable* resolvable = fResolvables.RemoveHead()) + delete resolvable; + + while (Dependency* dependency = fDependencies.RemoveHead()) + delete dependency; + free(fFileName); + free(fName); + delete fVersion; mutex_destroy(&fLock); } @@ -53,6 +65,30 @@ Package::Init(const char* fileName) } +status_t +Package::SetName(const char* name) +{ + if (fName != NULL) + free(fName); + + fName = strdup(name); + if (fName == NULL) + RETURN_ERROR(B_NO_MEMORY); + + return B_OK; +} + + +void +Package::SetVersion(::Version* version) +{ + if (fVersion != NULL) + delete fVersion; + + fVersion = version; +} + + void Package::AddNode(PackageNode* node) { @@ -61,6 +97,20 @@ Package::AddNode(PackageNode* node) } +void +Package::AddResolvable(Resolvable* resolvable) +{ + fResolvables.Add(resolvable); +} + + +void +Package::AddDependency(Dependency* dependency) +{ + fDependencies.Add(dependency); +} + + int Package::Open() { diff --git a/src/add-ons/kernel/file_systems/packagefs/Package.h b/src/add-ons/kernel/file_systems/packagefs/Package.h index a784accd2b..7d63ac27d1 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Package.h +++ b/src/add-ons/kernel/file_systems/packagefs/Package.h @@ -8,18 +8,24 @@ #include +#include #include #include #include +#include "Dependency.h" #include "PackageNode.h" +#include "Resolvable.h" class PackageDomain; +class PackageFamily; +class Version; -class Package : public BReferenceable { +class Package : public BReferenceable, + public DoublyLinkedListLinkImpl { public: Package(PackageDomain* domain, dev_t deviceID, ino_t nodeID); @@ -30,10 +36,25 @@ public: PackageDomain* Domain() const { return fDomain; } const char* FileName() const { return fFileName; } + status_t SetName(const char* name); + const char* Name() const { return fName; } + + void SetVersion(::Version* version); + // takes over object ownership + ::Version* Version() const + { return fVersion; } + + void SetFamily(PackageFamily* family) + { fFamily = family; } + PackageFamily* Family() const + { return fFamily; } + Package*& FileNameHashTableNext() { return fFileNameHashTableNext; } void AddNode(PackageNode* node); + void AddResolvable(Resolvable* resolvable); + void AddDependency(Dependency* dependency); int Open(); void Close(); @@ -44,12 +65,17 @@ private: mutex fLock; PackageDomain* fDomain; char* fFileName; + char* fName; + ::Version* fVersion; + PackageFamily* fFamily; int fFD; uint32 fOpenCount; Package* fFileNameHashTableNext; ino_t fNodeID; dev_t fDeviceID; PackageNodeList fNodes; + ResolvableList fResolvables; + DependencyList fDependencies; }; @@ -103,6 +129,7 @@ struct PackageFileNameHashDefinition { typedef BOpenHashTable PackageFileNameHashTable; +typedef DoublyLinkedList PackageList; #endif // PACKAGE_H diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp index a44786f2ff..d2f4ceaa57 100644 --- a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.cpp @@ -21,8 +21,9 @@ #include "DebugSupport.h" -PackageDomain::PackageDomain() +PackageDomain::PackageDomain(::Volume* volume) : + fVolume(volume), fPath(NULL), fDirFD(-1), fListener(NULL) diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h index ac39020693..67c0354fd0 100644 --- a/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h +++ b/src/add-ons/kernel/file_systems/packagefs/PackageDomain.h @@ -14,14 +14,16 @@ class NotificationListener; +class Volume; class PackageDomain : public BReferenceable, public DoublyLinkedListLinkImpl { public: - PackageDomain(); + PackageDomain(::Volume* volume); ~PackageDomain(); + ::Volume* Volume() const { return fVolume; } const char* Path() const { return fPath; } int DirectoryFD() { return fDirFD; } dev_t DeviceID() { return fDeviceID; } @@ -40,6 +42,7 @@ public: { return fPackages; } private: + ::Volume* fVolume; char* fPath; int fDirFD; dev_t fDeviceID; diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageFSRoot.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageFSRoot.cpp new file mode 100644 index 0000000000..e91adafa05 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageFSRoot.cpp @@ -0,0 +1,308 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageFSRoot.h" + +#include + +#include + +#include "DebugSupport.h" + + +mutex PackageFSRoot::sRootListLock = MUTEX_INITIALIZER("packagefs root list"); +PackageFSRoot::RootList PackageFSRoot::sRootList; + + +PackageFSRoot::PackageFSRoot(dev_t deviceID, ino_t nodeID) + : + fDeviceID(deviceID), + fNodeID(nodeID), + fSystemVolume(NULL) +{ + rw_lock_init(&fLock, "packagefs root"); +} + + +PackageFSRoot::~PackageFSRoot() +{ + rw_lock_destroy(&fLock); +} + + +/*static*/ status_t +PackageFSRoot::GlobalInit() +{ + return B_OK; +} + + +/*static*/ void +PackageFSRoot::GlobalUninit() +{ +} + + +status_t +PackageFSRoot::Init() +{ + status_t error = fPackageFamilies.Init(); + if (error != B_OK) + RETURN_ERROR(error); + + return B_OK; +} + + +/*static*/ status_t +PackageFSRoot::RegisterVolume(Volume* volume) +{ + // Unless the volume is custom mounted, we stat the supposed root directory. + // Get the volume mount point relative path to the root directory depending + // on the mount type. + const char* relativeRootPath = NULL; + + switch (volume->MountType()) { + case MOUNT_TYPE_SYSTEM: + case MOUNT_TYPE_COMMON: + relativeRootPath = ".."; + break; + case MOUNT_TYPE_HOME: + relativeRootPath = "../.."; + break; + case MOUNT_TYPE_CUSTOM: + default: + break; + } + + if (relativeRootPath != NULL) { + struct vnode* vnode; + status_t error = vfs_entry_ref_to_vnode(volume->MountPointDeviceID(), + volume->MountPointNodeID(), relativeRootPath, &vnode); + if (error != B_OK) { + dprintf("packagefs: Failed to get root directory \"%s\": %s\n", + relativeRootPath, strerror(error)); + RETURN_ERROR(error); + } + CObjectDeleter vnodePutter(vnode, &vfs_put_vnode); + + // stat it + struct stat st; + error = vfs_stat_vnode(vnode, &st); + if (error != B_OK) { + dprintf("packagefs: Failed to stat root directory \"%s\": %s\n", + relativeRootPath, strerror(error)); + RETURN_ERROR(error); + } + + // get/create the root + PackageFSRoot* root; + error = PackageFSRoot::_GetOrCreateRoot(st.st_dev, st.st_ino, root); + if (error != B_OK) + RETURN_ERROR(error); + + // add the volume + error = root->_AddVolume(volume); + if (error != B_OK) { + _PutRoot(root); + RETURN_ERROR(error); + } + + return B_OK; + } + + // custom mount -- always create a new root + PackageFSRoot* root = new(std::nothrow) PackageFSRoot(-1, 0); + if (root == NULL) + return B_NO_MEMORY; + ObjectDeleter rootDeleter(root); + + status_t error = root->Init(); + if (error != B_OK) + RETURN_ERROR(error); + + // add the volume + error = root->_AddVolume(volume); + if (error != B_OK) { + _PutRoot(root); + RETURN_ERROR(error); + } + + // We don't add the root to the list. + rootDeleter.Detach(); + return B_OK; +} + + +void +PackageFSRoot::UnregisterVolume(Volume* volume) +{ + _RemoveVolume(volume); + _PutRoot(this); +} + + +status_t +PackageFSRoot::AddPackage(Package* package) +{ + // Create a package family -- there might already be one, but since that's + // unlikely, we don't bother to check and recheck later. + PackageFamily* packageFamily = new(std::nothrow) PackageFamily; + if (packageFamily == NULL) + return B_NO_MEMORY; + ObjectDeleter packageFamilyDeleter(packageFamily); + + status_t error = packageFamily->Init(package); + if (error != B_OK) + RETURN_ERROR(error); + + // add the family + PackageFSRootWriteLocker writeLocker(this); + if (PackageFamily* otherPackageFamily + = fPackageFamilies.Lookup(packageFamily->Name())) { + packageFamily->RemovePackage(package); + packageFamily = otherPackageFamily; + packageFamily->AddPackage(package); + } else + fPackageFamilies.Insert(packageFamilyDeleter.Detach()); + +// TODO:... + + return B_OK; +} + + +void +PackageFSRoot::RemovePackage(Package* package) +{ + PackageFSRootWriteLocker writeLocker(this); + + PackageFamily* packageFamily = package->Family(); + if (packageFamily == NULL) + return; + + packageFamily->RemovePackage(package); + + if (packageFamily->IsEmpty()) { + fPackageFamilies.Remove(packageFamily); + delete packageFamily; + } + +// TODO:... +} + + +Volume* +PackageFSRoot::SystemVolume() const +{ + PackageFSRootReadLocker readLocker(this); + return fSystemVolume; +} + + +status_t +PackageFSRoot::_AddVolume(Volume* volume) +{ + PackageFSRootWriteLocker writeLocker(this); + + volume->SetPackageFSRoot(this); + + fVolumes.Add(volume); + // TODO: Correct order? + + if (fSystemVolume == NULL && volume->MountType() == MOUNT_TYPE_SYSTEM) + fSystemVolume = volume; + + return B_OK; +} + + +void +PackageFSRoot::_RemoveVolume(Volume* volume) +{ + PackageFSRootWriteLocker writeLocker(this); + + if (volume == fSystemVolume) + fSystemVolume = NULL; + + fVolumes.Remove(volume); + + volume->SetPackageFSRoot(NULL); +} + + +/*static*/ status_t +PackageFSRoot::_GetOrCreateRoot(dev_t deviceID, ino_t nodeID, + PackageFSRoot*& _root) +{ + // first check the list, if the root already exists + MutexLocker rootListLocker(sRootListLock); + + if (PackageFSRoot* root = _FindRootLocked(deviceID, nodeID)) { + root->AcquireReference(); + _root = root; + return B_OK; + } + + rootListLocker.Unlock(); + + // create a new root + PackageFSRoot* root = new(std::nothrow) PackageFSRoot(deviceID, nodeID); + if (root == NULL) + return B_NO_MEMORY; + ObjectDeleter rootDeleter(root); + + status_t error = root->Init(); + if (error != B_OK) + RETURN_ERROR(error); + + // add the root -- first recheck whether someone else added the root in the + // meantime + rootListLocker.Lock(); + + if (PackageFSRoot* otherRoot = _FindRootLocked(deviceID, nodeID)) { + // indeed, someone was faster + otherRoot->AcquireReference(); + _root = otherRoot; + return B_OK; + } + + sRootList.Add(root); + + _root = rootDeleter.Detach(); + return B_OK; +} + + +/*static*/ PackageFSRoot* +PackageFSRoot::_FindRootLocked(dev_t deviceID, ino_t nodeID) +{ + for (RootList::Iterator it = sRootList.GetIterator(); + PackageFSRoot* root = it.Next();) { + if (root->DeviceID() == deviceID && root->NodeID() == nodeID) + return root; + } + + return NULL; +} + + +/*static*/ void +PackageFSRoot::_PutRoot(PackageFSRoot* root) +{ + // Only non-custom roots are in the global list. + if (!root->IsCustom()) { + MutexLocker rootListLocker(sRootListLock); + + // When releasing the last reference, remove the root from the list. + if (root->CountReferences() == 1) + sRootList.Remove(root); + + rootListLocker.Unlock(); + } + + root->ReleaseReference(); +} diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageFSRoot.h b/src/add-ons/kernel/file_systems/packagefs/PackageFSRoot.h new file mode 100644 index 0000000000..9f08229971 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageFSRoot.h @@ -0,0 +1,109 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_FS_ROOT_H +#define PACKAGE_FS_ROOT_H + + +#include + +#include +#include + +#include + +#include "PackageFamily.h" +#include "Volume.h" + + +class PackageFSRoot : private BReferenceable, + public DoublyLinkedListLinkImpl { +public: + // constructor and destructor are conceptually private + PackageFSRoot(dev_t deviceID, ino_t nodeID); + virtual ~PackageFSRoot(); + + static status_t GlobalInit(); + static void GlobalUninit(); + + inline bool ReadLock() const; + inline void ReadUnlock() const; + inline bool WriteLock(); + inline void WriteUnlock(); + + status_t Init(); + + static status_t RegisterVolume(Volume* volume); + void UnregisterVolume(Volume* volume); + + status_t AddPackage(Package* package); + void RemovePackage(Package* package); + + dev_t DeviceID() const { return fDeviceID; } + ino_t NodeID() const { return fNodeID; } + bool IsCustom() const { return fDeviceID < 0; } + + Volume* SystemVolume() const; + +private: + typedef DoublyLinkedList RootList; + typedef DoublyLinkedList VolumeList; + +private: + status_t _AddVolume(Volume* volume); + void _RemoveVolume(Volume* volume); + + static status_t _GetOrCreateRoot(dev_t deviceID, ino_t nodeID, + PackageFSRoot*& _root); + static PackageFSRoot* _FindRootLocked(dev_t deviceID, ino_t nodeID); + static void _PutRoot(PackageFSRoot* root); + +private: + static mutex sRootListLock; + static RootList sRootList; + + mutable rw_lock fLock; + dev_t fDeviceID; + ino_t fNodeID; + VolumeList fVolumes; + Volume* fSystemVolume; + PackageFamilyHashTable fPackageFamilies; +}; + + +bool +PackageFSRoot::ReadLock() const +{ + return rw_lock_read_lock(&fLock) == B_OK; +} + + +void +PackageFSRoot::ReadUnlock() const +{ + rw_lock_read_unlock(&fLock); +} + + +bool +PackageFSRoot::WriteLock() +{ + return rw_lock_write_lock(&fLock) == B_OK; +} + + +void +PackageFSRoot::WriteUnlock() +{ + rw_lock_write_unlock(&fLock); +} + + +typedef AutoLocker > PackageFSRootReadLocker; +typedef AutoLocker > + PackageFSRootWriteLocker; + + +#endif // PACKAGE_FS_ROOT_H diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageFamily.cpp b/src/add-ons/kernel/file_systems/packagefs/PackageFamily.cpp new file mode 100644 index 0000000000..18ca55b67b --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageFamily.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageFamily.h" + +#include +#include + +#include "DebugSupport.h" +#include "Version.h" + + +PackageFamily::PackageFamily() + : + fName(NULL) +{ +} + + +PackageFamily::~PackageFamily() +{ + while (Package* package = fPackages.RemoveHead()) + package->SetFamily(NULL); + + free(fName); +} + + +status_t +PackageFamily::Init(Package* package) +{ + // compute the allocation size needed for the versioned name + size_t nameLength = strlen(package->Name()); + size_t size = nameLength + 1; + + Version* version = package->Version(); + if (version != NULL) { + size += 1 + version->ToString(NULL, 0); + // + 1 for the '-' + } + + // allocate the name and compose it + fName = (char*)malloc(size); + if (fName == NULL) + return B_NO_MEMORY; + + memcpy(fName, package->Name(), nameLength + 1); + if (version != NULL) { + fName[nameLength] = '-'; + version->ToString(fName + nameLength + 1, size - nameLength - 1); + } + + // add the package + AddPackage(package); + + return B_OK; +} + + +void +PackageFamily::AddPackage(Package* package) +{ + fPackages.Add(package); + package->SetFamily(this); +} + + +void +PackageFamily::RemovePackage(Package* package) +{ + ASSERT(package->Family() == this); + + package->SetFamily(NULL); + fPackages.Remove(package); +} diff --git a/src/add-ons/kernel/file_systems/packagefs/PackageFamily.h b/src/add-ons/kernel/file_systems/packagefs/PackageFamily.h new file mode 100644 index 0000000000..33939a0b54 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/PackageFamily.h @@ -0,0 +1,71 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_FAMILY_H +#define PACKAGE_FAMILY_H + + +#include + +#include "Package.h" + + +/*! A set of equally named and versioned packages. + This should only happen when the same package is installed in multiple + domains (e.g. common and home). +*/ +class PackageFamily { +public: + PackageFamily(); + ~PackageFamily(); + + status_t Init(Package* package); + + const char* Name() const { return fName; } + + void AddPackage(Package* package); + void RemovePackage(Package* package); + + bool IsEmpty() const + { return fPackages.IsEmpty(); } + + PackageFamily*& HashNext() { return fHashNext; } + +private: + PackageFamily* fHashNext; + char* fName; + PackageList fPackages; +}; + + +struct PackageFamilyHashDefinition { + typedef const char* KeyType; + typedef PackageFamily ValueType; + + size_t HashKey(const char* key) const + { + return hash_hash_string(key); + } + + size_t Hash(const PackageFamily* value) const + { + return HashKey(value->Name()); + } + + bool Compare(const char* key, const PackageFamily* value) const + { + return strcmp(value->Name(), key) == 0; + } + + PackageFamily*& GetLink(PackageFamily* value) const + { + return value->HashNext(); + } +}; + + +typedef BOpenHashTable PackageFamilyHashTable; + + +#endif // PACKAGE_FAMILY_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Resolvable.cpp b/src/add-ons/kernel/file_systems/packagefs/Resolvable.cpp new file mode 100644 index 0000000000..7e00edb05f --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Resolvable.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Resolvable.h" + +#include + +#include "Version.h" + + +Resolvable::Resolvable(Package* package) + : + fPackage(package), + fName(NULL), + fVersion(NULL) +{ +} + + +Resolvable::~Resolvable() +{ + free(fName); + delete fVersion; +} + + +status_t +Resolvable::Init(const char* name, Version* version) +{ + fVersion = version; + + fName = strdup(name); + if (fName == NULL) + return B_NO_MEMORY; + + return B_OK; +} diff --git a/src/add-ons/kernel/file_systems/packagefs/Resolvable.h b/src/add-ons/kernel/file_systems/packagefs/Resolvable.h new file mode 100644 index 0000000000..e73f6affc8 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Resolvable.h @@ -0,0 +1,38 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef RESOLVABLE_H +#define RESOLVABLE_H + + +#include + +#include + + +class Package; +class Version; + + +class Resolvable : public BReferenceable, + public DoublyLinkedListLinkImpl { +public: + Resolvable(Package* package); + virtual ~Resolvable(); + + status_t Init(const char* name, Version* version); + // version is optional; object takes over + // ownership (even in case of error) + +private: + Package* fPackage; + char* fName; + Version* fVersion; +}; + + +typedef DoublyLinkedList ResolvableList; + + +#endif // RESOLVABLE_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Version.cpp b/src/add-ons/kernel/file_systems/packagefs/Version.cpp new file mode 100644 index 0000000000..8ab800c23b --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Version.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Version.h" + +#include +#include +#include + +#include + +#include + +#include "DebugSupport.h" + + +static const char* const kVersionPartPlaceholder = "_"; + + +static int +compare_version_part(const char* a, const char* b) +{ + if (a == NULL) + return b != NULL ? -1 : 0; + if (b == NULL) + return 1; + + return BPrivate::NaturalCompare(a, b); +} + + +Version::Version() + : + fMajor(NULL), + fMinor(NULL), + fMicro(NULL), + fRelease(0) +{ +} + + +Version::~Version() +{ + free(fMajor); + free(fMinor); + free(fMicro); +} + + +status_t +Version::Init(const char* major, const char* minor, const char* micro, + uint8 release) +{ + if (major != NULL) { + fMajor = strdup(major); + if (fMajor == NULL) + return B_NO_MEMORY; + } + + if (minor != NULL) { + fMinor = strdup(major); + if (fMinor == NULL) + return B_NO_MEMORY; + } + + if (micro != NULL) { + fMicro = strdup(micro); + if (fMicro == NULL) + return B_NO_MEMORY; + } + + fRelease = release; + + return B_OK; +} + + +/*static*/ status_t +Version::Create(const char* major, const char* minor, const char* micro, + uint8 release, Version*& _version) +{ + Version* version = new(std::nothrow) Version; + if (version == NULL) + return B_NO_MEMORY; + + status_t error = version->Init(major, minor, micro, release); + if (error != B_OK) { + delete version; + return error; + } + + _version = version; + return B_OK; +} + + +int +Version::Compare(const Version& other) const +{ + int cmp = compare_version_part(fMajor, other.fMajor); + if (cmp != 0) + return cmp; + + cmp = compare_version_part(fMinor, other.fMinor); + if (cmp != 0) + return cmp; + + cmp = compare_version_part(fMicro, other.fMicro); + if (cmp != 0) + return cmp; + + return (int)fRelease - other.fRelease; +} + + +bool +Version::Compare(BPackageResolvableOperator op, + const Version& other) const +{ + int cmp = Compare(other); + + switch (op) { + case B_PACKAGE_RESOLVABLE_OP_LESS: + return cmp < 0; + case B_PACKAGE_RESOLVABLE_OP_LESS_EQUAL: + return cmp <= 0; + case B_PACKAGE_RESOLVABLE_OP_EQUAL: + return cmp == 0; + case B_PACKAGE_RESOLVABLE_OP_NOT_EQUAL: + return cmp != 0; + case B_PACKAGE_RESOLVABLE_OP_GREATER_EQUAL: + return cmp >= 0; + case B_PACKAGE_RESOLVABLE_OP_GREATER: + return cmp > 0; + default: + ERROR("packagefs: Version::Compare(): Invalid operator %d\n", op); + return false; + } +} + + +size_t +Version::ToString(char* buffer, size_t bufferSize) const +{ + // We need to normalize the version string somewhat. If a subpart is given, + // make sure that also the superparts are defined, using a placeholder. This + // avoids clashes, e.g. if one version defines only major and one only + // micro. + const char* major = fMajor; + const char* minor = fMinor; + const char* micro = fMicro; + + if (micro != NULL && minor == NULL) + minor = kVersionPartPlaceholder; + if (minor != NULL && major == NULL) + major = kVersionPartPlaceholder; + + if (micro != NULL) { + return snprintf(buffer, bufferSize, "%s.%s.%s-%u", major, minor, micro, + fRelease); + } + + if (minor != NULL) + return snprintf(buffer, bufferSize, "%s.%s-%u", major, minor, fRelease); + + if (major != NULL) + return snprintf(buffer, bufferSize, "%s-%u", major, fRelease); + + return snprintf(buffer, bufferSize, "%u", fRelease); +} diff --git a/src/add-ons/kernel/file_systems/packagefs/Version.h b/src/add-ons/kernel/file_systems/packagefs/Version.h new file mode 100644 index 0000000000..1c72731215 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/Version.h @@ -0,0 +1,44 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef VERSION_H +#define VERSION_H + + +#include +#include + + +using namespace BPackageKit; + + +class Version { +public: + Version(); + ~Version(); + + status_t Init(const char* major, const char* minor, + const char* micro, uint8 release); + + static status_t Create(const char* major, const char* minor, + const char* micro, uint8 release, + Version*& _version); + + int Compare(const Version& other) const; + bool Compare(BPackageResolvableOperator op, + const Version& other) const; + + size_t ToString(char* buffer, size_t bufferSize) const; + // returns how big the buffer should have + // been (excluding the terminating null) + +private: + char* fMajor; + char* fMinor; + char* fMicro; + uint8 fRelease; +}; + + +#endif // VERSION_H diff --git a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp index 7c1676f805..9f06212992 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -35,9 +36,13 @@ #include "kernel_interface.h" #include "PackageDirectory.h" #include "PackageFile.h" +#include "PackageFSRoot.h" #include "PackageSymlink.h" +#include "Resolvable.h" +#include "Version.h" +using namespace BPackageKit; using namespace BPackageKit::BHPKG; using BPackageKit::BHPKG::BPrivate::PackageReaderImpl; @@ -271,6 +276,99 @@ struct Volume::PackageLoaderContentHandler : BPackageContentHandler { virtual status_t HandlePackageAttribute( const BPackageInfoAttributeValue& value) { + switch (value.attributeID) { +// union { +// uint64 unsignedInt; +// const char* string; +// BPackageVersionData version; +// BPackageResolvableData resolvable; +// BPackageResolvableExpressionData resolvableExpression; +// }; + case B_PACKAGE_INFO_NAME: + return fPackage->SetName(value.string); + + case B_PACKAGE_INFO_VERSION: + { + Version* version; + status_t error = Version::Create(value.version.major, + value.version.minor, value.version.micro, + value.version.release, version); + if (error != B_OK) + RETURN_ERROR(error); + + fPackage->SetVersion(version); + + break; + } + + case B_PACKAGE_INFO_PROVIDES: + { + // create a version object, if a version is specified + Version* version = NULL; + if (value.resolvable.haveVersion) { + const BPackageVersionData& versionInfo + = value.resolvable.version; + status_t error = Version::Create(versionInfo.major, + versionInfo.minor, versionInfo.micro, + versionInfo.release, version); + if (error != B_OK) + RETURN_ERROR(error); + } + ObjectDeleter versionDeleter(version); + + // create the resolvable + Resolvable* resolvable = new(std::nothrow) Resolvable(fPackage); + if (resolvable == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter resolvableDeleter(resolvable); + + status_t error = resolvable->Init(value.resolvable.name, + versionDeleter.Detach()); + if (error != B_OK) + RETURN_ERROR(error); + + fPackage->AddResolvable(resolvableDeleter.Detach()); + + break; + } + + case B_PACKAGE_INFO_REQUIRES: + { + // create the dependency + Dependency* dependency = new(std::nothrow) Dependency(fPackage); + if (dependency == NULL) + RETURN_ERROR(B_NO_MEMORY); + ObjectDeleter dependencyDeleter(dependency); + + status_t error = dependency->Init( + value.resolvableExpression.name); + if (error != B_OK) + RETURN_ERROR(error); + + // create a version object, if a version is specified + Version* version = NULL; + if (value.resolvableExpression.haveOpAndVersion) { + const BPackageVersionData& versionInfo + = value.resolvableExpression.version; + status_t error = Version::Create(versionInfo.major, + versionInfo.minor, versionInfo.micro, + versionInfo.release, version); + if (error != B_OK) + RETURN_ERROR(error); + + dependency->SetVersionRequirement( + value.resolvableExpression.op, version); + } + + fPackage->AddDependency(dependencyDeleter.Detach()); + + break; + } + + default: + break; + } + // TODO! return B_OK; } @@ -323,6 +421,7 @@ Volume::Volume(fs_volume* fsVolume) : fFSVolume(fsVolume), fRootDirectory(NULL), + fPackageFSRoot(NULL), fPackageLoader(-1), fNextNodeID(kRootDirectoryID + 1), fTerminating(false) @@ -348,6 +447,9 @@ Volume::~Volume() node = next; } + if (fPackageFSRoot != NULL) + fPackageFSRoot->UnregisterVolume(this); + if (fRootDirectory != NULL) fRootDirectory->ReleaseReference(); @@ -366,6 +468,7 @@ Volume::Mount(const char* parameterString) const char* packages = NULL; const char* volumeName = NULL; + const char* mountType = NULL; const char* shineThrough = NULL; void* parameterHandle = parse_driver_settings_string(parameterString); @@ -374,6 +477,7 @@ Volume::Mount(const char* parameterString) NULL); volumeName = get_driver_parameter(parameterHandle, "volume-name", NULL, NULL); + mountType = get_driver_parameter(parameterHandle, "type", NULL, NULL); shineThrough = get_driver_parameter(parameterHandle, "shine-through", NULL, NULL); } @@ -386,6 +490,12 @@ Volume::Mount(const char* parameterString) RETURN_ERROR(B_BAD_VALUE); } + error = _InitMountType(mountType); + if (error != B_OK) { + ERROR("invalid mount type: \"%s\"\n", mountType); + RETURN_ERROR(B_ERROR); + } + struct stat st; if (stat(packages, &st) < 0) RETURN_ERROR(B_ERROR); @@ -398,6 +508,23 @@ Volume::Mount(const char* parameterString) fRootDirectory->Init(NULL, volumeName != NULL ? volumeName : "Package FS"); fNodes.Insert(fRootDirectory); + // get our mount point + error = vfs_get_mount_point(fFSVolume->id, &fMountPoint.deviceID, + &fMountPoint.nodeID); + if (error != B_OK) + RETURN_ERROR(error); + + // register with packagefs root + error = ::PackageFSRoot::RegisterVolume(this); + if (error != B_OK) + RETURN_ERROR(error); + + if (this == fPackageFSRoot->SystemVolume()) { + error = _AddPackageLinksDirectory(); + if (error != B_OK) + RETURN_ERROR(error); + } + // create default package domain error = _AddInitialPackageDomain(packages); if (error != B_OK) @@ -409,6 +536,11 @@ Volume::Mount(const char* parameterString) if (fPackageLoader < 0) RETURN_ERROR(fPackageLoader); + // establish shine-through directories + error = _CreateShineThroughDirectories(shineThrough); + if (error != B_OK) + RETURN_ERROR(error); + // publish the root node fRootDirectory->AcquireReference(); error = PublishVNode(fRootDirectory); @@ -417,11 +549,6 @@ Volume::Mount(const char* parameterString) RETURN_ERROR(error); } - // establish shine-through directories - error = _CreateShineThroughDirectories(shineThrough); - if (error != B_OK) - RETURN_ERROR(error); - // run the package loader resume_thread(fPackageLoader); @@ -468,7 +595,7 @@ Volume::PublishVNode(Node* node) status_t Volume::AddPackageDomain(const char* path) { - PackageDomain* packageDomain = new(std::nothrow) PackageDomain; + PackageDomain* packageDomain = new(std::nothrow) PackageDomain(this); if (packageDomain == NULL) RETURN_ERROR(B_NO_MEMORY); BReference packageDomainReference(packageDomain, true); @@ -552,7 +679,7 @@ Volume::_PushJob(Job* job) status_t Volume::_AddInitialPackageDomain(const char* path) { - PackageDomain* domain = new(std::nothrow) PackageDomain; + PackageDomain* domain = new(std::nothrow) PackageDomain(this); if (domain == NULL) RETURN_ERROR(B_NO_MEMORY); BReference domainReference(domain, true); @@ -656,12 +783,16 @@ Volume::_LoadPackage(Package* package) status_t Volume::_AddPackageContent(Package* package, bool notify) { + status_t error = fPackageFSRoot->AddPackage(package); + if (error != B_OK) + RETURN_ERROR(error); + for (PackageNodeList::Iterator it = package->Nodes().GetIterator(); PackageNode* node = it.Next();) { // skip over ".PackageInfo" file, it isn't part of the package content if (strcmp(node->Name(), B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) continue; - status_t error = _AddPackageContentRootNode(package, node, notify); + error = _AddPackageContentRootNode(package, node, notify); if (error != B_OK) { _RemovePackageContent(package, node, notify); RETURN_ERROR(error); @@ -689,6 +820,8 @@ Volume::_RemovePackageContent(Package* package, PackageNode* endNode, node = nextNode; } + + fPackageFSRoot->RemovePackage(package);; } @@ -1109,16 +1242,48 @@ Volume::_DomainEntryMoved(PackageDomain* domain, dev_t deviceID, } +status_t +Volume::_InitMountType(const char* mountType) +{ + if (mountType == NULL) + fMountType = MOUNT_TYPE_CUSTOM; + else if (strcmp(mountType, "system") == 0) + fMountType = MOUNT_TYPE_SYSTEM; + else if (strcmp(mountType, "common") == 0) + fMountType = MOUNT_TYPE_COMMON; + else if (strcmp(mountType, "home") == 0) + fMountType = MOUNT_TYPE_HOME; + else if (strcmp(mountType, "custom") == 0) + fMountType = MOUNT_TYPE_CUSTOM; + else + RETURN_ERROR(B_BAD_VALUE); + + return B_OK; +} + + status_t Volume::_CreateShineThroughDirectories(const char* shineThroughSetting) { - if (shineThroughSetting == NULL) - return B_OK; - // get the directories to map const char* const* directories = NULL; - if (strcmp(shineThroughSetting, "system") == 0) + if (shineThroughSetting == NULL) { + // nothing specified -- derive from mount type + switch (fMountType) { + case MOUNT_TYPE_SYSTEM: + directories = kSystemShineThroughDirectories; + break; + case MOUNT_TYPE_COMMON: + directories = kCommonShineThroughDirectories; + break; + case MOUNT_TYPE_HOME: + directories = kHomeShineThroughDirectories; + break; + case MOUNT_TYPE_CUSTOM: + return B_OK; + } + } else if (strcmp(shineThroughSetting, "system") == 0) directories = kSystemShineThroughDirectories; else if (strcmp(shineThroughSetting, "common") == 0) directories = kCommonShineThroughDirectories; @@ -1132,21 +1297,13 @@ Volume::_CreateShineThroughDirectories(const char* shineThroughSetting) if (directories == NULL) return B_OK; - // get our mount point - dev_t mountPointDevice; - ino_t mountPointNode; - status_t error = vfs_get_mount_point(fFSVolume->id, &mountPointDevice, - &mountPointNode); - if (error != B_OK) - RETURN_ERROR(error); - // iterate through the directory list, create the directories, and bind them // to the mount point subdirectories while (const char* directoryName = *(directories++)) { // look up the mount point subdirectory struct vnode* vnode; - error = vfs_entry_ref_to_vnode(mountPointDevice, mountPointNode, - directoryName, &vnode); + status_t error = vfs_entry_ref_to_vnode(fMountPoint.deviceID, + fMountPoint.nodeID, directoryName, &vnode); if (error != B_OK) { dprintf("packagefs: Failed to get shine-through directory \"%s\": " "%s\n", directoryName, strerror(error)); @@ -1196,3 +1353,11 @@ Volume::_CreateShineThroughDirectories(const char* shineThroughSetting) return B_OK; } + + +status_t +Volume::_AddPackageLinksDirectory() +{ +// TODO:... + 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 a5acba3016..5b46ad2a32 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Volume.h +++ b/src/add-ons/kernel/file_systems/packagefs/Volume.h @@ -20,9 +20,18 @@ class Directory; class Node; +class PackageFSRoot; -class Volume { +enum MountType { + MOUNT_TYPE_SYSTEM, + MOUNT_TYPE_COMMON, + MOUNT_TYPE_HOME, + MOUNT_TYPE_CUSTOM +}; + + +class Volume : public DoublyLinkedListLinkImpl { public: Volume(fs_volume* fsVolume); ~Volume(); @@ -36,6 +45,18 @@ public: dev_t ID() const { return fFSVolume->id; } Directory* RootDirectory() const { return fRootDirectory; } + ::MountType MountType() const { return fMountType; } + + void SetPackageFSRoot(::PackageFSRoot* root) + { fPackageFSRoot = root; } + ::PackageFSRoot* PackageFSRoot() const + { return fPackageFSRoot; } + + dev_t MountPointDeviceID() const + { return fMountPoint.deviceID; } + ino_t MountPointNodeID() const + { return fMountPoint.nodeID; } + status_t Mount(const char* parameterString); void Unmount(); @@ -118,15 +139,24 @@ private: ino_t nodeID, const char* fromName, const char* name, bool notify); + status_t _InitMountType(const char* mountType); status_t _CreateShineThroughDirectories( const char* shineThroughSetting); + status_t _AddPackageLinksDirectory(); private: rw_lock fLock; fs_volume* fFSVolume; Directory* fRootDirectory; + ::PackageFSRoot* fPackageFSRoot; + ::MountType fMountType; thread_id fPackageLoader; PackageDomainList fPackageDomains; + struct { + dev_t deviceID; + ino_t nodeID; + } fMountPoint; + NodeIDHashTable fNodes; JobList fJobQueue; 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 fbf46b1dce..c2c6c93636 100644 --- a/src/add-ons/kernel/file_systems/packagefs/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/kernel_interface.cpp @@ -22,6 +22,7 @@ #include "Directory.h" #include "GlobalFactory.h" #include "LeafNode.h" +#include "PackageFSRoot.h" #include "Volume.h" @@ -1063,12 +1064,21 @@ packagefs_std_ops(int32 op, ...) return error; } + error = PackageFSRoot::GlobalInit(); + if (error != B_OK) { + ERROR("Failed to init PackageFSRoot\n"); + GlobalFactory::DeleteDefault(); + exit_debugging(); + return error; + } + return B_OK; } case B_MODULE_UNINIT: { PRINT("package_std_ops(): B_MODULE_UNINIT\n"); + PackageFSRoot::GlobalUninit(); GlobalFactory::DeleteDefault(); exit_debugging(); return B_OK;