From cbc85916fb5c35022ba6e52eb5b55a93271b1fb6 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sun, 19 Jun 2011 14:20:04 +0200 Subject: [PATCH] Add boot loader packagefs support * Add pread(). * Add Node::ReadLink() to read a symbolic link path. * Add Directory::LookupDontTraverse() and make Lookup() non-abstract. Lookup() is implemented via LookupDontTraverse() and Node::ReadLink(). * Adjust all FS implementations accordingly. * Add a packagefs implementation. Unlike other FS implementations it isn't a pseudo-module, but provides a function to explicitly mount a package file (packagefs_mount_file()). * Finish BootVolume::SetTo() implementation, mounting the package file and replacing fSystemDirectory. Now the boot loader can load the kernel and boot modules from a packaged system. The kernel boots up to the point where the boot volume is mounted. --- headers/private/kernel/boot/vfs.h | 11 +- src/system/boot/loader/RootFileSystem.cpp | 22 +- src/system/boot/loader/RootFileSystem.h | 2 +- src/system/boot/loader/file_systems/Jamfile | 1 + .../file_systems/amiga_ffs/Directory.cpp | 14 +- .../loader/file_systems/amiga_ffs/Directory.h | 2 +- .../loader/file_systems/bfs/Directory.cpp | 45 +- .../boot/loader/file_systems/bfs/Directory.h | 2 +- .../boot/loader/file_systems/bfs/Link.h | 3 +- .../loader/file_systems/fat/Directory.cpp | 6 +- .../boot/loader/file_systems/fat/Directory.h | 2 +- .../loader/file_systems/packagefs/Jamfile | 46 + .../file_systems/packagefs/packagefs.cpp | 807 ++++++++++++++++++ .../loader/file_systems/packagefs/packagefs.h | 27 + .../boot/loader/file_systems/tarfs/tarfs.cpp | 32 +- src/system/boot/loader/vfs.cpp | 67 +- 16 files changed, 1006 insertions(+), 83 deletions(-) create mode 100644 src/system/boot/loader/file_systems/packagefs/Jamfile create mode 100644 src/system/boot/loader/file_systems/packagefs/packagefs.cpp create mode 100644 src/system/boot/loader/file_systems/packagefs/packagefs.h diff --git a/headers/private/kernel/boot/vfs.h b/headers/private/kernel/boot/vfs.h index 60d7d2bd4e..e936160498 100644 --- a/headers/private/kernel/boot/vfs.h +++ b/headers/private/kernel/boot/vfs.h @@ -26,8 +26,12 @@ class Node : public DoublyLinkedListLinkImpl { virtual status_t Open(void **_cookie, int mode); virtual status_t Close(void *cookie); - virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize) = 0; - virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize) = 0; + virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, + size_t bufferSize) = 0; + virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, + size_t bufferSize) = 0; + + virtual status_t ReadLink(char* buffer, size_t bufferSize); virtual status_t GetName(char *nameBuffer, size_t bufferSize) const; virtual status_t GetFileMap(struct file_map_run *runs, int32 *count); @@ -55,7 +59,8 @@ class Directory : public Node { virtual int32 Type() const; - virtual Node *Lookup(const char *name, bool traverseLinks) = 0; + virtual Node* Lookup(const char* name, bool traverseLinks); + virtual Node* LookupDontTraverse(const char* name) = 0; virtual status_t GetNextEntry(void *cookie, char *nameBuffer, size_t bufferSize) = 0; virtual status_t GetNextNode(void *cookie, Node **_node) = 0; diff --git a/src/system/boot/loader/RootFileSystem.cpp b/src/system/boot/loader/RootFileSystem.cpp index 441f5a1765..955eb4f50b 100644 --- a/src/system/boot/loader/RootFileSystem.cpp +++ b/src/system/boot/loader/RootFileSystem.cpp @@ -29,7 +29,7 @@ RootFileSystem::~RootFileSystem() } -status_t +status_t RootFileSystem::Open(void **_cookie, int mode) { EntryIterator *iterator = new (std::nothrow) EntryIterator(&fList); @@ -42,7 +42,7 @@ RootFileSystem::Open(void **_cookie, int mode) } -status_t +status_t RootFileSystem::Close(void *cookie) { delete (EntryIterator *)cookie; @@ -50,8 +50,8 @@ RootFileSystem::Close(void *cookie) } -Node * -RootFileSystem::Lookup(const char *name, bool /*traverseLinks*/) +Node* +RootFileSystem::LookupDontTraverse(const char* name) { EntryIterator iterator = fLinks.GetIterator(); struct entry *entry; @@ -84,7 +84,7 @@ RootFileSystem::Lookup(const char *name, bool /*traverseLinks*/) } -status_t +status_t RootFileSystem::GetNextEntry(void *_cookie, char *name, size_t size) { EntryIterator *iterator = (EntryIterator *)_cookie; @@ -98,7 +98,7 @@ RootFileSystem::GetNextEntry(void *_cookie, char *name, size_t size) } -status_t +status_t RootFileSystem::GetNextNode(void *_cookie, Node **_node) { EntryIterator *iterator = (EntryIterator *)_cookie; @@ -113,24 +113,24 @@ RootFileSystem::GetNextNode(void *_cookie, Node **_node) } -status_t +status_t RootFileSystem::Rewind(void *_cookie) { EntryIterator *iterator = (EntryIterator *)_cookie; iterator->Rewind(); - return B_OK; + return B_OK; } -bool +bool RootFileSystem::IsEmpty() { return fList.IsEmpty(); } -status_t +status_t RootFileSystem::AddVolume(Directory *volume, Partition *partition) { struct entry *entry = new (std::nothrow) RootFileSystem::entry(); @@ -165,7 +165,7 @@ RootFileSystem::AddLink(const char *name, Directory *target) } -status_t +status_t RootFileSystem::GetPartitionFor(Directory *volume, Partition **_partition) { EntryIterator iterator = fList.GetIterator(); diff --git a/src/system/boot/loader/RootFileSystem.h b/src/system/boot/loader/RootFileSystem.h index e1adfc434e..6ce33de903 100644 --- a/src/system/boot/loader/RootFileSystem.h +++ b/src/system/boot/loader/RootFileSystem.h @@ -20,7 +20,7 @@ class RootFileSystem : public Directory { virtual status_t Open(void **_cookie, int mode); virtual status_t Close(void *cookie); - virtual Node *Lookup(const char *name, bool traverseLinks); + virtual Node* LookupDontTraverse(const char* name); virtual status_t GetNextEntry(void *cookie, char *nameBuffer, size_t bufferSize); virtual status_t GetNextNode(void *cookie, Node **_node); diff --git a/src/system/boot/loader/file_systems/Jamfile b/src/system/boot/loader/file_systems/Jamfile index 49109c75ec..9af1c649fb 100644 --- a/src/system/boot/loader/file_systems/Jamfile +++ b/src/system/boot/loader/file_systems/Jamfile @@ -4,4 +4,5 @@ SubInclude HAIKU_TOP src system boot loader file_systems amiga_ffs ; SubInclude HAIKU_TOP src system boot loader file_systems bfs ; SubInclude HAIKU_TOP src system boot loader file_systems fat ; SubInclude HAIKU_TOP src system boot loader file_systems hfs_plus ; +SubInclude HAIKU_TOP src system boot loader file_systems packagefs ; SubInclude HAIKU_TOP src system boot loader file_systems tarfs ; diff --git a/src/system/boot/loader/file_systems/amiga_ffs/Directory.cpp b/src/system/boot/loader/file_systems/amiga_ffs/Directory.cpp index c299717118..bec052fd93 100644 --- a/src/system/boot/loader/file_systems/amiga_ffs/Directory.cpp +++ b/src/system/boot/loader/file_systems/amiga_ffs/Directory.cpp @@ -45,14 +45,14 @@ Directory::~Directory() } -status_t +status_t Directory::InitCheck() { return fNode.ValidateCheckSum(); } -status_t +status_t Directory::Open(void **_cookie, int mode) { _inherited::Open(_cookie, mode); @@ -71,7 +71,7 @@ Directory::Open(void **_cookie, int mode) } -status_t +status_t Directory::Close(void *cookie) { _inherited::Close(cookie); @@ -81,8 +81,8 @@ Directory::Close(void *cookie) } -Node * -Directory::Lookup(const char *name, bool traverseLinks) +Node* +Directory::LookupDontTraverse(const char* name) { if (!strcmp(name, ".")) { Acquire(); @@ -113,7 +113,7 @@ Directory::Lookup(const char *name, bool traverseLinks) } -status_t +status_t Directory::GetNextEntry(void *cookie, char *name, size_t size) { HashIterator *iterator = (HashIterator *)cookie; @@ -127,7 +127,7 @@ Directory::GetNextEntry(void *cookie, char *name, size_t size) } -status_t +status_t Directory::GetNextNode(void *cookie, Node **_node) { return B_ERROR; diff --git a/src/system/boot/loader/file_systems/amiga_ffs/Directory.h b/src/system/boot/loader/file_systems/amiga_ffs/Directory.h index bed55f8f32..4ba2aeb7dc 100644 --- a/src/system/boot/loader/file_systems/amiga_ffs/Directory.h +++ b/src/system/boot/loader/file_systems/amiga_ffs/Directory.h @@ -27,7 +27,7 @@ class Directory : public ::Directory { virtual status_t Open(void **_cookie, int mode); virtual status_t Close(void *cookie); - virtual Node *Lookup(const char *name, bool traverseLinks); + virtual Node* LookupDontTraverse(const char* name); virtual status_t GetNextEntry(void *cookie, char *nameBuffer, size_t bufferSize); virtual status_t GetNextNode(void *cookie, Node **_node); diff --git a/src/system/boot/loader/file_systems/bfs/Directory.cpp b/src/system/boot/loader/file_systems/bfs/Directory.cpp index 91fe02ffab..705e598c81 100644 --- a/src/system/boot/loader/file_systems/bfs/Directory.cpp +++ b/src/system/boot/loader/file_systems/bfs/Directory.cpp @@ -52,14 +52,14 @@ Directory::~Directory() } -status_t +status_t Directory::InitCheck() { return fStream.InitCheck(); } -status_t +status_t Directory::Open(void **_cookie, int mode) { _inherited::Open(_cookie, mode); @@ -72,7 +72,7 @@ Directory::Open(void **_cookie, int mode) } -status_t +status_t Directory::Close(void *cookie) { _inherited::Close(cookie); @@ -82,43 +82,18 @@ Directory::Close(void *cookie) } -Node * -Directory::Lookup(const char *name, bool traverseLinks) +Node* +Directory::LookupDontTraverse(const char* name) { off_t id; if (fTree.Find((uint8 *)name, strlen(name), &id) < B_OK) return NULL; - Node *node = Stream::NodeFactory(fStream.GetVolume(), id); - if (!node) - return NULL; - - if (S_ISLNK(node->Type())) { - // the node is a symbolic link, so we have to resolve the path - char linkPath[B_PATH_NAME_LENGTH]; - ((Link *)node)->ReadLink(linkPath, sizeof(linkPath)); - - delete node; - // we don't need this one anymore - - int fd = open_from(this, linkPath, O_RDONLY); - if (fd >= 0) { - node = get_node_from(fd); - if (node != NULL) - node->Acquire(); - - close(fd); - return node; - } - - return NULL; - } - - return node; + return Stream::NodeFactory(fStream.GetVolume(), id); } -status_t +status_t Directory::GetNextEntry(void *cookie, char *name, size_t size) { TreeIterator *iterator = (TreeIterator *)cookie; @@ -129,7 +104,7 @@ Directory::GetNextEntry(void *cookie, char *name, size_t size) } -status_t +status_t Directory::GetNextNode(void *cookie, Node **_node) { TreeIterator *iterator = (TreeIterator *)cookie; @@ -149,7 +124,7 @@ Directory::GetNextNode(void *cookie, Node **_node) } -status_t +status_t Directory::Rewind(void *cookie) { TreeIterator *iterator = (TreeIterator *)cookie; @@ -158,7 +133,7 @@ Directory::Rewind(void *cookie) } -bool +bool Directory::IsEmpty() { TreeIterator iterator(&fTree); diff --git a/src/system/boot/loader/file_systems/bfs/Directory.h b/src/system/boot/loader/file_systems/bfs/Directory.h index 4645b8154c..8d625477fb 100644 --- a/src/system/boot/loader/file_systems/bfs/Directory.h +++ b/src/system/boot/loader/file_systems/bfs/Directory.h @@ -27,7 +27,7 @@ class Directory : public ::Directory { virtual status_t Open(void **_cookie, int mode); virtual status_t Close(void *cookie); - virtual Node *Lookup(const char *name, bool traverseLinks); + virtual Node* LookupDontTraverse(const char* name); virtual status_t GetNextEntry(void *cookie, char *nameBuffer, size_t bufferSize); virtual status_t GetNextNode(void *cookie, Node **_node); diff --git a/src/system/boot/loader/file_systems/bfs/Link.h b/src/system/boot/loader/file_systems/bfs/Link.h index 167bfa07d6..f648be0b8a 100644 --- a/src/system/boot/loader/file_systems/bfs/Link.h +++ b/src/system/boot/loader/file_systems/bfs/Link.h @@ -20,7 +20,8 @@ class Link : public File { Link(const Stream &stream); status_t InitCheck(); - status_t ReadLink(char *buffer, size_t bufferSize); + + virtual status_t ReadLink(char *buffer, size_t bufferSize); virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize); virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize); diff --git a/src/system/boot/loader/file_systems/fat/Directory.cpp b/src/system/boot/loader/file_systems/fat/Directory.cpp index f1e56e6f5a..ff152d6384 100644 --- a/src/system/boot/loader/file_systems/fat/Directory.cpp +++ b/src/system/boot/loader/file_systems/fat/Directory.cpp @@ -351,10 +351,10 @@ Directory::Close(void *cookie) } -Node * -Directory::Lookup(const char *name, bool traverseLinks) +Node* +Directory::LookupDontTraverse(const char* name) { - TRACE(("FASFS::Directory::%s('%s', %d)\n", __FUNCTION__, name, traverseLinks)); + TRACE(("FASFS::Directory::%s('%s')\n", __FUNCTION__, name)); if (!strcmp(name, ".")) { Acquire(); return this; diff --git a/src/system/boot/loader/file_systems/fat/Directory.h b/src/system/boot/loader/file_systems/fat/Directory.h index 5bb10d560e..bb400420cd 100644 --- a/src/system/boot/loader/file_systems/fat/Directory.h +++ b/src/system/boot/loader/file_systems/fat/Directory.h @@ -31,7 +31,7 @@ class Directory : public ::Directory { virtual status_t Open(void **_cookie, int mode); virtual status_t Close(void *cookie); - virtual Node *Lookup(const char *name, bool traverseLinks); + virtual Node* LookupDontTraverse(const char* name); virtual status_t GetNextEntry(void *cookie, char *nameBuffer, size_t bufferSize); virtual status_t GetNextNode(void *cookie, Node **_node); diff --git a/src/system/boot/loader/file_systems/packagefs/Jamfile b/src/system/boot/loader/file_systems/packagefs/Jamfile new file mode 100644 index 0000000000..807c67c24e --- /dev/null +++ b/src/system/boot/loader/file_systems/packagefs/Jamfile @@ -0,0 +1,46 @@ +SubDir HAIKU_TOP src system boot loader file_systems packagefs ; + +UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ; +UsePrivateHeaders kernel shared ; +SubDirSysHdrs $(HAIKU_TOP) headers libs zlib ; + +DEFINES += _BOOT_MODE ; + +local kernelC++Header = [ FDirName $(HAIKU_TOP) headers private kernel util + kernel_cpp.h ] ; + +SubDirC++Flags -fno-rtti -include $(kernelC++Header) ; + +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package ] ; +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src kits package hpkg ] ; + + +KernelStaticLibrary boot_packagefs : + packagefs.cpp + + # package kit + BlockBufferCacheNoLock.cpp + + # package kit/hpkg + BlockBufferCache.cpp + BlockBufferCacheImpl.cpp + BufferCache.cpp + CachedBuffer.cpp + DataOutput.cpp + DataReader.cpp + ErrorOutput.cpp + FDDataReader.cpp + PackageContentHandler.cpp + PackageData.cpp + PackageDataReader.cpp + PackageEntry.cpp + PackageEntryAttribute.cpp + PackageReaderImpl.cpp + ReaderImplBase.cpp + + # compression + ZlibCompressionBase.cpp + ZlibDecompressor.cpp + + : -fno-pic +; diff --git a/src/system/boot/loader/file_systems/packagefs/packagefs.cpp b/src/system/boot/loader/file_systems/packagefs/packagefs.cpp new file mode 100644 index 0000000000..eb6abc7f92 --- /dev/null +++ b/src/system/boot/loader/file_systems/packagefs/packagefs.cpp @@ -0,0 +1,807 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "packagefs.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + + +#if 0 +# define RETURN_ERROR(error) return (error); +#else +# define RETURN_ERROR(err) \ + { \ + status_t _status = err; \ + if (_status < B_OK) \ + dprintf("%s:%d: %s\n", __FILE__, __LINE__, strerror(_status)); \ + return _status; \ + } +#endif + + +using namespace BPackageKit; +using namespace BPackageKit::BHPKG; +using BPackageKit::BHPKG::BPrivate::PackageReaderImpl; + + +namespace { + + +struct PackageDirectory; +struct PackageNode; +struct PackageVolume; + + +static status_t create_node(PackageNode* packageNode, ::Node*& _node); + + +// #pragma mark - PackageNode + + +struct PackageNode : DoublyLinkedListLinkImpl { + PackageNode(PackageVolume* volume, mode_t mode) + : + fVolume(volume), + fParentDirectory(NULL), + fName(NULL), + fMode(mode) + { + fModifiedTime.tv_sec = 0; + fModifiedTime.tv_nsec = 0; + } + + ~PackageNode() + { + free(fName); + } + + status_t Init(PackageDirectory* parentDir, const char* name) + { + fParentDirectory = parentDir; + fName = strdup(name); + + return fName != NULL ? B_OK : B_NO_MEMORY; + } + + PackageVolume* Volume() const + { + return fVolume; + } + + const char* Name() const + { + return fName; + } + + mode_t Mode() const + { + return fMode; + } + + void SetModifiedTime(const timespec& time) + { + fModifiedTime = time; + } + + const timespec& ModifiedTime() const + { + return fModifiedTime; + } + +protected: + PackageVolume* fVolume; + PackageDirectory* fParentDirectory; + char* fName; + mode_t fMode; + timespec fModifiedTime; +}; + + +// #pragma mark - PackageFile + + +struct PackageFile : PackageNode { + PackageFile(PackageVolume* volume, mode_t mode, const BPackageData& data) + : + PackageNode(volume, mode), + fData(data) + { + } + + const BPackageData& Data() const + { + return fData; + } + + off_t Size() const + { + return fData.UncompressedSize(); + } + +private: + BPackageData fData; +}; + + +// #pragma mark - PackageSymlink + + +struct PackageSymlink : PackageNode { + PackageSymlink(PackageVolume* volume, mode_t mode) + : + PackageNode(volume, mode), + fPath(NULL) + { + } + + ~PackageSymlink() + { + free(fPath); + } + + status_t SetSymlinkPath(const char* path) + { + fPath = strdup(path); + return fPath != NULL ? B_OK : B_NO_MEMORY; + } + + const char* SymlinkPath() const + { + return fPath; + } + +private: + char* fPath; +}; + + +// #pragma mark - PackageDirectory + + +struct PackageDirectory : PackageNode { + PackageDirectory(PackageVolume* volume, mode_t mode) + : + PackageNode(volume, mode) + { + } + + void AddChild(PackageNode* node) + { + fEntries.Add(node); + } + + PackageNode* FirstChild() const + { + return fEntries.Head(); + } + + PackageNode* NextChild(PackageNode* child) const + { + return fEntries.GetNext(child); + } + + PackageNode* Lookup(const char* name) const + { + for (NodeList::ConstIterator it = fEntries.GetIterator(); + PackageNode* child = it.Next();) { + if (strcmp(child->Name(), name) == 0) + return child; + } + + return NULL; + } + +private: + typedef DoublyLinkedList NodeList; + +private: + NodeList fEntries; +}; + + +// #pragma mark - FileDataReader + + +struct FileDataReader { + FileDataReader(int fd, const BPackageData& data) + : + fDataReader(fd), + fData(data), + fPackageDataReader(NULL) + { + } + + ~FileDataReader() + { + delete fPackageDataReader; + } + + status_t Init(BPackageDataReaderFactory* dataReaderFactory) + { + if (fData.IsEncodedInline()) + return B_OK; + + return dataReaderFactory->CreatePackageDataReader(&fDataReader, + fData, fPackageDataReader); + } + + status_t ReadData(off_t offset, void* buffer, size_t bufferSize) + { + if (fData.IsEncodedInline()) { + BBufferDataReader dataReader(fData.InlineData(), + fData.CompressedSize()); + return dataReader.ReadData(offset, buffer, bufferSize); + } + + return fPackageDataReader->ReadData(offset, buffer, bufferSize); + } + +private: + BFDDataReader fDataReader; + BPackageData fData; + BPackageDataReader* fPackageDataReader; +}; + + +// #pragma mark - PackageVolume + + +struct PackageVolume : BReferenceable { + PackageVolume() + : + fRootDirectory(this, S_IFDIR), + fBufferCache(B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, 2), + fDataReaderFactory(&fBufferCache), + fFD(-1) + { + } + + ~PackageVolume() + { + if (fFD >= 0) + close(fFD); + } + + status_t Init(int fd) + { + status_t error = fBufferCache.Init(); + if (error != B_OK) + return error; + + fFD = dup(fd); + if (fFD < 0) + return errno; + + return B_OK; + } + + PackageDirectory* RootDirectory() + { + return &fRootDirectory; + } + + void AddNode(PackageNode* node) + { + fRootDirectory.AddChild(node); + } + + status_t CreateFileDataReader(const BPackageData& data, + FileDataReader*& _fileDataReader) + { + // create the file reader object + FileDataReader* fileDataReader = new(std::nothrow) FileDataReader(fFD, + data); + if (fileDataReader == NULL) + return B_NO_MEMORY; + ObjectDeleter fileDataReaderDeleter(fileDataReader); + + status_t error = fileDataReader->Init(&fDataReaderFactory); + if (error != B_OK) + return error; + + _fileDataReader = fileDataReaderDeleter.Detach(); + return B_OK; + } + +private: + PackageDirectory fRootDirectory; + BBlockBufferCacheNoLock fBufferCache; + BPackageDataReaderFactory fDataReaderFactory; + int fFD; +}; + + +// #pragma mark - PackageLoaderErrorOutput + + +struct PackageLoaderErrorOutput : BErrorOutput { + PackageLoaderErrorOutput() + { + } + + virtual void PrintErrorVarArgs(const char* format, va_list args) + { + } +}; + + +// #pragma mark - PackageLoaderContentHandler + + +struct PackageLoaderContentHandler : BPackageContentHandler { + PackageLoaderContentHandler(PackageVolume* volume) + : + fVolume(volume), + fErrorOccurred(false) + { + } + + status_t Init() + { + return B_OK; + } + + virtual status_t HandleEntry(BPackageEntry* entry) + { + if (fErrorOccurred) + return B_OK; + + PackageDirectory* parentDir = NULL; + if (const BPackageEntry* parentEntry = entry->Parent()) { + if (!S_ISDIR(parentEntry->Mode())) + RETURN_ERROR(B_BAD_DATA); + + parentDir = static_cast( + (PackageNode*)parentEntry->UserToken()); + } + + status_t error; + + // get the file mode -- filter out write permissions + mode_t mode = entry->Mode() & ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); + + // create the package node + PackageNode* node; + if (S_ISREG(mode)) { + // file + node = new(std::nothrow) PackageFile(fVolume, mode, entry->Data()); + } else if (S_ISLNK(mode)) { + // symlink + PackageSymlink* symlink = new(std::nothrow) PackageSymlink( + fVolume, mode); + if (symlink == NULL) + RETURN_ERROR(B_NO_MEMORY); + + error = symlink->SetSymlinkPath(entry->SymlinkPath()); + if (error != B_OK) { + delete symlink; + return error; + } + + node = symlink; + } else if (S_ISDIR(mode)) { + // directory + node = new(std::nothrow) PackageDirectory(fVolume, mode); + } else + RETURN_ERROR(B_BAD_DATA); + + if (node == NULL) + RETURN_ERROR(B_NO_MEMORY); + + error = node->Init(parentDir, entry->Name()); + if (error != B_OK) { + delete node; + RETURN_ERROR(error); + } + + node->SetModifiedTime(entry->ModifiedTime()); + + // add it to the parent directory + if (parentDir != NULL) + parentDir->AddChild(node); + else + fVolume->AddNode(node); + + entry->SetUserToken(node); + + return B_OK; + } + + virtual status_t HandleEntryAttribute(BPackageEntry* entry, + BPackageEntryAttribute* attribute) + { + // attributes aren't needed in the boot loader + return B_OK; + } + + virtual status_t HandleEntryDone(BPackageEntry* entry) + { + return B_OK; + } + + virtual status_t HandlePackageAttribute( + const BPackageInfoAttributeValue& value) + { + // TODO? + return B_OK; + } + + virtual void HandleErrorOccurred() + { + fErrorOccurred = true; + } + +private: + PackageVolume* fVolume; + bool fErrorOccurred; +}; + + +// #pragma mark - File + + +struct File : ::Node { + File(PackageFile* file) + : + fFile(file) + { + fFile->Volume()->AcquireReference(); + } + + ~File() + { + fFile->Volume()->ReleaseReference(); + } + + virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, + size_t bufferSize) + { + off_t size = fFile->Size(); + if (pos < 0 || pos > size) + return B_BAD_VALUE; + if (pos + bufferSize > size) + bufferSize = size - pos; + + if (bufferSize > 0) { + FileDataReader* dataReader = (FileDataReader*)cookie; + status_t error = dataReader->ReadData(pos, buffer, bufferSize); + if (error != B_OK) + return error; + } + + return bufferSize; + } + + virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, + size_t bufferSize) + { + return B_READ_ONLY_DEVICE; + } + + virtual status_t GetName(char* nameBuffer, size_t bufferSize) const + { + strlcpy(nameBuffer, fFile->Name(), bufferSize); + return B_OK; + } + + virtual status_t Open(void** _cookie, int mode) + { + if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR) + return B_NOT_ALLOWED; + + FileDataReader* dataReader; + status_t error = fFile->Volume()->CreateFileDataReader(fFile->Data(), + dataReader); + if (error != B_OK) + return error; + + *_cookie = dataReader; + Acquire(); + return B_OK; + } + + virtual status_t Close(void* cookie) + { + FileDataReader* dataReader = (FileDataReader*)cookie; + delete dataReader; + Release(); + return B_OK; + } + + + virtual int32 Type() const + { + return fFile->Mode() & S_IFMT; + } + + virtual off_t Size() const + { + return fFile->Size(); + } + +private: + PackageFile* fFile; +}; + + +// #pragma mark - Symlink + + +struct Symlink : ::Node { + Symlink(PackageSymlink* symlink) + : + fSymlink(symlink) + { + fSymlink->Volume()->AcquireReference(); + } + + ~Symlink() + { + fSymlink->Volume()->ReleaseReference(); + } + + virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, + size_t bufferSize) + { + return B_BAD_VALUE; + } + + virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, + size_t bufferSize) + { + return B_READ_ONLY_DEVICE; + } + + virtual status_t ReadLink(char* buffer, size_t bufferSize) + { + const char* path = fSymlink->SymlinkPath(); + size_t size = strlen(path) + 1; + + if (size > bufferSize) + return B_BUFFER_OVERFLOW; + + memcpy(buffer, path, size); + return B_OK; + } + + virtual status_t GetName(char* nameBuffer, size_t bufferSize) const + { + strlcpy(nameBuffer, fSymlink->Name(), bufferSize); + return B_OK; + } + + virtual int32 Type() const + { + return fSymlink->Mode() & S_IFMT; + } + + virtual off_t Size() const + { + return strlen(fSymlink->SymlinkPath()) + 1; + } + +private: + PackageSymlink* fSymlink; +}; + + +// #pragma mark - Directory + + +struct Directory : ::Directory { + Directory(PackageDirectory* symlink) + : + fDirectory(symlink) + { + fDirectory->Volume()->AcquireReference(); + } + + ~Directory() + { + fDirectory->Volume()->ReleaseReference(); + } + + virtual ssize_t ReadAt(void* cookie, off_t pos, void* buffer, + size_t bufferSize) + { + return B_IS_A_DIRECTORY; + } + + virtual ssize_t WriteAt(void* cookie, off_t pos, const void *buffer, + size_t bufferSize) + { + return B_IS_A_DIRECTORY; + } + + virtual status_t GetName(char* nameBuffer, size_t bufferSize) const + { + strlcpy(nameBuffer, fDirectory->Name(), bufferSize); + return B_OK; + } + + virtual int32 Type() const + { + return fDirectory->Mode() & S_IFMT; + } + + virtual status_t Open(void** _cookie, int mode) + { + if ((mode & O_ACCMODE) != O_RDONLY && (mode & O_ACCMODE) != O_RDWR) + return B_NOT_ALLOWED; + + Cookie* cookie = new(std::nothrow) Cookie; + if (cookie == NULL) + return B_NO_MEMORY; + + cookie->nextChild = fDirectory->FirstChild(); + + Acquire(); + *_cookie = cookie; + return B_OK; + } + + virtual status_t Close(void* _cookie) + { + Cookie* cookie = (Cookie*)_cookie; + delete cookie; + Release(); + return B_OK; + } + + virtual Node* LookupDontTraverse(const char* name) + { + // look up the child + PackageNode* child = fDirectory->Lookup(name); + if (child == NULL) + return NULL; + + // create the node + ::Node* node; + return create_node(child, node) == B_OK ? node : NULL; + } + + virtual status_t GetNextEntry(void* _cookie, char* nameBuffer, + size_t bufferSize) + { + Cookie* cookie = (Cookie*)_cookie; + PackageNode* child = cookie->nextChild; + if (child == NULL) + return B_ENTRY_NOT_FOUND; + + cookie->nextChild = fDirectory->NextChild(child); + + strlcpy(nameBuffer, child->Name(), bufferSize); + return B_OK; + } + + virtual status_t GetNextNode(void* _cookie, Node** _node) + { + Cookie* cookie = (Cookie*)_cookie; + PackageNode* child = cookie->nextChild; + if (child == NULL) + return B_ENTRY_NOT_FOUND; + + cookie->nextChild = fDirectory->NextChild(child); + + return create_node(child, *_node); + } + + virtual status_t Rewind(void* _cookie) + { + Cookie* cookie = (Cookie*)_cookie; + cookie->nextChild = NULL; + return B_OK; + } + + virtual bool IsEmpty() + { + return fDirectory->FirstChild() == NULL; + } + +private: + struct Cookie { + PackageNode* nextChild; + }; + + +private: + PackageDirectory* fDirectory; +}; + + +// #pragma mark - + + +static status_t +create_node(PackageNode* packageNode, ::Node*& _node) +{ + if (packageNode == NULL) + return B_BAD_VALUE; + + ::Node* node; + switch (packageNode->Mode() & S_IFMT) { + case S_IFREG: + node = new(std::nothrow) File( + static_cast(packageNode)); + break; + case S_IFLNK: + node = new(std::nothrow) Symlink( + static_cast(packageNode)); + break; + case S_IFDIR: + node = new(std::nothrow) Directory( + static_cast(packageNode)); + break; + default: + return B_BAD_VALUE; + } + + if (node == NULL) + return B_NO_MEMORY; + + _node = node; + return B_OK; +} + + +} // unnamed namespace + + +status_t +packagefs_mount_file(int fd, ::Directory*& _mountedDirectory) +{ + PackageLoaderErrorOutput errorOutput; + PackageReaderImpl packageReader(&errorOutput); + status_t error = packageReader.Init(fd, false); + if (error != B_OK) + RETURN_ERROR(error); + + // create the volume + PackageVolume* volume = new(std::nothrow) PackageVolume; + if (volume == NULL) + return B_NO_MEMORY; + BReference volumeReference(volume, true); + + error = volume->Init(fd); + if (error != B_OK) + RETURN_ERROR(error); + + // parse content -- this constructs the entry/node tree + PackageLoaderContentHandler handler(volume); + error = handler.Init(); + if (error != B_OK) + RETURN_ERROR(error); + + error = packageReader.ParseContent(&handler); + if (error != B_OK) + RETURN_ERROR(error); + + // create a VFS node for the root node + ::Node* rootNode; + error = create_node(volume->RootDirectory(), rootNode); + if (error != B_OK) + RETURN_ERROR(error); + + _mountedDirectory = static_cast< ::Directory*>(rootNode); + return B_OK; +} diff --git a/src/system/boot/loader/file_systems/packagefs/packagefs.h b/src/system/boot/loader/file_systems/packagefs/packagefs.h new file mode 100644 index 0000000000..1ae18a0abe --- /dev/null +++ b/src/system/boot/loader/file_systems/packagefs/packagefs.h @@ -0,0 +1,27 @@ +/* + * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef BOOT_LOADER_FILE_SYSTEMS_PACKAGEFS_H +#define BOOT_LOADER_FILE_SYSTEMS_PACKAGEFS_H + + +#include + +#include + + +class Directory; +class Node; + + +__BEGIN_DECLS + + +status_t packagefs_mount_file(int fd, Directory*& _mountedDirectory); + + +__END_DECLS + + +#endif // BOOT_LOADER_FILE_SYSTEMS_PACKAGEFS_H diff --git a/src/system/boot/loader/file_systems/tarfs/tarfs.cpp b/src/system/boot/loader/file_systems/tarfs/tarfs.cpp index c85cb896a3..de71e8c0c1 100644 --- a/src/system/boot/loader/file_systems/tarfs/tarfs.cpp +++ b/src/system/boot/loader/file_systems/tarfs/tarfs.cpp @@ -119,7 +119,7 @@ public: size_t bufferSize) const; virtual TarFS::Entry* LookupEntry(const char* name); - virtual ::Node* Lookup(const char* name, bool traverseLinks); + virtual ::Node* LookupDontTraverse(const char* name); virtual status_t GetNextEntry(void* cookie, char* nameBuffer, size_t bufferSize); @@ -153,6 +153,8 @@ public: virtual ssize_t WriteAt(void* cookie, off_t pos, const void* buffer, size_t bufferSize); + virtual status_t ReadLink(char* buffer, size_t bufferSize); + virtual status_t GetName(char* nameBuffer, size_t bufferSize) const; @@ -392,25 +394,13 @@ TarFS::Directory::LookupEntry(const char* name) ::Node* -TarFS::Directory::Lookup(const char* name, bool traverseLinks) +TarFS::Directory::LookupDontTraverse(const char* name) { TarFS::Entry* entry = LookupEntry(name); if (!entry) return NULL; Node* node = entry->ToNode(); - - if (traverseLinks) { - if (S_ISLNK(node->Type())) { - Symlink* symlink = static_cast(node); - int fd = open_from(this, symlink->LinkPath(), O_RDONLY); - if (fd >= 0) { - node = get_node_from(fd); - close(fd); - } - } - } - if (node) node->Acquire(); @@ -587,6 +577,20 @@ TarFS::Symlink::WriteAt(void* cookie, off_t pos, const void* buffer, } +status_t +TarFS::Symlink::ReadLink(char* buffer, size_t bufferSize) +{ + const char* path = fHeader->linkname; + size_t size = strlen(path) + 1; + + if (size > bufferSize) + return B_BUFFER_OVERFLOW; + + memcpy(buffer, path, size); + return B_OK; +} + + status_t TarFS::Symlink::GetName(char* nameBuffer, size_t bufferSize) const { diff --git a/src/system/boot/loader/vfs.cpp b/src/system/boot/loader/vfs.cpp index a3e2801719..eeb6e6c979 100644 --- a/src/system/boot/loader/vfs.cpp +++ b/src/system/boot/loader/vfs.cpp @@ -22,6 +22,7 @@ #include #include "RootFileSystem.h" +#include "file_systems/packagefs/packagefs.h" using namespace boot; @@ -98,6 +99,13 @@ Node::Close(void *cookie) } +status_t +Node::ReadLink(char* buffer, size_t bufferSize) +{ + return B_BAD_VALUE; +} + + status_t Node::GetName(char *nameBuffer, size_t bufferSize) const { @@ -209,6 +217,40 @@ Directory::Type() const } +Node* +Directory::Lookup(const char* name, bool traverseLinks) +{ + Node* node = LookupDontTraverse(name); + if (node == NULL) + return NULL; + + if (!traverseLinks || !S_ISLNK(node->Type())) + return node; + + // the node is a symbolic link, so we have to resolve the path + char linkPath[B_PATH_NAME_LENGTH]; + status_t error = node->ReadLink(linkPath, sizeof(linkPath)); + + node->Release(); + // we don't need this one anymore + + if (error != B_OK) + return NULL; + + // let open_from() do the real work + int fd = open_from(this, linkPath, O_RDONLY); + if (fd < 0) + return NULL; + + node = get_node_from(fd); + if (node != NULL) + node->Acquire(); + + close(fd); + return node; +} + + status_t Directory::CreateFile(const char *name, mode_t permissions, Node **_node) { @@ -393,7 +435,7 @@ BootVolume::SetTo(Directory* rootDirectory) fSystemDirectory = static_cast(systemNode); - // check, if the system is packaged + // try opening the system package int packageFD = open_from(fSystemDirectory, "packages/haiku.hpkg", O_RDONLY); fPackaged = packageFD >= 0; @@ -401,10 +443,18 @@ BootVolume::SetTo(Directory* rootDirectory) return B_OK; // the system is packaged -- mount the packagefs -// TODO:... -Unset(); -dprintf("BootVolume::SetTo(): packagefs not supported yet!\n"); -return B_NOT_SUPPORTED; + Directory* packageRootDirectory; + status_t error = packagefs_mount_file(packageFD, packageRootDirectory); + close(packageFD); + if (error != B_OK) { + Unset(); + return error; + } + + fSystemDirectory->Release(); + fSystemDirectory = packageRootDirectory; + + return B_OK; } @@ -725,6 +775,13 @@ read_pos(int fd, off_t offset, void *buffer, size_t bufferSize) } +ssize_t +pread(int fd, void* buffer, size_t bufferSize, off_t offset) +{ + return read_pos(fd, offset, buffer, bufferSize); +} + + ssize_t read(int fd, void *buffer, size_t bufferSize) {