diff --git a/headers/private/haiku_package/haiku_package.h b/headers/private/haiku_package/haiku_package.h new file mode 100644 index 0000000000..15a6182a8e --- /dev/null +++ b/headers/private/haiku_package/haiku_package.h @@ -0,0 +1,155 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _HAIKU_PACKAGE_H +#define _HAIKU_PACKAGE_H + + +#include + + +// header +struct hpkg_header { + uint32 magic; // "hpkg" + uint16 header_size; + uint16 version; + uint64 total_size; + + // package attributes section + uint32 attributes_compression; + uint32 attributes_length_compressed; + uint32 attributes_length_uncompressed; + + // TOC section + uint32 toc_compression; + uint64 toc_length_compressed; + uint64 toc_length_uncompressed; + + uint64 toc_attribute_types_length; + uint64 toc_attribute_types_count; + uint64 toc_strings_length; + uint64 toc_strings_count; +}; + + +// magic, version +enum { + B_HPKG_MAGIC = 'hpkg', + B_HPKG_VERSION = 1 +}; + + +// compression types +enum { + B_HPKG_COMPRESSION_NONE = 0, + B_HPKG_COMPRESSION_ZLIB = 1 +}; + + +// attribute types +enum { + // types + B_HPKG_ATTRIBUTE_TYPE_INVALID = 0, + B_HPKG_ATTRIBUTE_TYPE_INT = 1, + B_HPKG_ATTRIBUTE_TYPE_UINT = 2, + B_HPKG_ATTRIBUTE_TYPE_STRING = 3, + B_HPKG_ATTRIBUTE_TYPE_RAW = 4 +}; + + +// attribute encodings +enum { + // signed/unsigned int encodings + B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT = 0, + B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT = 1, + B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT = 2, + B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT = 3, + + // string encodings + B_HPKG_ATTRIBUTE_ENCODING_STRING_INLINE = 0, + // null-terminated string + B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE = 1, + // unsigned LEB128 index into string table + + // raw data encodings + B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE = 0, + // unsigned LEB128 size, raw bytes + B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP = 1 + // unsigned LEB128 size, unsigned LEB128 offset into the heap +}; + + +// attribute tag arithmetics +#define B_HPKG_ATTRIBUTE_TAG_COMPOSE(index, encoding, hasChildren) \ + (((uint64(index) << 3) | uint64(encoding) << 1 \ + | ((hasChildren) ? 1 : 0)) + 1) +#define B_HPKG_ATTRIBUTE_TAG_INDEX(tag) (uint64((tag) - 1) >> 3) +#define B_HPKG_ATTRIBUTE_TAG_ENCODING(tag) ((uint64((tag) - 1) >> 1) & 0x3) +#define B_HPKG_ATTRIBUTE_TAG_HAS_CHILDREN(tag) ((uint64((tag) - 1) & 0x1) != 0) + + +// standard attribute names +#define B_HPKG_ATTRIBUTE_NAME_DIRECTORY_ENTRY "dir:entry" + // path/entry name (string) +#define B_HPKG_ATTRIBUTE_NAME_FILE_TYPE "file:type" + // file type (uint) +#define B_HPKG_ATTRIBUTE_NAME_FILE_PERMISSIONS "file:permissions" + // file permissions (uint) +#define B_HPKG_ATTRIBUTE_NAME_FILE_USER "file:user" + // file user (string) +#define B_HPKG_ATTRIBUTE_NAME_FILE_GROUP "file:group" + // file group (string) +#define B_HPKG_ATTRIBUTE_NAME_FILE_ATIME "file:atime" + // file access time in seconds (uint) +#define B_HPKG_ATTRIBUTE_NAME_FILE_MTIME "file:mtime" + // file modification time in seconds (uint) +#define B_HPKG_ATTRIBUTE_NAME_FILE_CRTIME "file:crtime" + // file creation time in seconds (uint) +#define B_HPKG_ATTRIBUTE_NAME_FILE_ATIME_NANOS "file:atime:nanos" + // file access time nanoseconds fraction (uint) +#define B_HPKG_ATTRIBUTE_NAME_FILE_MTIME_NANOS "file:mtime:nanos" + // file modification time nanoseconds fraction (uint) +#define B_HPKG_ATTRIBUTE_NAME_FILE_CRTIM_NANOS "file:crtime:nanos" + // file creation time nanoseconds fraction (uint) +#define B_HPKG_ATTRIBUTE_NAME_FILE_ATTRIBUTE "file:attribute" + // file attribute (string) +#define B_HPKG_ATTRIBUTE_NAME_FILE_ATTRIBUTE_TYPE "file:attribute:type" + // file attribute type (uint) +#define B_HPKG_ATTRIBUTE_NAME_DATA "data" + // (file/attribute) data (raw) +#define B_HPKG_ATTRIBUTE_NAME_DATA_COMPRESSION "data:compression" + // (file/attribute) data compression (uint, default: none) +#define B_HPKG_ATTRIBUTE_NAME_DATA_SIZE "data:size" + // (file/attribute) uncompressed data size (uint) +#define B_HPKG_ATTRIBUTE_NAME_DATA_CHUNK_SIZE "data:chunk_size" + // the size of compressed (file/attribute) data chunks (uint) +#define B_HPKG_ATTRIBUTE_NAME_SYMLINK_PATH "symlink:path" + // symlink path (string) + + +// file types (B_HPKG_ATTRIBUTE_NAME_FILE_TYPE) +enum { + B_HPKG_FILE_TYPE_FILE = 0, + B_HPKG_FILE_TYPE_DIRECTORY = 1, + B_HPKG_FILE_TYPE_SYMLINK = 2 +}; + + +// maximum number of bytes of data to be encoded inline; more will be allocated +// on the heap +#define B_HPKG_MAX_INLINE_DATA_SIZE 8 + + +// default values +enum { + B_HPKG_DEFAULT_FILE_TYPE = B_HPKG_FILE_TYPE_FILE, + B_HPKG_DEFAULT_FILE_PERMISSIONS = 0644, + B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS = 0755, + B_HPKG_DEFAULT_SYMLINK_PERMISSIONS = 0777, + B_HPKG_DEFAULT_DATA_COMPRESSION = B_HPKG_COMPRESSION_NONE, + B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB = 64 * 1024 +}; + + +#endif // _HAIKU_PACKAGE_H diff --git a/src/bin/Jamfile b/src/bin/Jamfile index 7f9610be68..7580f07089 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -218,6 +218,7 @@ SubInclude HAIKU_TOP src bin mkdepend ; SubInclude HAIKU_TOP src bin mkdos ; SubInclude HAIKU_TOP src bin mkfs ; SubInclude HAIKU_TOP src bin multiuser ; +SubInclude HAIKU_TOP src bin package ; SubInclude HAIKU_TOP src bin patch ; SubInclude HAIKU_TOP src bin pc ; SubInclude HAIKU_TOP src bin pcmcia-cs ; diff --git a/src/bin/package/DataReader.cpp b/src/bin/package/DataReader.cpp new file mode 100644 index 0000000000..05aa82af87 --- /dev/null +++ b/src/bin/package/DataReader.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "DataReader.h" + +#include +#include +#include + + +// #pragma mark - DataReader + + +DataReader::~DataReader() +{ +} + + +// #pragma mark - FDDataReader + + +FDDataReader::FDDataReader(int fd) + : + fFD(fd) +{ +} + + +status_t +FDDataReader::ReadData(off_t offset, void* buffer, size_t size) +{ + ssize_t bytesRead = pread(fFD, buffer, size, offset); + if (bytesRead < 0) + return errno; + return (size_t)bytesRead == size ? B_OK : B_ERROR; +} + + +// #pragma mark - BufferDataReader + + +BufferDataReader::BufferDataReader(const void* data, size_t size) + : + fData(data), + fSize(size) +{ +} + + +status_t +BufferDataReader::ReadData(off_t offset, void* buffer, size_t size) +{ + if (size == 0) + return B_OK; + + if (offset < 0) + return B_BAD_VALUE; + + if (size > fSize || offset > fSize - size) + return B_ERROR; + + memcpy(buffer, (const uint8*)fData + offset, size); + return B_OK; +} diff --git a/src/bin/package/DataReader.h b/src/bin/package/DataReader.h new file mode 100644 index 0000000000..6ac47f9a64 --- /dev/null +++ b/src/bin/package/DataReader.h @@ -0,0 +1,46 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef DATA_READER_H +#define DATA_READER_H + + +#include + + +class DataReader { +public: + virtual ~DataReader(); + + virtual status_t ReadData(off_t offset, void* buffer, + size_t size) = 0; +}; + + +class FDDataReader : public DataReader { +public: + FDDataReader(int fd); + + virtual status_t ReadData(off_t offset, void* buffer, + size_t size); + +private: + int fFD; +}; + + +class BufferDataReader : public DataReader { +public: + BufferDataReader(const void* data, size_t size); + + virtual status_t ReadData(off_t offset, void* buffer, + size_t size); + +private: + const void* fData; + size_t fSize; +}; + + +#endif // DATA_READER_H diff --git a/src/bin/package/FDCloser.h b/src/bin/package/FDCloser.h new file mode 100644 index 0000000000..b21f9ad226 --- /dev/null +++ b/src/bin/package/FDCloser.h @@ -0,0 +1,27 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef FD_CLOSER_H +#define FD_CLOSER_H + + +struct FDCloser { + FDCloser(int fd) + : + fFD(fd) + { + } + + ~FDCloser() + { + if (fFD >= 0) + close(fFD); + } + +private: + int fFD; +}; + + +#endif // FD_CLOSER_H diff --git a/src/bin/package/Jamfile b/src/bin/package/Jamfile new file mode 100644 index 0000000000..2a5530d4c7 --- /dev/null +++ b/src/bin/package/Jamfile @@ -0,0 +1,25 @@ +SubDir HAIKU_TOP src bin package ; + +UsePrivateHeaders kernel shared ; +UsePrivateHeaders haiku_package ; + +DEFINES += B_ENABLE_INCOMPLETE_POSIX_AT_SUPPORT ; + # TODO: Remove when it is complete! + +BinCommand package : + command_create.cpp + command_dump.cpp + command_extract.cpp + command_list.cpp + DataReader.cpp + package.cpp + PackageData.cpp + PackageDataReader.cpp + PackageEntry.cpp + PackageEntryAttribute.cpp + PackageReader.cpp + PackageWriter.cpp + Strings.cpp + : + $(TARGET_LIBSUPC++) +; diff --git a/src/bin/package/PackageAttributeValue.h b/src/bin/package/PackageAttributeValue.h new file mode 100644 index 0000000000..f7fcaefe66 --- /dev/null +++ b/src/bin/package/PackageAttributeValue.h @@ -0,0 +1,147 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_ATTRIBUTE_VALUE_H +#define PACKAGE_ATTRIBUTE_VALUE_H + + +#include + +#include + + +struct PackageAttributeValue { + union { + int64 signedInt; + uint64 unsignedInt; + const char* string; + struct { + uint64 size; + union { + uint64 offset; + uint8 raw[B_HPKG_MAX_INLINE_DATA_SIZE]; + }; + } data; + }; + uint8 type; + uint8 encoding; + +public: + inline PackageAttributeValue(); + + inline void SetTo(int8 value); + inline void SetTo(uint8 value); + inline void SetTo(int16 value); + inline void SetTo(uint16 value); + inline void SetTo(int32 value); + inline void SetTo(uint32 value); + inline void SetTo(int64 value); + inline void SetTo(uint64 value); + inline void SetTo(const char* value); + inline void SetToData(uint64 size, uint64 offset); + inline void SetToData(uint64 size, const void* rawData); +}; + + +PackageAttributeValue::PackageAttributeValue() + : + type(B_HPKG_ATTRIBUTE_TYPE_INVALID) +{ +} + + +void +PackageAttributeValue::SetTo(int8 value) +{ + signedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_INT; +} + + +void +PackageAttributeValue::SetTo(uint8 value) +{ + unsignedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_UINT; +} + + +void +PackageAttributeValue::SetTo(int16 value) +{ + signedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_INT; +} + + +void +PackageAttributeValue::SetTo(uint16 value) +{ + unsignedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_UINT; +} + + +void +PackageAttributeValue::SetTo(int32 value) +{ + signedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_INT; +} + + +void +PackageAttributeValue::SetTo(uint32 value) +{ + unsignedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_UINT; +} + + +void +PackageAttributeValue::SetTo(int64 value) +{ + signedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_INT; +} + + +void +PackageAttributeValue::SetTo(uint64 value) +{ + unsignedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_UINT; +} + + +void +PackageAttributeValue::SetTo(const char* value) +{ + string = value; + type = B_HPKG_ATTRIBUTE_TYPE_STRING; +} + + +void +PackageAttributeValue::SetToData(uint64 size, uint64 offset) +{ + data.size = size; + data.offset = offset; + type = B_HPKG_ATTRIBUTE_TYPE_RAW; + encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP; +} + + +void +PackageAttributeValue::SetToData(uint64 size, const void* rawData) +{ + data.size = size; + if (size > 0) + memcpy(data.raw, rawData, size); + type = B_HPKG_ATTRIBUTE_TYPE_RAW; + encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE; +} + + +#endif // PACKAGE_ATTRIBUTE_VALUE_H diff --git a/src/bin/package/PackageData.cpp b/src/bin/package/PackageData.cpp new file mode 100644 index 0000000000..be9731959e --- /dev/null +++ b/src/bin/package/PackageData.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageData.h" + +#include + + +PackageData::PackageData() + : + fCompressedSize(0), + fUncompressedSize(0), + fCompression(B_HPKG_COMPRESSION_NONE), + fEncodedInline(true) +{ +} + + +void +PackageData::SetData(uint64 size, uint64 offset) +{ + fUncompressedSize = fCompressedSize = size; + fOffset = offset; + fEncodedInline = false; +} + + +void +PackageData::SetData(uint8 size, const void* data) +{ + fUncompressedSize = fCompressedSize = size; + if (size > 0) + memcpy(fInlineData, data, size); + fEncodedInline = true; +} diff --git a/src/bin/package/PackageData.h b/src/bin/package/PackageData.h new file mode 100644 index 0000000000..0ba59c0a51 --- /dev/null +++ b/src/bin/package/PackageData.h @@ -0,0 +1,47 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_DATA_H +#define PACKAGE_DATA_H + + +#include + + +class PackageData { +public: + PackageData(); + + uint64 CompressedSize() const + { return fCompressedSize; } + uint64 UncompressedSize() const + { return fUncompressedSize; } + uint64 Offset() const { return fOffset; } + uint32 Compression() const { return fCompression; } + + bool IsEncodedInline() const + { return fEncodedInline; } + const uint8* InlineData() const { return fInlineData; } + + void SetData(uint64 size, uint64 offset); + void SetData(uint8 size, const void* data); + + void SetCompression(uint32 compression) + { fCompression = compression; } + void SetUncompressedSize(uint64 size) + { fUncompressedSize = size; } + +private: + uint64 fCompressedSize; + uint64 fUncompressedSize; + union { + uint64 fOffset; + uint8 fInlineData[B_HPKG_MAX_INLINE_DATA_SIZE]; + }; + uint32 fCompression; + bool fEncodedInline; +}; + + +#endif // PACKAGE_DATA_H diff --git a/src/bin/package/PackageDataReader.cpp b/src/bin/package/PackageDataReader.cpp new file mode 100644 index 0000000000..52a725668b --- /dev/null +++ b/src/bin/package/PackageDataReader.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageDataReader.h" + +#include + +#include + +#include "PackageData.h" + + +// #pragma mark - PackageDataReader + + +PackageDataReader::PackageDataReader(DataReader* dataReader) + : + fDataReader(dataReader) +{ +} + + +PackageDataReader::~PackageDataReader() +{ +} + + +// #pragma mark - UncompressedPackageDataReader + + +class UncompressedPackageDataReader : public PackageDataReader { +public: + UncompressedPackageDataReader(DataReader* dataReader) + : + PackageDataReader(dataReader) + { + } + + status_t Init(const PackageData& data) + { + fOffset = data.Offset(); + fSize = data.UncompressedSize(); + return B_OK; + } + + virtual uint64 Size() const + { + return fSize; + } + + virtual size_t BlockSize() const + { + // TODO: Some other value? + return 64 * 1024; + } + + virtual status_t ReadData(off_t offset, void* buffer, size_t size) + { + if (size == 0) + return B_OK; + + if (offset < 0) + return B_BAD_VALUE; + + if ((uint64)offset > fSize || size > fSize - offset) + return B_ERROR; + + return fDataReader->ReadData(fOffset + offset, buffer, size); + } + +private: + uint64 fOffset; + uint64 fSize; +}; + + +// #pragma mark - PackageDataReaderFactory + + +/*static*/ status_t +PackageDataReaderFactory::CreatePackageDataReader(DataReader* dataReader, + const PackageData& data, PackageDataReader*& _reader) +{ + PackageDataReader* reader; + + switch (data.Compression()) { + case B_HPKG_COMPRESSION_NONE: + reader = new(std::nothrow) UncompressedPackageDataReader( + dataReader); + break; + case B_HPKG_COMPRESSION_ZLIB: + // TODO:... + return B_UNSUPPORTED;; + default: + return B_BAD_VALUE; + } + + if (reader == NULL) + return B_NO_MEMORY; + + status_t error = reader->Init(data); + if (error != B_OK) { + delete reader; + return error; + } + + _reader = reader; + return B_OK; +} diff --git a/src/bin/package/PackageDataReader.h b/src/bin/package/PackageDataReader.h new file mode 100644 index 0000000000..438bd2cdd9 --- /dev/null +++ b/src/bin/package/PackageDataReader.h @@ -0,0 +1,38 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_DATA_READER_H +#define PACKAGE_DATA_READER_H + + +#include "DataReader.h" + + +class PackageData; + + +class PackageDataReader : public DataReader { +public: + PackageDataReader(DataReader* dataReader); + virtual ~PackageDataReader(); + + virtual status_t Init(const PackageData& data) = 0; + + virtual uint64 Size() const = 0; + virtual size_t BlockSize() const = 0; + +protected: + DataReader* fDataReader; +}; + + +class PackageDataReaderFactory { +public: + static status_t CreatePackageDataReader(DataReader* dataReader, + const PackageData& data, + PackageDataReader*& _reader); +}; + + +#endif // PACKAGE_DATA_READER_H diff --git a/src/bin/package/PackageEntry.cpp b/src/bin/package/PackageEntry.cpp new file mode 100644 index 0000000000..c609aba835 --- /dev/null +++ b/src/bin/package/PackageEntry.cpp @@ -0,0 +1,24 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageEntry.h" + + +PackageEntry::PackageEntry(PackageEntry* parent, const char* name) + : + fParent(parent), + fName(name), + fUserToken(NULL), + fMode(S_IFREG | S_IRUSR | S_IRGRP | S_IROTH), + fSymlinkPath(NULL) +{ + fAccessTime.tv_sec = 0; + fAccessTime.tv_nsec = 0; + fModifiedTime.tv_sec = 0; + fModifiedTime.tv_nsec = 0; + fCreationTime.tv_sec = 0; + fCreationTime.tv_nsec = 0; +} diff --git a/src/bin/package/PackageEntry.h b/src/bin/package/PackageEntry.h new file mode 100644 index 0000000000..2c22f6b4b9 --- /dev/null +++ b/src/bin/package/PackageEntry.h @@ -0,0 +1,84 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_ENTRY_H +#define PACKAGE_ENTRY_H + + +#include + +#include "PackageData.h" + + +class PackageEntry { +public: + PackageEntry(PackageEntry* parent, + const char* name); + + const PackageEntry* Parent() const { return fParent; } + const char* Name() const { return fName; } + void* UserToken() const { return fUserToken; } + + mode_t Mode() const { return fMode; } + + const timespec& AccessTime() const + { return fAccessTime; } + const timespec& ModifiedTime() const + { return fModifiedTime; } + const timespec& CreationTime() const + { return fCreationTime; } + + PackageData& Data() { return fData; } + + const char* SymlinkPath() const { return fSymlinkPath; } + + void SetUserToken(void* token) + { fUserToken = token; } + + void SetType(uint32 type); + void SetPermissions(uint32 permissions); + + void SetAccessTime(uint32 seconds) + { fAccessTime.tv_sec = seconds; } + void SetAccessTimeNanos(uint32 nanos) + { fAccessTime.tv_nsec = nanos; } + void SetModifiedTime(uint32 seconds) + { fModifiedTime.tv_sec = seconds; } + void SetModifiedTimeNanos(uint32 nanos) + { fModifiedTime.tv_nsec = nanos; } + void SetCreationTime(uint32 seconds) + { fCreationTime.tv_sec = seconds; } + void SetCreationTimeNanos(uint32 nanos) + { fCreationTime.tv_nsec = nanos; } + + void SetSymlinkPath(const char* path) + { fSymlinkPath = path; } +private: + PackageEntry* fParent; + const char* fName; + void* fUserToken; + mode_t fMode; + timespec fAccessTime; + timespec fModifiedTime; + timespec fCreationTime; + PackageData fData; + const char* fSymlinkPath; +}; + + +inline void +PackageEntry::SetType(uint32 type) +{ + fMode = (fMode & ~(uint32)S_IFMT) | (type & S_IFMT); +} + + +inline void +PackageEntry::SetPermissions(uint32 permissions) +{ + fMode = (fMode & ~(uint32)ALLPERMS) | (permissions & ALLPERMS); +} + + +#endif // PACKAGE_ENTRY_H diff --git a/src/bin/package/PackageEntryAttribute.cpp b/src/bin/package/PackageEntryAttribute.cpp new file mode 100644 index 0000000000..27f5bd5d80 --- /dev/null +++ b/src/bin/package/PackageEntryAttribute.cpp @@ -0,0 +1,15 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageEntryAttribute.h" + + +PackageEntryAttribute::PackageEntryAttribute(const char* name) + : + fName(name), + fType(0) +{ +} diff --git a/src/bin/package/PackageEntryAttribute.h b/src/bin/package/PackageEntryAttribute.h new file mode 100644 index 0000000000..ef76e2fd81 --- /dev/null +++ b/src/bin/package/PackageEntryAttribute.h @@ -0,0 +1,30 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_ENTRY_ATTRIBUTE_H +#define PACKAGE_ENTRY_ATTRIBUTE_H + + +#include "PackageData.h" + + +class PackageEntryAttribute { +public: + PackageEntryAttribute(const char* name); + + const char* Name() const { return fName; } + uint32 Type() const { return fType; } + + PackageData& Data() { return fData; } + + void SetType(uint32 type) { fType = type; } + +private: + const char* fName; + uint32 fType; + PackageData fData; +}; + + +#endif // PACKAGE_ENTRY_ATTRIBUTE_H diff --git a/src/bin/package/PackageReader.cpp b/src/bin/package/PackageReader.cpp new file mode 100644 index 0000000000..a8606ea221 --- /dev/null +++ b/src/bin/package/PackageReader.cpp @@ -0,0 +1,1276 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageReader.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include "PackageData.h" +#include "PackageEntry.h" +#include "PackageEntryAttribute.h" + + +// maximum TOC size, we support reading +static const size_t kMaxTOCSize = 64 * 1024 * 1024; + + +enum { + ATTRIBUTE_INDEX_DIRECTORY_ENTRY = 0, + ATTRIBUTE_INDEX_FILE_TYPE, + ATTRIBUTE_INDEX_FILE_PERMISSIONS, + ATTRIBUTE_INDEX_FILE_USER, + ATTRIBUTE_INDEX_FILE_GROUP, + ATTRIBUTE_INDEX_FILE_ATIME, + ATTRIBUTE_INDEX_FILE_MTIME, + ATTRIBUTE_INDEX_FILE_CRTIME, + ATTRIBUTE_INDEX_FILE_ATIME_NANOS, + ATTRIBUTE_INDEX_FILE_MTIME_NANOS, + ATTRIBUTE_INDEX_FILE_CRTIM_NANOS, + ATTRIBUTE_INDEX_FILE_ATTRIBUTE, + ATTRIBUTE_INDEX_FILE_ATTRIBUTE_TYPE, + ATTRIBUTE_INDEX_DATA, + ATTRIBUTE_INDEX_DATA_SIZE, + ATTRIBUTE_INDEX_DATA_COMPRESSION, + ATTRIBUTE_INDEX_SYMLINK_PATH +}; + + +struct standard_attribute_index_entry { + const char* name; + uint8 type; + int8 index; +}; + +#undef MAKE_ATTRIBUTE_INDEX_ENTRY +#define MAKE_ATTRIBUTE_INDEX_ENTRY(name, type) \ + { B_HPKG_ATTRIBUTE_NAME_##name, B_HPKG_ATTRIBUTE_TYPE_##type, \ + ATTRIBUTE_INDEX_##name } + +static const standard_attribute_index_entry kStandardAttributeIndices[] = { + MAKE_ATTRIBUTE_INDEX_ENTRY(DIRECTORY_ENTRY, STRING), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_TYPE, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_PERMISSIONS, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_USER, STRING), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_GROUP, STRING), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_ATIME, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_MTIME, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_CRTIME, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_ATIME_NANOS, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_MTIME_NANOS, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_CRTIM_NANOS, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_ATTRIBUTE, STRING), + MAKE_ATTRIBUTE_INDEX_ENTRY(FILE_ATTRIBUTE_TYPE, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(DATA, RAW), + MAKE_ATTRIBUTE_INDEX_ENTRY(DATA_SIZE, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(DATA_COMPRESSION, UINT), + MAKE_ATTRIBUTE_INDEX_ENTRY(SYMLINK_PATH, STRING), + {} +}; + + +// #pragma mark - LowLevelPackageContentHandler + + +LowLevelPackageContentHandler::~LowLevelPackageContentHandler() +{ +} + + +// #pragma mark - PackageContentHandler + + +PackageContentHandler::~PackageContentHandler() +{ +} + + +// #pragma mark - AttributeType + + +struct PackageReader::AttributeType { + uint8 type; + char name[0]; +}; + + +// #pragma mark - AttributeType + + +struct PackageReader::AttributeTypeReference { + AttributeType* type; + int32 standardIndex; +}; + + +// #pragma mark - AttributeHandler + + +struct PackageReader::AttributeHandlerContext { + union { + PackageContentHandler* packageContentHandler; + LowLevelPackageContentHandler* lowLevelPackageContentHandler; + }; + bool hasLowLevelHandler; + + uint64 heapOffset; + uint64 heapSize; + + + AttributeHandlerContext(PackageContentHandler* packageContentHandler) + : + packageContentHandler(packageContentHandler), + hasLowLevelHandler(false) + { + } + + AttributeHandlerContext( + LowLevelPackageContentHandler* lowLevelPackageContentHandler) + : + lowLevelPackageContentHandler(lowLevelPackageContentHandler), + hasLowLevelHandler(true) + { + } + + void ErrorOccurred() + { + if (hasLowLevelHandler) + lowLevelPackageContentHandler->HandleErrorOccurred(); + else + packageContentHandler->HandleErrorOccurred(); + } +}; + + +struct PackageReader::AttributeHandler + : SinglyLinkedListLinkImpl { + + virtual ~AttributeHandler() + { + } + + void SetLevel(int level) + { + fLevel = level; + } + + virtual status_t HandleChildAttribute(AttributeHandlerContext* context, + AttributeType* type, int8 typeIndex, const AttributeValue& value, + AttributeHandler** _handler) + { +printf("%*signored attribute \"%s\" (%u)\n", fLevel * 2, "", type->name, type->type); + return B_OK; + } + + virtual status_t Delete(AttributeHandlerContext* context) + { + delete this; + return B_OK; + } + +protected: + int fLevel; +}; + + +struct PackageReader::IgnoreAttributeHandler : AttributeHandler { +}; + + +struct PackageReader::DataAttributeHandler : AttributeHandler { + DataAttributeHandler(PackageData* data) + : + fData(data) + { + } + + static status_t InitData(AttributeHandlerContext* context, + PackageData* data, const AttributeValue& value) + { + if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) + data->SetData(value.data.size, value.data.raw); + else + data->SetData(value.data.size, value.data.offset); + + data->SetUncompressedSize(value.data.size); + + return B_OK; + } + + static status_t Create(AttributeHandlerContext* context, + PackageData* data, const AttributeValue& value, + AttributeHandler*& _handler) + { + DataAttributeHandler* handler = new(std::nothrow) DataAttributeHandler( + data); + if (handler == NULL) + return B_NO_MEMORY; + + InitData(context, data, value); + + _handler = handler; + return B_OK; + } + + virtual status_t HandleChildAttribute(AttributeHandlerContext* context, + AttributeType* type, int8 typeIndex, const AttributeValue& value, + AttributeHandler** _handler) + { + switch (typeIndex) { + case ATTRIBUTE_INDEX_DATA_SIZE: + fData->SetUncompressedSize(value.unsignedInt); + return B_OK; + + case ATTRIBUTE_INDEX_DATA_COMPRESSION: + { + switch (value.unsignedInt) { + case B_HPKG_COMPRESSION_NONE: + case B_HPKG_COMPRESSION_ZLIB: + break; + default: + fprintf(stderr, "Error: Invalid compression type for " + "data (%llu)\n", value.unsignedInt); + return B_BAD_DATA; + } + + fData->SetCompression(value.unsignedInt); + return B_OK; + } + } + + return AttributeHandler::HandleChildAttribute(context, type, typeIndex, + value, _handler); + } + +private: + PackageData* fData; +}; + + +struct PackageReader::AttributeAttributeHandler : AttributeHandler { + AttributeAttributeHandler(PackageEntry* entry, const char* name) + : + fEntry(entry), + fAttribute(name) + { + } + + virtual status_t HandleChildAttribute(AttributeHandlerContext* context, + AttributeType* type, int8 typeIndex, const AttributeValue& value, + AttributeHandler** _handler) + { + switch (typeIndex) { + case ATTRIBUTE_INDEX_DATA: + { + if (_handler != NULL) { + return DataAttributeHandler::Create(context, + &fAttribute.Data(), value, *_handler); + } + return DataAttributeHandler::InitData(context, &fAttribute.Data(), + value); + } + + case ATTRIBUTE_INDEX_FILE_ATTRIBUTE_TYPE: + fAttribute.SetType(value.unsignedInt); + return B_OK; + } + + return AttributeHandler::HandleChildAttribute(context, type, typeIndex, + value, _handler); + } + + virtual status_t Delete(AttributeHandlerContext* context) + { + status_t error = context->packageContentHandler->HandleEntryAttribute( + fEntry, &fAttribute); + + delete this; + return error; + } + +private: + PackageEntry* fEntry; + PackageEntryAttribute fAttribute; +}; + + +struct PackageReader::EntryAttributeHandler : AttributeHandler { + EntryAttributeHandler(PackageEntry* parentEntry, const char* name) + : + fEntry(parentEntry, name), + fNotified(false) + { + _SetFileType(B_HPKG_DEFAULT_FILE_TYPE); + } + + static status_t Create(PackageEntry* parentEntry, const char* name, + AttributeHandler*& _handler) + { + // check name + if (name[0] == '\0' || strcmp(name, ".") == 0 + || strcmp(name, "..") == 0 || strchr(name, '/') != NULL) { + fprintf(stderr, "Error: Invalid package: Invalid entry name: " + "\"%s\"\n", name); + return B_BAD_DATA; + } + + // create handler + EntryAttributeHandler* handler = new(std::nothrow) + EntryAttributeHandler(parentEntry, name); + if (handler == NULL) + return B_NO_MEMORY; + + _handler = handler; + return B_OK; + } + + virtual status_t HandleChildAttribute(AttributeHandlerContext* context, + AttributeType* type, int8 typeIndex, const AttributeValue& value, + AttributeHandler** _handler) + { + switch (typeIndex) { + case ATTRIBUTE_INDEX_DIRECTORY_ENTRY: + { + status_t error = _Notify(context); + if (error != B_OK) + return error; + +//printf("%*sentry \"%s\"\n", fLevel * 2, "", value.string); + if (_handler != NULL) { + return EntryAttributeHandler::Create(&fEntry, value.string, + *_handler); + } + return B_OK; + } + + case ATTRIBUTE_INDEX_FILE_TYPE: + return _SetFileType(value.unsignedInt); + + case ATTRIBUTE_INDEX_FILE_PERMISSIONS: + fEntry.SetPermissions(value.unsignedInt); + return B_OK; + + case ATTRIBUTE_INDEX_FILE_USER: + case ATTRIBUTE_INDEX_FILE_GROUP: + // TODO:... + break; + + case ATTRIBUTE_INDEX_FILE_ATIME: + fEntry.SetAccessTime(value.unsignedInt); + return B_OK; + + case ATTRIBUTE_INDEX_FILE_MTIME: + fEntry.SetModifiedTime(value.unsignedInt); + return B_OK; + + case ATTRIBUTE_INDEX_FILE_CRTIME: + fEntry.SetCreationTime(value.unsignedInt); + return B_OK; + + case ATTRIBUTE_INDEX_FILE_ATIME_NANOS: + fEntry.SetAccessTimeNanos(value.unsignedInt); + return B_OK; + + case ATTRIBUTE_INDEX_FILE_MTIME_NANOS: + fEntry.SetModifiedTimeNanos(value.unsignedInt); + return B_OK; + + case ATTRIBUTE_INDEX_FILE_CRTIM_NANOS: + fEntry.SetCreationTimeNanos(value.unsignedInt); + return B_OK; + + case ATTRIBUTE_INDEX_FILE_ATTRIBUTE: + { + status_t error = _Notify(context); + if (error != B_OK) + return error; + + if (_handler != NULL) { + *_handler = new(std::nothrow) AttributeAttributeHandler( + &fEntry, value.string); + if (*_handler == NULL) + return B_NO_MEMORY; + return B_OK; + } else { + PackageEntryAttribute attribute(value.string); + return context->packageContentHandler->HandleEntryAttribute( + &fEntry, &attribute); + } + } + + case ATTRIBUTE_INDEX_DATA: + { + if (_handler != NULL) { + return DataAttributeHandler::Create(context, &fEntry.Data(), + value, *_handler); + } + return DataAttributeHandler::InitData(context, &fEntry.Data(), + value); + } + + case ATTRIBUTE_INDEX_SYMLINK_PATH: + { + fEntry.SetSymlinkPath(value.string); + return B_OK; + } + } + + return AttributeHandler::HandleChildAttribute(context, type, typeIndex, + value, _handler); + } + + virtual status_t Delete(AttributeHandlerContext* context) + { + // notify if not done yet + status_t error = _Notify(context); + + // notify done + if (error == B_OK) + error = context->packageContentHandler->HandleEntryDone(&fEntry); + else + context->packageContentHandler->HandleEntryDone(&fEntry); + + delete this; + return error; + } + +private: + status_t _Notify(AttributeHandlerContext* context) + { + if (fNotified) + return B_OK; + + fNotified = true; + return context->packageContentHandler->HandleEntry(&fEntry); + } + + status_t _SetFileType(uint64 fileType) + { + switch (fileType) { + case B_HPKG_FILE_TYPE_FILE: + fEntry.SetType(S_IFREG); + fEntry.SetPermissions(B_HPKG_DEFAULT_FILE_PERMISSIONS); + break; + case B_HPKG_FILE_TYPE_DIRECTORY: + fEntry.SetType(S_IFDIR); + fEntry.SetPermissions(B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS); + break; + case B_HPKG_FILE_TYPE_SYMLINK: + fEntry.SetType(S_IFLNK); + fEntry.SetPermissions(B_HPKG_DEFAULT_SYMLINK_PERMISSIONS); + break; + default: + fprintf(stderr, "Error: Invalid file type for package " + "entry (%llu)\n", fileType); + return B_BAD_DATA; + } + return B_OK; + } + +private: + PackageEntry fEntry; + bool fNotified; +}; + + +struct PackageReader::RootAttributeHandler : AttributeHandler { + virtual status_t HandleChildAttribute(AttributeHandlerContext* context, + AttributeType* type, int8 typeIndex, const AttributeValue& value, + AttributeHandler** _handler) + { + if (typeIndex == ATTRIBUTE_INDEX_DIRECTORY_ENTRY) { +//printf("%*sentry \"%s\"\n", fLevel * 2, "", value.string); + if (_handler != NULL) { + return EntryAttributeHandler::Create(NULL, value.string, + *_handler); + } + return B_OK; + } + + return AttributeHandler::HandleChildAttribute(context, type, typeIndex, + value, _handler); + } +}; + + +struct PackageReader::PackageAttributeHandler : AttributeHandler { + PackageAttributeHandler() + : + fToken(NULL), + fAttributeName(NULL) + { + } + + PackageAttributeHandler(const char* attributeName, + const PackageAttributeValue& value, void* token) + : + fToken(token), + fAttributeName(attributeName), + fValue(value) + { + } + + virtual status_t HandleChildAttribute(AttributeHandlerContext* context, + AttributeType* type, int8 typeIndex, const AttributeValue& value, + AttributeHandler** _handler) + { + // notify the content handler + void* token; + status_t error = context->lowLevelPackageContentHandler + ->HandleAttribute(type->name, value, fToken, token); + if (error != B_OK) + return error; + + // create a subhandler for the attribute, if it has children + if (_handler != NULL) { + *_handler = new(std::nothrow) PackageAttributeHandler(type->name, + value, token); + if (*_handler == NULL) { + context->lowLevelPackageContentHandler->HandleAttributeDone( + type->name, value, token); + return B_NO_MEMORY; + } + return B_OK; + } + + // no children -- just call the done hook + return context->lowLevelPackageContentHandler->HandleAttributeDone( + type->name, value, token); + } + + virtual status_t Delete(AttributeHandlerContext* context) + { + if (fAttributeName != NULL) { + return context->lowLevelPackageContentHandler->HandleAttributeDone( + fAttributeName, fValue, fToken); + } + + return B_OK; + } + +private: + void* fToken; + const char* fAttributeName; + AttributeValue fValue; +}; + + +// #pragma mark - PackageReader + + +inline PackageReader::AttributeHandler* +PackageReader::_CurrentAttributeHandler() const +{ + return fAttributeHandlerStack->Head(); +} + + +inline void +PackageReader::_PushAttributeHandler(AttributeHandler* handler) +{ + fAttributeHandlerStack->Add(handler); +} + + +inline PackageReader::AttributeHandler* +PackageReader::_PopAttributeHandler() +{ + return fAttributeHandlerStack->RemoveHead(); +} + + +PackageReader::PackageReader() + : + fFD(-1), + fTOCSection(NULL), + fAttributeTypes(NULL), + fStrings(NULL) +{ +} + + +PackageReader::~PackageReader() +{ + if (fFD >= 0) + close(fFD); + + delete[] fStrings; + delete[] fAttributeTypes; + delete[] fTOCSection; +} + + +status_t +PackageReader::Init(const char* fileName) +{ + // open file + fFD = open(fileName, O_RDONLY); + if (fFD < 0) { + fprintf(stderr, "Error: Failed to open package file \"%s\": %s\n", + fileName, strerror(errno)); + return errno; + } + + // stat it + struct stat st; + if (fstat(fFD, &st) < 0) { + fprintf(stderr, "Error: Failed to access package file \"%s\": %s\n", + fileName, strerror(errno)); + return errno; + } + + // read the header + hpkg_header header; + status_t error = _ReadBuffer(&header, sizeof(header), 0); + if (error != B_OK) + return error; + + // check the header + + // magic + if (B_BENDIAN_TO_HOST_INT32(header.magic) != B_HPKG_MAGIC) { + fprintf(stderr, "Error: File \"%s\" is not a valid package file: " + "Invalid magic\n", fileName); + return B_BAD_DATA; + } + + // header size + fHeapOffset = B_BENDIAN_TO_HOST_INT16(header.header_size); + if ((size_t)fHeapOffset < sizeof(hpkg_header)) { + fprintf(stderr, "Error: File \"%s\" is not a valid package file: " + "Invalid header size (%llu)\n", fileName, fHeapOffset); + return B_BAD_DATA; + } + + // version + if (B_BENDIAN_TO_HOST_INT16(header.version) != B_HPKG_VERSION) { + fprintf(stderr, "Error: Can't read package file \"%s\": " + "Invalid/unsupported version (%d)\n", fileName, + B_BENDIAN_TO_HOST_INT16(header.version)); + return B_BAD_DATA; + } + + // total size + fTotalSize = B_BENDIAN_TO_HOST_INT64(header.total_size); + if (fTotalSize != (uint64)st.st_size) { + fprintf(stderr, "Error: File \"%s\" is not a valid package file: " + "Total size in header (%llu) doesn't agree with total file size " + "(%lld)\n", fileName, fTotalSize, st.st_size); + return B_BAD_DATA; + } + + // package attributes length and compression + fPackageAttributesCompression + = B_BENDIAN_TO_HOST_INT32(header.attributes_compression); + fPackageAttributesCompressedLength + = B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed); + fPackageAttributesUncompressedLength + = B_BENDIAN_TO_HOST_INT32(header.attributes_length_uncompressed); + + if (const char* errorString = _CheckCompression( + fPackageAttributesCompression, fPackageAttributesCompressedLength, + fPackageAttributesUncompressedLength)) { + fprintf(stderr, "Error: File \"%s\" is not a valid package file: " + "package attributes section: %s\n", fileName, errorString); + return B_BAD_DATA; + } + + // TOC length and compression + fTOCCompression = B_BENDIAN_TO_HOST_INT32(header.toc_compression); + fTOCCompressedLength + = B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed); + fTOCUncompressedLength + = B_BENDIAN_TO_HOST_INT64(header.toc_length_uncompressed); + + if (const char* errorString = _CheckCompression(fTOCCompression, + fTOCCompressedLength, fTOCUncompressedLength)) { + fprintf(stderr, "Error: File \"%s\" is not a valid package file: " + "TOC section: %s\n", fileName, errorString); + return B_BAD_DATA; + } + + // TOC subsections + fTOCAttributeTypesLength + = B_BENDIAN_TO_HOST_INT64(header.toc_attribute_types_length); + fTOCAttributeTypesCount + = B_BENDIAN_TO_HOST_INT64(header.toc_attribute_types_count); + fTOCStringsLength = B_BENDIAN_TO_HOST_INT64(header.toc_strings_length); + fTOCStringsCount = B_BENDIAN_TO_HOST_INT64(header.toc_strings_count); + + if (fTOCAttributeTypesLength > fTOCUncompressedLength + || fTOCStringsLength > fTOCUncompressedLength - fTOCAttributeTypesLength + || fTOCAttributeTypesCount > fTOCAttributeTypesLength + || fTOCStringsCount > fTOCStringsLength) { + fprintf(stderr, "Error: File \"%s\" is not a valid package file: " + "Invalid TOC subsections description\n", fileName); + return B_BAD_DATA; + } + + // check whether the sections fit together + if (fPackageAttributesCompressedLength > fTotalSize + || fTOCCompressedLength + > fTotalSize - fPackageAttributesCompressedLength + || fHeapOffset + > fTotalSize - fPackageAttributesCompressedLength + - fTOCCompressedLength) { + fprintf(stderr, "Error: File \"%s\" is not a valid package file: " + "The sum of the sections sizes is greater than the package size\n", + fileName); + return B_BAD_DATA; + } + + fPackageAttributesOffset = fTotalSize - fPackageAttributesCompressedLength; + fTOCSectionOffset = fPackageAttributesOffset - fTOCCompressedLength; + fHeapSize = fTOCSectionOffset - fHeapOffset; + + // TOC size sanity check + if (fTOCUncompressedLength > kMaxTOCSize) { + fprintf(stderr, "Error: Package file \"%s\" has a TOC section size of " + "%llu bytes. This is beyond the reader's sanity limit\n", fileName, + fTOCUncompressedLength); + return B_UNSUPPORTED; + } + + // read in the complete TOC + fTOCSection = new(std::nothrow) uint8[fTOCUncompressedLength]; + if (fTOCSection == NULL) { + fprintf(stderr, "Error: Out of memory!\n"); + return B_NO_MEMORY; + } + + error = _ReadBuffer(fTOCSection, fTOCUncompressedLength, fTOCSectionOffset); + if (error != B_OK) + return error; + + // start parsing the TOC + fCurrentTOCOffset = 0; + + // attribute types + error = _ParseTOCAttributeTypes(); + if (error != B_OK) + return error; + fCurrentTOCOffset += fTOCAttributeTypesLength; + + // strings + error = _ParseTOCStrings(); + if (error != B_OK) + return error; + fCurrentTOCOffset += fTOCStringsLength; + + return B_OK; +} + + +status_t +PackageReader::ParseContent(PackageContentHandler* contentHandler) +{ + AttributeHandlerContext context(contentHandler); + RootAttributeHandler rootAttributeHandler; + return _ParseContent(&context, &rootAttributeHandler); +} + + +status_t +PackageReader::ParseContent(LowLevelPackageContentHandler* contentHandler) +{ + AttributeHandlerContext context(contentHandler); + PackageAttributeHandler rootAttributeHandler; + return _ParseContent(&context, &rootAttributeHandler); +} + + +const char* +PackageReader::_CheckCompression(uint32 compression, uint64 compressedLength, + uint64 uncompressedLength) const +{ + switch (compression) { + case B_HPKG_COMPRESSION_NONE: + if (compressedLength != uncompressedLength) { + return "Uncompressed, but compressed and uncompressed length " + "don't match"; + } + return NULL; + + case B_HPKG_COMPRESSION_ZLIB: + if (compressedLength >= uncompressedLength) { + return "Compressed, but compressed length is not less than " + "uncompressed length"; + } + return NULL; + + default: + return "Invalid compression algorithm ID"; + } +} + + +status_t +PackageReader::_ParseTOCAttributeTypes() +{ + // allocate table + fAttributeTypes = new(std::nothrow) AttributeTypeReference[ + fTOCAttributeTypesCount]; + if (fAttributeTypes == NULL) { + fprintf(stderr, "Error: Out of memory!\n"); + return B_NO_MEMORY; + } + + // parse the section and fill the table + uint8* position = fTOCSection + fCurrentTOCOffset; + uint8* sectionEnd = position + fTOCAttributeTypesLength; + uint32 index = 0; + while (true) { + if (position >= sectionEnd) { + fprintf(stderr, "Error: Malformed TOC attribute types section\n"); + return B_BAD_DATA; + } + + AttributeType* type = (AttributeType*)position; + + if (type->type == 0) { + if (position + 1 != sectionEnd) { + fprintf(stderr, "Error: Excess bytes in TOC attribute types " + "section\n"); +printf("position: %p, sectionEnd: %p\n", position, sectionEnd); + return B_BAD_DATA; + } + + if (index != fTOCAttributeTypesCount) { + fprintf(stderr, "Error: Invalid TOC attribute types section: " + "Less types than specified in the header\n"); + return B_BAD_DATA; + } + + return B_OK; + } + + if (index >= fTOCAttributeTypesCount) { + fprintf(stderr, "Error: Invalid TOC attribute types section: " + "More types than specified in the header\n"); + return B_BAD_DATA; + } + + size_t nameLength = strnlen(type->name, + (char*)sectionEnd - type->name); + position = (uint8*)type->name + nameLength + 1; + fAttributeTypes[index].type = type; + fAttributeTypes[index].standardIndex = _GetStandardIndex(type); + index++; +printf("type: %u, \"%s\"\n", type->type, type->name); + } +} + + +status_t +PackageReader::_ParseTOCStrings() +{ + // allocate table + fStrings = new(std::nothrow) char*[fTOCStringsCount]; + if (fStrings == NULL) { + fprintf(stderr, "Error: Out of memory!\n"); + return B_NO_MEMORY; + } + + // parse the section and fill the table + char* position = (char*)fTOCSection + fCurrentTOCOffset; + char* sectionEnd = position + fTOCStringsLength; + uint32 index = 0; + while (true) { + if (position >= sectionEnd) { + fprintf(stderr, "Error: Malformed TOC strings section\n"); + return B_BAD_DATA; + } + + size_t stringLength = strnlen(position, (char*)sectionEnd - position); + + if (stringLength == 0) { + if (position + 1 != sectionEnd) { + fprintf(stderr, "Error: Excess bytes in TOC strings section\n"); +printf("position: %p, sectionEnd: %p\n", position, sectionEnd); + return B_BAD_DATA; + } + + if (index != fTOCStringsCount) { + fprintf(stderr, "Error: Invalid TOC strings section: " + "Less strings than specified in the header\n"); + return B_BAD_DATA; + } + + return B_OK; + } + + if (index >= fTOCStringsCount) { + fprintf(stderr, "Error: Invalid TOC strings section: " + "More strings than specified in the header\n"); + return B_BAD_DATA; + } + + fStrings[index++] = position; +printf("string: \"%s\"\n", position); + position += stringLength + 1; + } +} + + +status_t +PackageReader::_ParseContent(AttributeHandlerContext* context, + AttributeHandler* rootAttributeHandler) +{ + // parse the TOC + fCurrentTOCOffset = fTOCAttributeTypesLength + fTOCStringsLength; + + // prepare attribute handler context + context->heapOffset = fHeapOffset; + context->heapSize = fHeapSize; + + // init the attribute handler stack + AttributeHandlerList attributeHandlerStack; + fAttributeHandlerStack = &attributeHandlerStack; + fAttributeHandlerStack->Add(rootAttributeHandler); + rootAttributeHandler->SetLevel(0); + + status_t error = _ParseAttributeTree(context); + if (error == B_OK) { + if (fCurrentTOCOffset < fTOCUncompressedLength) { + fprintf(stderr, "Error: %llu excess byte(s) in TOC section\n", + fTOCUncompressedLength - fCurrentTOCOffset); + error = B_BAD_DATA; + } + } + + // clean up on error + if (error != B_OK) { + context->ErrorOccurred(); + while (AttributeHandler* handler = _PopAttributeHandler()) { + if (handler != rootAttributeHandler) + handler->Delete(context); + } + return error; + } + + return B_OK; +} + + +status_t +PackageReader::_ParseAttributeTree(AttributeHandlerContext* context) +{ + int level = 0; + + while (true) { + uint64 tag; + status_t error = _ReadUnsignedLEB128(tag); + if (error != B_OK) + return error; + + if (tag == 0) { + AttributeHandler* handler = _PopAttributeHandler(); + if (level-- == 0) + return B_OK; + + error = handler->Delete(context); + if (error != B_OK) + return error; + + continue; + } + + // get the type + uint64 typeIndex = B_HPKG_ATTRIBUTE_TAG_INDEX(tag); + if (typeIndex >= fTOCAttributeTypesCount) { + fprintf(stderr, "Error: Invalid TOC section: " + "Invalid attribute type index\n"); + return B_BAD_DATA; + } + AttributeTypeReference* type = fAttributeTypes + typeIndex; + + // get the value + AttributeValue value; + error = _ReadAttributeValue(type->type->type, + B_HPKG_ATTRIBUTE_TAG_ENCODING(tag), value); + if (error != B_OK) + return error; + + bool hasChildren = B_HPKG_ATTRIBUTE_TAG_HAS_CHILDREN(tag); + AttributeHandler* childHandler = NULL; + error = _CurrentAttributeHandler()->HandleChildAttribute(context, + type->type, type->standardIndex, value, + hasChildren ? &childHandler : NULL); + if (error != B_OK) + return error; + + // parse children + if (hasChildren) { + // create an ignore handler, if necessary + if (childHandler == NULL) { + childHandler = new(std::nothrow) IgnoreAttributeHandler; + if (childHandler == NULL) { + fprintf(stderr, "Error: Out of memory!\n"); + return B_NO_MEMORY; + } + } + + childHandler->SetLevel(level); + _PushAttributeHandler(childHandler); + level++; + } + } +} + + +status_t +PackageReader::_ReadAttributeValue(uint8 type, uint8 encoding, + AttributeValue& _value) +{ + switch (type) { + case B_HPKG_ATTRIBUTE_TYPE_INT: + case B_HPKG_ATTRIBUTE_TYPE_UINT: + { + uint64 intValue; + status_t error; + + switch (encoding) { + case B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT: + { + uint8 value; + error = _Read(value); + intValue = value; + break; + } + case B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT: + { + uint16 value; + error = _Read(value); + intValue = B_BENDIAN_TO_HOST_INT16(value); + break; + } + case B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT: + { + uint32 value; + error = _Read(value); + intValue = B_BENDIAN_TO_HOST_INT32(value); + break; + } + case B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT: + { + uint64 value; + error = _Read(value); + intValue = B_BENDIAN_TO_HOST_INT64(value); + break; + } + default: + { + fprintf(stderr, "Error: Invalid TOC section: invalid " + "encoding %d for int value type %d\n", encoding, type); + throw status_t(B_BAD_VALUE); + } + } + + if (error != B_OK) + return error; + + if (type == B_HPKG_ATTRIBUTE_TYPE_INT) + _value.SetTo((int64)intValue); + else + _value.SetTo(intValue); + + return B_OK; + } + + case B_HPKG_ATTRIBUTE_TYPE_STRING: + { + if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE) { + uint64 index; + status_t error = _ReadUnsignedLEB128(index); + if (error != B_OK) + return error; + + if (index > fTOCStringsCount) { + fprintf(stderr, "Error: Invalid TOC section: string " + "reference out of bounds\n"); + return B_BAD_DATA; + } + + _value.SetTo(fStrings[index]); + } else if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_INLINE) { + const char* string; + status_t error = _ReadString(string); + if (error != B_OK) + return error; + + _value.SetTo(string); + } else { + fprintf(stderr, "Error: Invalid TOC section: invalid string " + "encoding (%u)\n", encoding); + return B_BAD_DATA; + } + + return B_OK; + } + + case B_HPKG_ATTRIBUTE_TYPE_RAW: + { + uint64 size; + status_t error = _ReadUnsignedLEB128(size); + if (error != B_OK) + return error; + + if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) { + uint64 offset; + error = _ReadUnsignedLEB128(offset); + if (error != B_OK) + return error; + + if (offset > fHeapSize || size > fHeapSize - offset) { + fprintf(stderr, "Error: Invalid TOC section: invalid data " + "reference\n"); + return B_BAD_DATA; + } + + _value.SetToData(size, fHeapOffset + offset); + } else if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) { + if (size > B_HPKG_MAX_INLINE_DATA_SIZE) { + fprintf(stderr, "Error: Invalid TOC section: inline data " + "too long\n"); + return B_BAD_DATA; + } + + const void* buffer; + error = _GetTOCBuffer(size, buffer); + if (error != B_OK) + return error; + _value.SetToData(size, buffer); + } else { + fprintf(stderr, "Error: Invalid TOC section: invalid raw " + "encoding (%u)\n", encoding); + return B_BAD_DATA; + } + + return B_OK; + } + + default: + fprintf(stderr, "Error: Invalid TOC section: invalid value type: " + "%d\n", type); + return B_BAD_DATA; + } +} + + +status_t +PackageReader::_ReadUnsignedLEB128(uint64& _value) +{ + uint64 result = 0; + int shift = 0; + while (true) { + uint8 byte; + status_t error = _Read(byte); + if (error != B_OK) + return error; + + result |= uint64(byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + + _value = result; + return B_OK; +} + + +status_t +PackageReader::_ReadString(const char*& _string, size_t* _stringLength) +{ + const char* string = (const char*)fTOCSection + fCurrentTOCOffset; + size_t stringLength = strnlen(string, + fTOCUncompressedLength - fCurrentTOCOffset); + + if (stringLength == fTOCUncompressedLength - fCurrentTOCOffset) { + fprintf(stderr, "_ReadString(): string extends beyond TOC end\n"); + return B_BAD_DATA; + } + + _string = string; + if (_stringLength != NULL) + *_stringLength = stringLength; + + fCurrentTOCOffset += stringLength + 1; + return B_OK; +} + + +status_t +PackageReader::_GetTOCBuffer(size_t size, const void*& _buffer) +{ + if (size > fTOCUncompressedLength - fCurrentTOCOffset) { + fprintf(stderr, "_GetTOCBuffer(%lu): read beyond TOC end\n", size); + return B_BAD_DATA; + } + + _buffer = fTOCSection + fCurrentTOCOffset; + fCurrentTOCOffset += size; + return B_OK; +} + + +status_t +PackageReader::_ReadTOCBuffer(void* buffer, size_t size) +{ + if (size > fTOCUncompressedLength - fCurrentTOCOffset) { + fprintf(stderr, "_ReadTOCBuffer(%lu): read beyond TOC end\n", size); + return B_BAD_DATA; + } + + memcpy(buffer, fTOCSection + fCurrentTOCOffset, size); + fCurrentTOCOffset += size; + return B_OK; +} + + +status_t +PackageReader::_ReadBuffer(void* buffer, size_t size, off_t offset) +{ + ssize_t bytesRead = pread(fFD, buffer, size, offset); + if (bytesRead < 0) { + fprintf(stderr, "_ReadBuffer(%p, %lu) failed to read data: %s\n", + buffer, size, strerror(errno)); + return errno; + } + if ((size_t)bytesRead != size) { + fprintf(stderr, "_ReadBuffer(%p, %lu) failed to read all data\n", + buffer, size); + return B_ERROR; + } + + return B_OK; +} + + +/*static*/ int8 +PackageReader::_GetStandardIndex(const AttributeType* type) +{ + // TODO: Building a hash table once would make the loopup faster. + for (int32 i = 0; kStandardAttributeIndices[i].name != NULL; i++) { + if (type->type == kStandardAttributeIndices[i].type + && strcmp(kStandardAttributeIndices[i].name, type->name) == 0) { + return i; + } + } + + return -1; +} diff --git a/src/bin/package/PackageReader.h b/src/bin/package/PackageReader.h new file mode 100644 index 0000000000..a2e16d50e8 --- /dev/null +++ b/src/bin/package/PackageReader.h @@ -0,0 +1,155 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_READER_H +#define PACKAGE_READER_H + + +#include + +#include + +#include "PackageAttributeValue.h" + + +class PackageEntry; +class PackageEntryAttribute; + + +class LowLevelPackageContentHandler { +public: + virtual ~LowLevelPackageContentHandler(); + + virtual status_t HandleAttribute(const char* attributeName, + const PackageAttributeValue& value, + void* parentToken, void*& _token) = 0; + virtual status_t HandleAttributeDone(const char* attributeName, + const PackageAttributeValue& value, + void* token) = 0; + + virtual void HandleErrorOccurred() = 0; +}; + + +class PackageContentHandler { +public: + virtual ~PackageContentHandler(); + + virtual status_t HandleEntry(PackageEntry* entry) = 0; + virtual status_t HandleEntryAttribute(PackageEntry* entry, + PackageEntryAttribute* attribute) = 0; + virtual status_t HandleEntryDone(PackageEntry* entry) = 0; + + virtual void HandleErrorOccurred() = 0; +}; + + +class PackageReader { +public: + PackageReader(); + ~PackageReader(); + + status_t Init(const char* fileName); + status_t ParseContent( + PackageContentHandler* contentHandler); + status_t ParseContent( + LowLevelPackageContentHandler* + contentHandler); + + int PackageFileFD() { return fFD; } + +private: + struct AttributeType; + struct AttributeTypeReference; + struct AttributeHandlerContext; + struct AttributeHandler; + struct IgnoreAttributeHandler; + struct DataAttributeHandler; + struct AttributeAttributeHandler; + struct EntryAttributeHandler; + struct RootAttributeHandler; + struct PackageAttributeHandler; + struct PackageContentListHandler; + + typedef PackageAttributeValue AttributeValue; + typedef SinglyLinkedList AttributeHandlerList; + +private: + status_t _Init(const char* fileName); + + const char* _CheckCompression(uint32 compression, + uint64 compressedLength, + uint64 uncompressedLength) const; + + status_t _ParseTOCAttributeTypes(); + status_t _ParseTOCStrings(); + + status_t _ParseContent(AttributeHandlerContext* context, + AttributeHandler* rootAttributeHandler); + status_t _ParseAttributeTree( + AttributeHandlerContext* context); + + status_t _ReadAttributeValue(uint8 type, uint8 encoding, + AttributeValue& _value); + + status_t _ReadUnsignedLEB128(uint64& _value); + status_t _ReadString(const char*& _string, + size_t* _stringLength = NULL); + + template + inline status_t _Read(Type& _value); + + status_t _GetTOCBuffer(size_t size, + const void*& _buffer); + status_t _ReadTOCBuffer(void* buffer, size_t size); + + status_t _ReadBuffer(void* buffer, size_t size, + off_t offset); + + static int8 _GetStandardIndex(const AttributeType* type); + + inline AttributeHandler* _CurrentAttributeHandler() const; + inline void _PushAttributeHandler( + AttributeHandler* handler); + inline AttributeHandler* _PopAttributeHandler(); + +private: + int fFD; + + uint64 fTotalSize; + uint64 fHeapOffset; + uint64 fHeapSize; + + uint32 fTOCCompression; + uint64 fTOCCompressedLength; + uint64 fTOCUncompressedLength; + uint64 fTOCSectionOffset; + uint64 fTOCAttributeTypesLength; + uint64 fTOCAttributeTypesCount; + uint64 fTOCStringsLength; + uint64 fTOCStringsCount; + + uint32 fPackageAttributesCompression; + uint32 fPackageAttributesCompressedLength; + uint32 fPackageAttributesUncompressedLength; + uint64 fPackageAttributesOffset; + + uint8* fTOCSection; + uint64 fCurrentTOCOffset; + AttributeTypeReference* fAttributeTypes; + char** fStrings; + + AttributeHandlerList* fAttributeHandlerStack; +}; + + +template +status_t +PackageReader::_Read(Type& _value) +{ + return _ReadTOCBuffer(&_value, sizeof(Type)); +} + + +#endif // PACKAGE_READER_H diff --git a/src/bin/package/PackageWriter.cpp b/src/bin/package/PackageWriter.cpp new file mode 100644 index 0000000000..a2b3325532 --- /dev/null +++ b/src/bin/package/PackageWriter.cpp @@ -0,0 +1,1196 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "PackageWriter.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +#include "FDCloser.h" +#include "Stacker.h" + + +// #pragma mark - Data interface + + +struct Data { + virtual ~Data() + { + } + + virtual ssize_t Read(void* buffer, size_t size, off_t offset) = 0; +}; + + +struct FileData : public Data { + FileData(int fd) + : + fFD(fd) + { + } + + virtual ssize_t Read(void* buffer, size_t size, off_t offset) + { + ssize_t bytesRead = pread(fFD, buffer, size, offset); + return bytesRead < 0 ? errno : bytesRead; + } + +private: + int fFD; +}; + + +struct AttributeData : public Data { + AttributeData(int fd, const char* attribute, uint32 type) + : + fFD(fd), + fAttribute(attribute) + { + } + + virtual ssize_t Read(void* buffer, size_t size, off_t offset) + { + ssize_t bytesRead = fs_read_attr(fFD, fAttribute, fType, offset, buffer, + size); + return bytesRead < 0 ? errno : bytesRead; + } + +private: + int fFD; + const char* fAttribute; + uint32 fType; +}; + + +// #pragma mark - Attributes + + +struct PackageWriter::AttributeValue { + union { + int64 signedInt; + uint64 unsignedInt; + CachedString* string; + struct { + uint64 size; + union { + uint64 offset; + uint8 raw[B_HPKG_MAX_INLINE_DATA_SIZE]; + }; + } data; + }; + uint8 type; + int8 encoding; + + AttributeValue() + : + type(B_HPKG_ATTRIBUTE_TYPE_INVALID), + encoding(-1) + { + } + + ~AttributeValue() + { + } + + void SetTo(int8 value) + { + signedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_INT; + } + + void SetTo(uint8 value) + { + unsignedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_UINT; + } + + void SetTo(int16 value) + { + signedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_INT; + } + + void SetTo(uint16 value) + { + unsignedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_UINT; + } + + void SetTo(int32 value) + { + signedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_INT; + } + + void SetTo(uint32 value) + { + unsignedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_UINT; + } + + void SetTo(int64 value) + { + signedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_INT; + } + + void SetTo(uint64 value) + { + unsignedInt = value; + type = B_HPKG_ATTRIBUTE_TYPE_UINT; + } + + void SetTo(CachedString* value) + { + string = value; + type = B_HPKG_ATTRIBUTE_TYPE_STRING; + } + + void SetToData(uint64 size, uint64 offset) + { + data.size = size; + data.offset = offset; + type = B_HPKG_ATTRIBUTE_TYPE_RAW; + encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP; + } + + void SetToData(uint64 size, const void* rawData) + { + data.size = size; + if (size > 0) + memcpy(data.raw, rawData, size); + type = B_HPKG_ATTRIBUTE_TYPE_RAW; + encoding = B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE; + } + + uint8 PreferredEncoding() const + { + switch (type) { + case B_HPKG_ATTRIBUTE_TYPE_INT: + return _PreferredIntEncoding(signedInt >= 0 + ? (uint64)signedInt << 1 + : (uint64)(-(signedInt + 1) << 1)); + case B_HPKG_ATTRIBUTE_TYPE_UINT: + return _PreferredIntEncoding(unsignedInt); + case B_HPKG_ATTRIBUTE_TYPE_STRING: + return string->index >= 0 + ? B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE + : B_HPKG_ATTRIBUTE_ENCODING_STRING_INLINE; + case B_HPKG_ATTRIBUTE_TYPE_RAW: + return encoding; + default: + return 0; + } + } + +private: + static uint8 _PreferredIntEncoding(uint64 value) + { + if (value <= 0xff) + return B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT; + if (value <= 0xffff) + return B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT; + if (value <= 0xffffffff) + return B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT; + + return B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT; + } +}; + + +struct PackageWriter::AttributeTypeKey { + const char* name; + uint8 type; + + AttributeTypeKey(const char* name, uint8 type) + : + name(name), + type(type) + { + } +}; + + +struct PackageWriter::AttributeType { + char* name; + uint8 type; + int32 index; + uint32 usageCount; + AttributeType* next; // hash table link + + AttributeType() + : + name(NULL), + type(B_HPKG_ATTRIBUTE_TYPE_INVALID), + index(-1), + usageCount(1) + { + } + + ~AttributeType() + { + free(name); + } + + bool Init(const char* name, uint8 type) + { + this->name = strdup(name); + if (this->name == NULL) + return false; + + this->type = type; + return true; + } +}; + + +struct PackageWriter::AttributeTypeHashDefinition { + typedef AttributeTypeKey KeyType; + typedef AttributeType ValueType; + + size_t HashKey(const AttributeTypeKey& key) const + { + return hash_string(key.name) ^ (uint32)key.type; + } + + size_t Hash(const AttributeType* value) const + { + return hash_string(value->name) ^ (uint32)value->type; + } + + bool Compare(const AttributeTypeKey& key, const AttributeType* value) const + { + return strcmp(value->name, key.name) == 0 && value->type == key.type; + } + + AttributeType*& GetLink(AttributeType* value) const + { + return value->next; + } +}; + + +struct PackageWriter::Attribute : public DoublyLinkedListLinkImpl { + AttributeType* type; + AttributeValue value; + DoublyLinkedList children; + + Attribute(AttributeType* type) + : + type(type) + { + } + + ~Attribute() + { + DeleteChildren(); + } + + void AddChild(Attribute* child) + { + children.Add(child); + } + + void DeleteChildren() + { + while (Attribute* child = children.RemoveHead()) + delete child; + } +}; + + +struct PackageWriter::AttributeTypeUsageGreater { + bool operator()(const AttributeType* a, const AttributeType* b) + { + return a->usageCount > b->usageCount; + } +}; + + +// #pragma mark - PackageWriter + + +struct PackageWriter::Entry : DoublyLinkedListLinkImpl { + Entry(char* name, size_t nameLength, bool isImplicit) + : + fName(name), + fNameLength(nameLength), + fIsImplicit(isImplicit) + { +printf("%p->Entry::Entry(\"%s\", %lu, %d)\n", this, name, nameLength, isImplicit); + } + + ~Entry() + { + DeleteChildren(); + free(fName); + } + + static Entry* Create(const char* name, size_t nameLength, bool isImplicit) + { + char* clonedName = (char*)malloc(nameLength + 1); + if (clonedName == NULL) + throw std::bad_alloc(); + memcpy(clonedName, name, nameLength); + clonedName[nameLength] = '\0'; + + Entry* entry = new(std::nothrow) Entry(clonedName, nameLength, + isImplicit); + if (entry == NULL) { + free(clonedName); + throw std::bad_alloc(); + } + + return entry; + } + + const char* Name() const + { + return fName; + } + + bool IsImplicit() const + { + return fIsImplicit; + } + + void SetImplicit(bool isImplicit) + { + fIsImplicit = isImplicit; + } + + bool HasName(const char* name, size_t nameLength) + { + return nameLength == fNameLength + && strncmp(name, fName, nameLength) == 0; + } + + void AddChild(Entry* child) + { + fChildren.Add(child); + } + + void DeleteChildren() + { + while (Entry* child = fChildren.RemoveHead()) + delete child; + } + + Entry* GetChild(const char* name, size_t nameLength) const + { + EntryList::ConstIterator it = fChildren.GetIterator(); + while (Entry* child = it.Next()) { + if (child->HasName(name, nameLength)) + return child; + } + + return NULL; + } + + EntryList::ConstIterator ChildIterator() const + { + return fChildren.GetIterator(); + } + +private: + char* fName; + size_t fNameLength; + bool fIsImplicit; + EntryList fChildren; +}; + + +// #pragma mark - PackageWriter + + +template +inline PackageWriter::Attribute* +PackageWriter::_AddAttribute(const char* attributeName, Type value) +{ + AttributeValue attributeValue; + attributeValue.SetTo(value); + return _AddAttribute(attributeName, attributeValue); +} + + +PackageWriter::PackageWriter() + : + fFileName(NULL), + fFD(-1), + fFinished(false), + fDataBuffer(NULL), + fRootEntry(NULL), + fRootAttribute(NULL), + fTopAttribute(NULL), + fCachedStrings(NULL), + fAttributeTypes(NULL) +{ +} + + +PackageWriter::~PackageWriter() +{ + delete fRootAttribute; + + if (fAttributeTypes != NULL) { + AttributeType* attributeType = fAttributeTypes->Clear(true); + while (attributeType != NULL) { + AttributeType* next = attributeType->next; + delete attributeType; + attributeType = next; + } + } + + if (fCachedStrings != NULL) { + CachedString* cachedString = fCachedStrings->Clear(true); + while (cachedString != NULL) { + CachedString* next = cachedString->next; + delete cachedString; + cachedString = next; + } + } + + delete fRootEntry; + + free(fDataBuffer); + + if (fFD >= 0) + close(fFD); + + if (!fFinished && fFileName != NULL) + unlink(fFileName); +} + + +status_t +PackageWriter::Init(const char* fileName) +{ + try { + return _Init(fileName); + } catch (status_t error) { + return error; + } catch (std::bad_alloc) { + fprintf(stderr, "Out of memory!\n"); + return B_NO_MEMORY; + } +} + + +status_t +PackageWriter::AddEntry(const char* fileName) +{ + try { + return _RegisterEntry(fileName); + } catch (status_t error) { + return error; + } catch (std::bad_alloc) { + fprintf(stderr, "Out of memory!\n"); + return B_NO_MEMORY; + } +} + + +status_t +PackageWriter::Finish() +{ + try { + return _Finish(); + } catch (status_t error) { + return error; + } catch (std::bad_alloc) { + fprintf(stderr, "Out of memory!\n"); + return B_NO_MEMORY; + } +} + + +status_t +PackageWriter::_Init(const char* fileName) +{ + // create hash tables + fCachedStrings = new CachedStringTable; + if (fCachedStrings->Init() != B_OK) + throw std::bad_alloc(); + + fAttributeTypes = new AttributeTypeTable; + if (fAttributeTypes->Init() != B_OK) + throw std::bad_alloc(); + + // allocate data buffer + fDataBufferSize = 128 * 1024; + fDataBuffer = malloc(fDataBufferSize); + if (fDataBuffer == NULL) + throw std::bad_alloc(); + + // create entry list + fRootEntry = new Entry(NULL, 0, true); + + // open file + fFD = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fFD < 0) { + fprintf(stderr, "Error: Failed to open package file \"%s\": %s\n", + fileName, strerror(errno)); + return errno; + } + + fRootAttribute = new Attribute(NULL); + + fFileName = fileName; + fHeapOffset = fHeapEnd = sizeof(hpkg_header); + fTopAttribute = fRootAttribute; + + return B_OK; +} + + +status_t +PackageWriter::_Finish() +{ + // write entries + for (EntryList::ConstIterator it = fRootEntry->ChildIterator(); + Entry* entry = it.Next();) { + _AddEntry(AT_FDCWD, entry, entry->Name()); + } + +printf("header size: %lu\n", sizeof(hpkg_header)); +printf("heap size: %lld\n", fHeapEnd - sizeof(hpkg_header)); + + // write the attribute type abbreviations + off_t attributeTypesOffset = fHeapEnd; + _WriteAttributeTypes(); +printf("attributes types size: %lld\n", fHeapEnd - attributeTypesOffset); + + // write the cached strings + off_t cachedStringsOffset = fHeapEnd; + int32 cachedStringsWritten = _WriteCachedStrings(); +printf("cached strings size: %lld\n", fHeapEnd - cachedStringsOffset); + + // write the TOC + off_t tocOffset = fHeapEnd; + _WriteAttributeChildren(fRootAttribute); +printf("toc size: %lld\n", fHeapEnd - tocOffset); + + // write the package attributes + off_t packageAttributesOffset = fHeapEnd; + _Write(0); + // TODO: Write them for real! +printf("package attributes size: %lld\n", fHeapEnd - packageAttributesOffset); + + off_t totalSize = fHeapEnd; +printf("total size: %lld\n", totalSize); + + // prepare the header + + // general + hpkg_header header; + header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_MAGIC); + header.header_size = B_HOST_TO_BENDIAN_INT16( + (uint16)sizeof(hpkg_header)); + header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_VERSION); + header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize); + + // package attributes + header.attributes_compression = B_HOST_TO_BENDIAN_INT32( + B_HPKG_COMPRESSION_NONE); + header.attributes_length_compressed + = B_HOST_TO_BENDIAN_INT32(fHeapEnd - packageAttributesOffset); + header.attributes_length_uncompressed + = header.attributes_length_compressed; + // TODO: Support compression! + + // TOC + header.toc_compression = B_HOST_TO_BENDIAN_INT32(B_HPKG_COMPRESSION_NONE); + header.toc_length_compressed = B_HOST_TO_BENDIAN_INT64( + packageAttributesOffset - attributeTypesOffset); + header.toc_length_uncompressed = header.toc_length_compressed; + // TODO: Support compression! + + // TOC subsections + header.toc_attribute_types_length = B_HOST_TO_BENDIAN_INT64( + cachedStringsOffset - attributeTypesOffset); + header.toc_attribute_types_count = B_HOST_TO_BENDIAN_INT64( + fAttributeTypes->CountElements()); + header.toc_strings_length = B_HOST_TO_BENDIAN_INT64( + tocOffset - cachedStringsOffset); + header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten); + + // write the header + _WriteBuffer(&header, sizeof(hpkg_header), 0); + + fFinished = true; + return B_OK; +} + + +status_t +PackageWriter::_RegisterEntry(const char* fileName) +{ + if (*fileName == '\0') { + fprintf(stderr, "Error: Invalid empty file name\n"); + return B_BAD_VALUE; + } + + // add all components of the path + Entry* entry = fRootEntry; + while (*fileName != 0) { + const char* nextSlash = strchr(fileName, '/'); + // no slash, just add the file name + if (nextSlash == NULL) { + entry = _RegisterEntry(entry, fileName, strlen(fileName), false); + break; + } + + // find the start of the next component, skipping slashes + const char* nextComponent = nextSlash + 1; + while (*nextComponent == '/') + nextComponent++; + + if (nextSlash == fileName) { + // the FS root + entry = _RegisterEntry(entry, fileName, 1, + *nextComponent != '\0'); + } else { + entry = _RegisterEntry(entry, fileName, nextSlash - fileName, + *nextComponent != '\0'); + } + + fileName = nextComponent; + } + + return B_OK; +} + + +PackageWriter::Entry* +PackageWriter::_RegisterEntry(Entry* parent, const char* name, + size_t nameLength, bool isImplicit) +{ + // check the component name -- don't allow "." or ".." + if (*name == '.' + && (nameLength == 1 || (nameLength == 2 && name[2] == '.'))) { + fprintf(stderr, "Error: Invalid file name: \".\" and \"..\" " + "are not allowed as path components\n"); + throw status_t(B_BAD_VALUE); + } + + // the entry might already exist + Entry* entry = parent->GetChild(name, nameLength); + if (entry != NULL) { + // If the entry was implicit and is no longer, we mark it non-implicit + // and delete all of it's children. + if (entry->IsImplicit() && !isImplicit) { + entry->DeleteChildren(); + entry->SetImplicit(false); + } + } else { + // nope -- create it + entry = Entry::Create(name, nameLength, isImplicit); + parent->AddChild(entry); + } + + return entry; +} + + +void +PackageWriter::_WriteAttributeTypes() +{ + // create an array of the attribute types + int32 attributeTypeCount = fAttributeTypes->CountElements(); + AttributeType** attributeTypes = new AttributeType*[attributeTypeCount]; + ArrayDeleter attributeTypesDeleter(attributeTypes); + + int32 index = 0; + for (AttributeTypeTable::Iterator it = fAttributeTypes->GetIterator(); + AttributeType* type = it.Next();) { + attributeTypes[index++] = type; + } + + // sort it by descending usage count + std::sort(attributeTypes, attributeTypes + attributeTypeCount, + AttributeTypeUsageGreater()); + + // assign the indices and write entries to disk + for (int32 i = 0; i < attributeTypeCount; i++) { + AttributeType* attributeType = attributeTypes[i]; + attributeType->index = i; + + _Write(attributeType->type); + _WriteString(attributeType->name); + } + + // write a terminating 0 byte + _Write(0); +} + + +int32 +PackageWriter::_WriteCachedStrings() +{ + // create an array of the cached strings + int32 count = fCachedStrings->CountElements(); + CachedString** cachedStrings = new CachedString*[count]; + ArrayDeleter cachedStringsDeleter(cachedStrings); + + int32 index = 0; + for (CachedStringTable::Iterator it = fCachedStrings->GetIterator(); + CachedString* string = it.Next();) { + cachedStrings[index++] = string; + } + + // sort it by descending usage count + std::sort(cachedStrings, cachedStrings + count, + CachedStringUsageGreater()); + + // assign the indices and write entries to disk + int32 stringsWritten = 0; + for (int32 i = 0; i < count; i++) { + CachedString* cachedString = cachedStrings[i]; + + // strings that are used only once are better stored inline + if (cachedString->usageCount < 2) + break; + + cachedString->index = i; + + _WriteString(cachedString->string); + stringsWritten++; + } + + // write a terminating 0 byte + _Write(0); + + return stringsWritten; +} + + +void +PackageWriter::_WriteAttributeChildren(Attribute* attribute) +{ + DoublyLinkedList::Iterator it + = attribute->children.GetIterator(); + while (Attribute* child = it.Next()) { + AttributeType* type = child->type; + + // write tag + uint8 encoding = child->value.PreferredEncoding(); + _WriteUnsignedLEB128(B_HPKG_ATTRIBUTE_TAG_COMPOSE(type->index, + encoding, !child->children.IsEmpty())); + + // write value + _WriteAttributeValue(child->value, encoding); + + if (!child->children.IsEmpty()) + _WriteAttributeChildren(child); + } + + _WriteUnsignedLEB128(0); +} + + +void +PackageWriter::_WriteAttributeValue(const AttributeValue& value, uint8 encoding) +{ + switch (value.type) { + case B_HPKG_ATTRIBUTE_TYPE_INT: + case B_HPKG_ATTRIBUTE_TYPE_UINT: + { + uint64 intValue = value.type == B_HPKG_ATTRIBUTE_TYPE_INT + ? (uint64)value.signedInt : value.unsignedInt; + + switch (encoding) { + case B_HPKG_ATTRIBUTE_ENCODING_INT_8_BIT: + _Write((uint8)intValue); + break; + case B_HPKG_ATTRIBUTE_ENCODING_INT_16_BIT: + _Write( + B_HOST_TO_BENDIAN_INT16((uint16)intValue)); + break; + case B_HPKG_ATTRIBUTE_ENCODING_INT_32_BIT: + _Write( + B_HOST_TO_BENDIAN_INT32((uint32)intValue)); + break; + case B_HPKG_ATTRIBUTE_ENCODING_INT_64_BIT: + _Write( + B_HOST_TO_BENDIAN_INT64((uint64)intValue)); + break; + default: + { + fprintf(stderr, "_WriteAttributeValue(): invalid " + "encoding %d for int value type %d\n", encoding, + value.type); + throw status_t(B_BAD_VALUE); + } + } + + break; + } + + case B_HPKG_ATTRIBUTE_TYPE_STRING: + { + if (encoding == B_HPKG_ATTRIBUTE_ENCODING_STRING_TABLE) + _WriteUnsignedLEB128(value.string->index); + else + _WriteString(value.string->string); + break; + } + + case B_HPKG_ATTRIBUTE_TYPE_RAW: + { + _WriteUnsignedLEB128(value.data.size); + if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) + _WriteUnsignedLEB128(value.data.offset); + else + _WriteBuffer(value.data.raw, value.data.size); + break; + } + + default: + fprintf(stderr, "_WriteAttributeValue(): invalid value type: " + "%d\n", value.type); + throw status_t(B_BAD_VALUE); + } +} + + +void +PackageWriter::_WriteUnsignedLEB128(uint64 value) +{ + uint8 bytes[10]; + int32 count = 0; + do { + uint8 byte = value & 0x7f; + value >>= 7; + bytes[count++] = byte | (value != 0 ? 0x80 : 0); + } while (value != 0); + + _WriteBuffer(bytes, count); +} + + +void +PackageWriter::_WriteBuffer(const void* buffer, size_t size, off_t offset) +{ + ssize_t bytesWritten = pwrite(fFD, buffer, size, offset); + if (bytesWritten < 0) { + fprintf(stderr, "_WriteBuffer(%p, %lu) failed to write data: %s\n", + buffer, size, strerror(errno)); + throw status_t(errno); + } + if ((size_t)bytesWritten != size) { + fprintf(stderr, "_WriteBuffer(%p, %lu) failed to write all data\n", + buffer, size); + throw status_t(B_ERROR); + } +} + + +void +PackageWriter::_AddEntry(int dirFD, Entry* entry, const char* fileName) +{ +printf("PackageWriter::_AddEntry(%d, %p, \"%s\")\n", dirFD, entry, fileName); + bool isImplicitEntry = entry != NULL && entry->IsImplicit(); + + // open the node + int fd = openat(dirFD, fileName, + O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); + if (fd < 0) { + fprintf(stderr, "Error: Failed to open entry \"%s\": %s\n", + fileName, strerror(errno)); + throw status_t(errno); + } + FDCloser fdCloser(fd); + + // stat the node + struct stat st; + if (fstat(fd, &st) < 0) { + fprintf(stderr, "Error: Failed to fstat() file \"%s\": %s\n", + fileName, strerror(errno)); + throw status_t(errno); + } + + // implicit entries must be directories + if (isImplicitEntry && !S_ISDIR(st.st_mode)) { + fprintf(stderr, "Error: Non-leaf path component \"%s\" is not a " + "directory\n", fileName); + throw status_t(B_BAD_VALUE); + } + + // check/translate the node type + uint8 fileType; + uint32 defaultPermissions; + if (S_ISREG(st.st_mode)) { + fileType = B_HPKG_FILE_TYPE_FILE; + defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS; + } else if (S_ISLNK(st.st_mode)) { + fileType = B_HPKG_FILE_TYPE_SYMLINK; + defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS; + } else if (S_ISDIR(st.st_mode)) { + fileType = B_HPKG_FILE_TYPE_DIRECTORY; + defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS; + } else { + // unsupported node type + fprintf(stderr, "Error: Unsupported node type, entry: \"%s\"\n", + fileName); + throw status_t(B_UNSUPPORTED); + } + + // add attribute entry + Attribute* entryAttribute = _AddStringAttribute( + B_HPKG_ATTRIBUTE_NAME_DIRECTORY_ENTRY, fileName); + Stacker entryAttributeStacker(fTopAttribute, entryAttribute); + + // add stat data + if (fileType != B_HPKG_DEFAULT_FILE_TYPE) + _AddAttribute(B_HPKG_ATTRIBUTE_NAME_FILE_TYPE, fileType); + if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) { + _AddAttribute(B_HPKG_ATTRIBUTE_NAME_FILE_PERMISSIONS, + uint32(st.st_mode & ALLPERMS)); + } + _AddAttribute(B_HPKG_ATTRIBUTE_NAME_FILE_ATIME, uint32(st.st_atime)); + _AddAttribute(B_HPKG_ATTRIBUTE_NAME_FILE_MTIME, uint32(st.st_mtime)); + _AddAttribute(B_HPKG_ATTRIBUTE_NAME_FILE_CRTIME, uint32(st.st_crtime)); + // TODO: File user/group! + + // add file data/symlink path + if (S_ISREG(st.st_mode)) { + // regular file -- add data + if (st.st_size > 0) { + FileData data(fd); + status_t error = _AddData(data, st.st_size); + if (error != B_OK) + throw status_t(error); + } + } else if (S_ISLNK(st.st_mode)) { + // symlink -- add link address + char path[B_PATH_NAME_LENGTH + 1]; + ssize_t bytesRead = readlinkat(dirFD, fileName, path, + B_PATH_NAME_LENGTH); + if (bytesRead < 0) { + fprintf(stderr, "Error: Failed to read symlink \"%s\": %s\n", + fileName, strerror(errno)); + throw status_t(errno); + } + + path[bytesRead] = '\0'; + _AddStringAttribute(B_HPKG_ATTRIBUTE_NAME_SYMLINK_PATH, path); + } + + // add attributes + if (DIR* attrDir = fs_fopen_attr_dir(fd)) { + CObjectDeleter attrDirCloser(attrDir, fs_close_attr_dir); + + while (dirent* entry = readdir(attrDir)) { + attr_info attrInfo; + if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { + fprintf(stderr, "Error: Failed to stat attribute \"%s\" of " + "file \"%s\": %s\n", entry->d_name, fileName, + strerror(errno)); + throw status_t(errno); + } + + // create attribute entry + Attribute* attributeAttribute = _AddStringAttribute( + B_HPKG_ATTRIBUTE_NAME_FILE_ATTRIBUTE, entry->d_name); + Stacker attributeAttributeStacker(fTopAttribute, + attributeAttribute); + + // add type + _AddAttribute(B_HPKG_ATTRIBUTE_NAME_FILE_ATTRIBUTE_TYPE, + (uint32)attrInfo.type); + + // add data + AttributeData data(fd, entry->d_name, attrInfo.type); + status_t error = _AddData(data, attrInfo.size); + if (error != B_OK) + throw status_t(error); + } + } + + if (S_ISDIR(st.st_mode)) { + // directory -- recursively add children + if (isImplicitEntry) { + // this is an implicit entry -- just add it's children + for (EntryList::ConstIterator it = entry->ChildIterator(); + Entry* child = it.Next();) { + _AddEntry(fd, child, child->Name()); + } + } else { + // we need to clone the directory FD for fdopendir() + int clonedFD = dup(fd); + if (clonedFD < 0) { + fprintf(stderr, "Error: Failed to dup() directory FD: %s\n", + strerror(errno)); + throw status_t(errno); + } + + DIR* dir = fdopendir(clonedFD); + if (dir == NULL) { + fprintf(stderr, "Error: Failed to open directory \"%s\": %s\n", + fileName, strerror(errno)); + close(clonedFD); + throw status_t(errno); + } + CObjectDeleter dirCloser(dir, closedir); + + while (dirent* entry = readdir(dir)) { + // skip "." and ".." + if (strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) { + continue; + } + + _AddEntry(fd, NULL, entry->d_name); + } + } + } +} + + +PackageWriter::Attribute* +PackageWriter::_AddAttribute(const char* attributeName, + const AttributeValue& value) +{ + Attribute* attribute = new Attribute( + _GetAttributeType(attributeName, value.type)); + + attribute->value = value; + fTopAttribute->AddChild(attribute); + + return attribute; +} + + +PackageWriter::Attribute* +PackageWriter::_AddStringAttribute(const char* attributeName, const char* value) +{ + AttributeValue attributeValue; + attributeValue.SetTo(_GetCachedString(value)); + return _AddAttribute(attributeName, attributeValue); +} + + +PackageWriter::Attribute* +PackageWriter::_AddDataAttribute(const char* attributeName, uint64 dataSize, + uint64 dataOffset) +{ + AttributeValue attributeValue; + attributeValue.SetToData(dataSize, dataOffset); + return _AddAttribute(attributeName, attributeValue); +} + + +CachedString* +PackageWriter::_GetCachedString(const char* value) +{ + CachedString* string = fCachedStrings->Lookup(value); + if (string != NULL) { + string->usageCount++; + return string; + } + + string = new CachedString; + if (!string->Init(value)) { + delete string; + throw std::bad_alloc(); + } + + fCachedStrings->Insert(string); + return string; +} + + +PackageWriter::AttributeType* +PackageWriter::_GetAttributeType(const char* attributeName, uint8 type) +{ + AttributeType* attributeType = fAttributeTypes->Lookup( + AttributeTypeKey(attributeName, type)); + if (attributeType != NULL) { + attributeType->usageCount++; + return attributeType; + } + + attributeType = new AttributeType; + if (!attributeType->Init(attributeName, type)) { + delete attributeType; + throw std::bad_alloc(); + } + + fAttributeTypes->Insert(attributeType); + return attributeType; +} + + +status_t +PackageWriter::_AddData(Data& data, off_t size) +{ + uint64 dataOffset = fHeapEnd - fHeapOffset; + + // copy the data to the heap + off_t readOffset = 0; + off_t remainingSize = size; + while (remainingSize > 0) { + // read from FD + size_t toCopy = std::min(remainingSize, (off_t)fDataBufferSize); + ssize_t bytesRead = data.Read(fDataBuffer, toCopy, readOffset); + if (bytesRead < 0) { + fprintf(stderr, "Error: Failed to read data: %s\n", + strerror(errno)); + return errno; + } + if ((size_t)bytesRead != toCopy) { + fprintf(stderr, "Error: Failed to read all data\n"); + return B_ERROR; + } + + // write to heap + ssize_t bytesWritten = pwrite(fFD, fDataBuffer, bytesRead, + fHeapEnd); + if (bytesWritten < 0) { + fprintf(stderr, "Error: Failed to write data: %s\n", + strerror(errno)); + return errno; + } + if (bytesWritten != bytesRead) { + fprintf(stderr, "Error: Failed to write all data\n"); + return B_ERROR; + } + + remainingSize -= bytesRead; + readOffset += bytesRead; + fHeapEnd += bytesRead; + } + + // add data attribute + Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_NAME_DATA, + size, dataOffset); + Stacker attributeAttributeStacker(fTopAttribute, dataAttribute); + +// _AddAttribute(B_HPKG_ATTRIBUTE_NAME_DATA_SIZE, (uint64)size); + // uncompressed size -- not required for uncompressed data + + // TODO: Support inline data! + // TODO: Support compression! + + return B_OK; +} diff --git a/src/bin/package/PackageWriter.h b/src/bin/package/PackageWriter.h new file mode 100644 index 0000000000..2eb1b61169 --- /dev/null +++ b/src/bin/package/PackageWriter.h @@ -0,0 +1,129 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_WRITER_H +#define PACKAGE_WRITER_H + + +#include +#include + +#include "Strings.h" + + +class Data; + + +class PackageWriter { +public: + PackageWriter(); + ~PackageWriter(); + + status_t Init(const char* fileName); + status_t AddEntry(const char* fileName); + status_t Finish(); + +private: + struct AttributeValue; + struct AttributeTypeKey; + struct AttributeType; + struct AttributeTypeHashDefinition; + struct Attribute; + struct AttributeTypeUsageGreater; + struct Entry; + + typedef BOpenHashTable + AttributeTypeTable; + typedef DoublyLinkedList EntryList; + +private: + status_t _Init(const char* fileName); + status_t _Finish(); + + status_t _RegisterEntry(const char* fileName); + Entry* _RegisterEntry(Entry* parent, + const char* name, size_t nameLength, + bool isImplicit); + + void _WriteAttributeTypes(); + int32 _WriteCachedStrings(); + void _WriteAttributeChildren(Attribute* attribute); + + void _WriteAttributeValue( + const AttributeValue& value, + uint8 encoding); + void _WriteUnsignedLEB128(uint64 value); + inline void _WriteString(const char* string); + + template + inline void _Write(const Type& value); + + void _WriteBuffer(const void* buffer, size_t size, + off_t offset); + inline void _WriteBuffer(const void* buffer, size_t size); + + void _AddEntry(int dirFD, Entry* entry, + const char* fileName); + + Attribute* _AddAttribute(const char* attributeName, + const AttributeValue& value); + + template + inline Attribute* _AddAttribute(const char* attributeName, + Type value); + + Attribute* _AddStringAttribute(const char* attributeName, + const char* value); + Attribute* _AddDataAttribute(const char* attributeName, + uint64 dataSize, uint64 dataOffset); + + CachedString* _GetCachedString(const char* value); + AttributeType* _GetAttributeType(const char* attributeName, + uint8 type); + + status_t _AddData(Data& data, off_t size); + +private: + const char* fFileName; + int fFD; + bool fFinished; + off_t fHeapOffset; + off_t fHeapEnd; + void* fDataBuffer; + size_t fDataBufferSize; + + Entry* fRootEntry; + + Attribute* fRootAttribute; + Attribute* fTopAttribute; + + CachedStringTable* fCachedStrings; + AttributeTypeTable* fAttributeTypes; +}; + + +template +inline void +PackageWriter::_Write(const Type& value) +{ + _WriteBuffer(&value, sizeof(Type)); +} + + +inline void +PackageWriter::_WriteString(const char* string) +{ + _WriteBuffer(string, strlen(string) + 1); +} + + +inline void +PackageWriter::_WriteBuffer(const void* buffer, size_t size) +{ + _WriteBuffer(buffer, size, fHeapEnd); + fHeapEnd += size; +} + + +#endif // PACKAGE_WRITER_H diff --git a/src/bin/package/Stacker.h b/src/bin/package/Stacker.h new file mode 100644 index 0000000000..312c80184e --- /dev/null +++ b/src/bin/package/Stacker.h @@ -0,0 +1,40 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef STACKER_H +#define STACKER_H + + +template +class Stacker { +public: + Stacker(Type*& location, Type* element) + : + fLocation(&location), + fPreviousElement(location) + { + *fLocation = element; + } + + Stacker(Type** location, Type* element) + : + fLocation(location), + fPreviousElement(*location) + { + *fLocation = element; + } + + ~Stacker() + { + if (fLocation != NULL) + *fLocation = fPreviousElement; + } + +private: + Type** fLocation; + Type* fPreviousElement; +}; + + +#endif // STACKER_H diff --git a/src/bin/package/Strings.cpp b/src/bin/package/Strings.cpp new file mode 100644 index 0000000000..881d987f58 --- /dev/null +++ b/src/bin/package/Strings.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "Strings.h" + + +// from the Dragon Book: a slightly modified hashpjw() +uint32 +hash_string(const char* string) +{ + if (string == NULL) + return 0; + + uint32 h = 0; + + for (; *string; string++) { + uint32 g = h & 0xf0000000; + if (g) + h ^= g >> 24; + h = (h << 4) + *string; + } + + return h; +} diff --git a/src/bin/package/Strings.h b/src/bin/package/Strings.h new file mode 100644 index 0000000000..184cc69b1c --- /dev/null +++ b/src/bin/package/Strings.h @@ -0,0 +1,82 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef STRINGS_H +#define STRINGS_H + + +#include + + +uint32 hash_string(const char* string); + + +struct CachedString { + char* string; + int32 index; + uint32 usageCount; + CachedString* next; // hash table link + + CachedString() + : + string(NULL), + index(-1), + usageCount(1) + { + } + + ~CachedString() + { + free(string); + } + + bool Init(const char* string) + { + this->string = strdup(string); + if (this->string == NULL) + return false; + + return true; + } +}; + + +struct CachedStringHashDefinition { + typedef const char* KeyType; + typedef CachedString ValueType; + + size_t HashKey(const char* key) const + { + return hash_string(key); + } + + size_t Hash(const CachedString* value) const + { + return HashKey(value->string); + } + + bool Compare(const char* key, const CachedString* value) const + { + return strcmp(value->string, key) == 0; + } + + CachedString*& GetLink(CachedString* value) const + { + return value->next; + } +}; + + +typedef BOpenHashTable CachedStringTable; + + +struct CachedStringUsageGreater { + bool operator()(const CachedString* a, const CachedString* b) + { + return a->usageCount > b->usageCount; + } +}; + + +#endif // STRINGS_H diff --git a/src/bin/package/command_create.cpp b/src/bin/package/command_create.cpp new file mode 100644 index 0000000000..87089df691 --- /dev/null +++ b/src/bin/package/command_create.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "package.h" + +#include +#include +#include +#include +#include + +#include "package.h" +#include "PackageWriter.h" + + +int +command_create(int argc, const char* const* argv) +{ + const char* changeToDirectory = NULL; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+C:h", sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'C': + changeToDirectory = optarg; + break; + + case 'h': + print_usage_and_exit(false); + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // The remaining arguments are the package file and the list of files to + // include, i.e. at least two more arguments. + if (optind + 2 > argc) + print_usage_and_exit(true); + + const char* packageFileName = argv[optind++]; + const char* const* fileNames = argv + optind; + int fileNameCount = argc - optind; + + // create package + PackageWriter packageWriter; + status_t error = packageWriter.Init(packageFileName); +printf("Init(): %s\n", strerror(error)); + if (error != B_OK) + return 1; + + // change directory, if requested + if (changeToDirectory != NULL) { + if (chdir(changeToDirectory) != 0) { + fprintf(stderr, "Error: Failed to change the current working " + "directory to \"%s\": %s\n", changeToDirectory, + strerror(errno)); + } + } + + // add files + for (int i = 0; i < fileNameCount; i++) { + error = packageWriter.AddEntry(fileNames[i]); +printf("AddEntry(\"%s\"): %s\n", fileNames[i], strerror(error)); + if (error != B_OK) + return 1; + } + + // write the package + error = packageWriter.Finish(); +printf("Finish(): %s\n", strerror(error)); + if (error != B_OK) + return 1; + + return 0; +} diff --git a/src/bin/package/command_dump.cpp b/src/bin/package/command_dump.cpp new file mode 100644 index 0000000000..468e962f34 --- /dev/null +++ b/src/bin/package/command_dump.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "package.h" + +#include +#include +#include +#include +#include +#include + +#include "package.h" +#include "PackageEntry.h" +#include "PackageEntryAttribute.h" +#include "PackageReader.h" + + +struct PackageContentDumpHandler : LowLevelPackageContentHandler { + PackageContentDumpHandler() + : + fLevel(0), + fErrorOccurred(false), + fHasChildren(false) + { + } + + virtual status_t HandleAttribute(const char* attributeName, + const PackageAttributeValue& value, void* parentToken, void*& _token) + { + if (fErrorOccurred) + return B_OK; + + printf("%*s>%s: ", fLevel * 2, "", attributeName); + _PrintValue(value); + printf("\n"); + + fHasChildren = false; + fLevel++; + return B_OK; + } + + virtual status_t HandleAttributeDone(const char* attributeName, + const PackageAttributeValue& value, void* token) + { + if (fErrorOccurred) + return B_OK; + + fLevel--; + + if (fHasChildren) + printf("%*s<%s\n", fLevel * 2, "", attributeName); + + fHasChildren = true; + return B_OK; + } + + virtual void HandleErrorOccurred() + { + fErrorOccurred = true; + } + +private: + void _PrintValue(const PackageAttributeValue& value) + { + switch (value.type) { + case B_HPKG_ATTRIBUTE_TYPE_INT: + printf("%lld (%#llx)", value.signedInt, value.signedInt); + break; + case B_HPKG_ATTRIBUTE_TYPE_UINT: + printf("%llu (%#llx)", value.unsignedInt, value.unsignedInt); + break; + case B_HPKG_ATTRIBUTE_TYPE_STRING: + printf("\"%s\"", value.string); + break; + case B_HPKG_ATTRIBUTE_TYPE_RAW: + switch (value.encoding) { + case B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE: + printf("data: size: %llu, inline", value.data.size); + // TODO: Print the data bytes! + break; + case B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP: + printf("data: size: %llu, offset: %llu", + value.data.size, value.data.offset); + break; + default: + break; + } + break; + default: + printf("\n", value.type); + break; + } + } + +private: + int fLevel; + bool fErrorOccurred; + bool fHasChildren; +}; + + +int +command_dump(int argc, const char* const* argv) +{ + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage_and_exit(false); + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // One argument should remain -- the package file name. + if (optind + 1 != argc) + print_usage_and_exit(true); + + const char* packageFileName = argv[optind++]; + + // open package + PackageReader packageReader; + status_t error = packageReader.Init(packageFileName); +printf("Init(): %s\n", strerror(error)); + if (error != B_OK) + return 1; + + // list + PackageContentDumpHandler handler; + error = packageReader.ParseContent(&handler); +printf("ParseContent(): %s\n", strerror(error)); + if (error != B_OK) + return 1; + + return 0; +} diff --git a/src/bin/package/command_extract.cpp b/src/bin/package/command_extract.cpp new file mode 100644 index 0000000000..5684f4a929 --- /dev/null +++ b/src/bin/package/command_extract.cpp @@ -0,0 +1,358 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "package.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "FDCloser.h" +#include "package.h" +#include "PackageDataReader.h" +#include "PackageEntry.h" +#include "PackageEntryAttribute.h" +#include "PackageReader.h" + + +struct PackageContentExtractHandler : PackageContentHandler { + PackageContentExtractHandler(int packageFileFD) + : + fPackageFileReader(packageFileFD), + fDataBuffer(NULL), + fDataBufferSize(0), + fErrorOccurred(false) + { + } + + ~PackageContentExtractHandler() + { + free(fDataBuffer); + } + + status_t Init() + { + fDataBufferSize = 64 * 1024; + fDataBuffer = malloc(fDataBufferSize); + if (fDataBuffer == NULL) + return B_NO_MEMORY; + + return B_OK; + } + + virtual status_t HandleEntry(PackageEntry* entry) + { + // create a token + Token* token = new(std::nothrow) Token; + if (token == NULL) + return B_NO_MEMORY; + ObjectDeleter tokenDeleter(token); + + // get parent FD + int parentFD = AT_FDCWD; + if (entry->Parent() != NULL) + parentFD = ((Token*)entry->Parent()->UserToken())->fd; + + // check whether something is in the way + struct stat st; + bool entryExists = fstatat(parentFD, entry->Name(), &st, + AT_SYMLINK_NOFOLLOW) == 0; + if (entryExists) { + if (S_ISREG(entry->Mode()) || S_ISLNK(entry->Mode())) { + // If the entry in the way is a regular file or a symlink, + // remove it, otherwise fail. + if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { + fprintf(stderr, "Error: Can't create entry \"%s\", since " + "something is in the way\n", entry->Name()); + return B_FILE_EXISTS; + } + + if (unlinkat(parentFD, entry->Name(), 0) != 0) { + fprintf(stderr, "Error: Failed to unlink entry \"%s\": %s\n", + entry->Name(), strerror(errno)); + return errno; + } + + entryExists = false; + } else if (S_ISDIR(entry->Mode())) { + // If the entry in the way is a directory, merge, otherwise + // fail. + if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "Error: Can't create directory \"%s\", " + "since something is in the way\n", entry->Name()); + return B_FILE_EXISTS; + } + } + } + + // create the entry + int fd = -1; + if (S_ISREG(entry->Mode())) { + // create the file + fd = openat(parentFD, entry->Name(), O_RDWR | O_CREAT | O_EXCL, + entry->Mode() & ALLPERMS); + if (fd < 0) { + fprintf(stderr, "Error: Failed to create file \"%s\": %s\n", + entry->Name(), strerror(errno)); + return errno; + } + + // write data + status_t error; + const PackageData& data = entry->Data(); + if (data.IsEncodedInline()) { + BufferDataReader dataReader(data.InlineData(), + data.CompressedSize()); + error = _ExtractFileData(&dataReader, data, fd); + } else + error = _ExtractFileData(&fPackageFileReader, data, fd); + + if (error != B_OK) + return error; + } else if (S_ISLNK(entry->Mode())) { + // create the symlink + const char* symlinkPath = entry->SymlinkPath(); + if (symlinkat(symlinkPath != NULL ? symlinkPath : "", parentFD, + entry->Name()) != 0) { + fprintf(stderr, "Error: Failed to create symlink \"%s\": %s\n", + entry->Name(), strerror(errno)); + return errno; + } +// TODO: Set symlink permissions? + } else if (S_ISDIR(entry->Mode())) { + // create the directory, if necessary + if (!entryExists + && mkdirat(parentFD, entry->Name(), entry->Mode() & ALLPERMS) + != 0) { + fprintf(stderr, "Error: Failed to create directory \"%s\": " + "%s\n", entry->Name(), strerror(errno)); + return errno; + } + } else { + fprintf(stderr, "Error: Invalid file type for entry \"%s\"\n", + entry->Name()); + return B_BAD_DATA; + } + + // If not done yet (symlink, dir), open the node -- we need the FD. + if (fd < 0) { + fd = openat(parentFD, entry->Name(), O_RDONLY | O_NOTRAVERSE); + if (fd < 0) { + fprintf(stderr, "Error: Failed to open entry \"%s\": %s\n", + entry->Name(), strerror(errno)); + return errno; + } + } + token->fd = fd; + + // set the file times + if (!entryExists) { + timespec times[2] = {entry->AccessTime(), entry->ModifiedTime()}; + futimens(fd, times); + + // set user/group + // TODO:... + } + + entry->SetUserToken(tokenDeleter.Detach()); + return B_OK; + } + + virtual status_t HandleEntryAttribute(PackageEntry* entry, + PackageEntryAttribute* attribute) + { + int entryFD = ((Token*)entry->UserToken())->fd; + + // create the attribute + int fd = fs_fopen_attr(entryFD, attribute->Name(), attribute->Type(), + O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + fprintf(stderr, "Error: Failed to create attribute \"%s\" of " + "file \"%s\": %s\n", attribute->Name(), entry->Name(), + strerror(errno)); + return errno; + } + FDCloser fdCloser(fd); + + // write data + status_t error; + const PackageData& data = attribute->Data(); + if (data.IsEncodedInline()) { + BufferDataReader dataReader(data.InlineData(), + data.CompressedSize()); + error = _ExtractFileData(&dataReader, data, fd); + } else + error = _ExtractFileData(&fPackageFileReader, data, fd); + + if (error != B_OK) + return error; + + return B_OK; + } + + virtual status_t HandleEntryDone(PackageEntry* entry) + { + if (Token* token = (Token*)entry->UserToken()) { + delete token; + entry->SetUserToken(NULL); + } + + return B_OK; + } + + virtual void HandleErrorOccurred() + { + fErrorOccurred = true; + } + +private: + struct Token { + int fd; + + Token() + : + fd(-1) + { + } + + ~Token() + { + if (fd >= 0) + close(fd); + } + }; + +private: + status_t _ExtractFileData(DataReader* dataReader, const PackageData& data, + int fd) + { + // create a PackageDataReader + PackageDataReader* reader; + status_t error = PackageDataReaderFactory::CreatePackageDataReader( + dataReader, data, reader); + if (error != B_OK) + return error; + ObjectDeleter readerDeleter(reader); + + // write the data + off_t bytesRemaining = data.UncompressedSize(); + off_t offset = 0; + while (bytesRemaining > 0) { + // read + size_t toCopy = std::min((off_t)fDataBufferSize, bytesRemaining); + error = reader->ReadData(offset, fDataBuffer, toCopy); + if (error != B_OK) { + fprintf(stderr, "Error: Failed to read data: %s\n", + strerror(errno)); + return error; + } + + // write + ssize_t bytesWritten = pwrite(fd, fDataBuffer, toCopy, offset); + if (bytesWritten < 0) { + fprintf(stderr, "Error: Failed to write data: %s\n", + strerror(errno)); + return errno; + } + if ((size_t)bytesWritten != toCopy) { + fprintf(stderr, "Error: Failed to write all data\n"); + return B_ERROR; + } + + offset += toCopy; + bytesRemaining -= toCopy; + } + + return B_OK; + } + +private: + FDDataReader fPackageFileReader; + void* fDataBuffer; + size_t fDataBufferSize; + bool fErrorOccurred; +}; + + +int +command_extract(int argc, const char* const* argv) +{ + const char* changeToDirectory = NULL; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+C:h", sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'C': + changeToDirectory = optarg; + break; + + case 'h': + print_usage_and_exit(false); + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // One argument should remain -- the package file name. + if (optind + 1 != argc) + print_usage_and_exit(true); + + const char* packageFileName = argv[optind++]; + + // open package + PackageReader packageReader; + status_t error = packageReader.Init(packageFileName); +printf("Init(): %s\n", strerror(error)); + if (error != B_OK) + return 1; + + // change directory, if requested + if (changeToDirectory != NULL) { + if (chdir(changeToDirectory) != 0) { + fprintf(stderr, "Error: Failed to change the current working " + "directory to \"%s\": %s\n", changeToDirectory, + strerror(errno)); + } + } + + // extract + PackageContentExtractHandler handler(packageReader.PackageFileFD()); + error = handler.Init(); + if (error != B_OK) + return 1; + + error = packageReader.ParseContent(&handler); +printf("ParseContent(): %s\n", strerror(error)); + if (error != B_OK) + return 1; + + return 0; +} diff --git a/src/bin/package/command_list.cpp b/src/bin/package/command_list.cpp new file mode 100644 index 0000000000..ce56d124ae --- /dev/null +++ b/src/bin/package/command_list.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "package.h" + +#include +#include +#include +#include +#include +#include + +#include "package.h" +#include "PackageEntry.h" +#include "PackageEntryAttribute.h" +#include "PackageReader.h" + + +struct PackageContentListHandler : PackageContentHandler { + PackageContentListHandler(bool listAttributes) + : + fLevel(0), + fListAttribute(listAttributes) + { + } + + virtual status_t HandleEntry(PackageEntry* entry) + { + fLevel++; + + int indentation = (fLevel - 1) * 2; + printf("%*s", indentation, ""); + + // name and size + printf("%-*s", indentation < 32 ? 32 - indentation : 0, entry->Name()); + printf(" %8llu", entry->Data().UncompressedSize()); + + // time + struct tm* time = localtime(&entry->ModifiedTime().tv_sec); + printf(" %04d-%02d-%02d %02d:%02d:%02d", + 1900 + time->tm_year, time->tm_mon + 1, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); + + // file type + mode_t mode = entry->Mode(); + if (S_ISREG(mode)) + printf(" -"); + else if (S_ISDIR(mode)) + printf(" d"); + else if (S_ISLNK(mode)) + printf(" l"); + else + printf(" ?"); + + // permissions + char buffer[4]; + printf("%s", _PermissionString(buffer, mode >> 6, + (mode & S_ISUID) != 0)); + printf("%s", _PermissionString(buffer, mode >> 3, + (mode & S_ISGID) != 0)); + printf("%s", _PermissionString(buffer, mode, false)); + + // print the symlink path + if (S_ISLNK(mode)) + printf(" -> %s", entry->SymlinkPath()); + + printf("\n"); + return B_OK; + } + + virtual status_t HandleEntryAttribute(PackageEntry* entry, + PackageEntryAttribute* attribute) + { + if (!fListAttribute) + return B_OK; + + int indentation = fLevel * 2; + printf("%*s<", indentation, ""); + printf("%-*s %8llu", indentation < 31 ? 31 - indentation : 0, + attribute->Name(), attribute->Data().UncompressedSize()); + + uint32 type = attribute->Type(); + if (isprint(type & 0xff) && isprint((type >> 8) & 0xff) + && isprint((type >> 16) & 0xff) && isprint(type >> 24)) { + printf(" '%c%c%c%c'", int(type >> 24), int((type >> 16) & 0xff), + int((type >> 8) & 0xff), int(type & 0xff)); + } else + printf(" %#lx", type); + + printf(">\n"); + return B_OK; + } + + virtual status_t HandleEntryDone(PackageEntry* entry) + { + fLevel--; + return B_OK; + } + + virtual void HandleErrorOccurred() + { + } + +private: + static const char* _PermissionString(char* buffer, uint32 mode, bool sticky) + { + buffer[0] = (mode & 0x4) != 0 ? 'r' : '-'; + buffer[1] = (mode & 0x2) != 0 ? 'w' : '-'; + + if ((mode & 0x1) != 0) + buffer[2] = sticky ? 's' : 'x'; + else + buffer[2] = '-'; + + buffer[3] = '\0'; + return buffer; + } + +private: + int fLevel; + bool fListAttribute; +}; + + +int +command_list(int argc, const char* const* argv) +{ + bool listAttributes = false; + + while (true) { + static struct option sLongOptions[] = { + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+ha", sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'a': + listAttributes = true; + break; + + case 'h': + print_usage_and_exit(false); + break; + + default: + print_usage_and_exit(true); + break; + } + } + + // One argument should remain -- the package file name. + if (optind + 1 != argc) + print_usage_and_exit(true); + + const char* packageFileName = argv[optind++]; + + // open package + PackageReader packageReader; + status_t error = packageReader.Init(packageFileName); +printf("Init(): %s\n", strerror(error)); + if (error != B_OK) + return 1; + + // list + PackageContentListHandler handler(listAttributes); + error = packageReader.ParseContent(&handler); +printf("ParseContent(): %s\n", strerror(error)); + if (error != B_OK) + return 1; + + return 0; +} diff --git a/src/bin/package/package.cpp b/src/bin/package/package.cpp new file mode 100644 index 0000000000..0e3c711564 --- /dev/null +++ b/src/bin/package/package.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "package.h" + +#include +#include +#include +#include +#include + +#include "PackageWriter.h" + + +extern const char* __progname; +const char* kCommandName = __progname; + + +static const char* kUsage = + "Usage: %s \n" + "Creates, inspects, or extracts a Haiku package.\n" + "\n" + "Commands:\n" + " create [ ] ...\n" + " Creates package file from a list of files.\n" + "\n" + " -C - Change to directory before interpreting the file\n" + " names .\n" + "\n" + " dump [ ] \n" + " Dumps the TOC section of package file . For debugging only.\n" + "\n" + " extract [ ] \n" + " Extracts the contents of package file .\n" + "\n" + " -C - Change to directory before extracting the " + "contents\n" + " of the archive.\n" + "\n" + " list [ ] \n" + " Lists the contents of package file .\n" + "\n" + " -a - Also list the file attributes.\n" + "\n" + "Common Options:\n" + " -h, --help - Print this usage info.\n" +; + + +void +print_usage_and_exit(bool error) +{ + fprintf(error ? stderr : stdout, kUsage, kCommandName); + exit(error ? 1 : 0); +} + + +int +main(int argc, const char* const* argv) +{ + if (argc < 2) + print_usage_and_exit(true); + + const char* command = argv[1]; + if (strcmp(command, "create") == 0) + return command_create(argc - 1, argv + 1); + + if (strcmp(command, "dump") == 0) + return command_dump(argc - 1, argv + 1); + + if (strcmp(command, "extract") == 0) + return command_extract(argc - 1, argv + 1); + + if (strcmp(command, "list") == 0) + return command_list(argc - 1, argv + 1); + + if (strcmp(command, "help") == 0) + print_usage_and_exit(false); + else + print_usage_and_exit(true); + + // never gets here + return 0; +} diff --git a/src/bin/package/package.h b/src/bin/package/package.h new file mode 100644 index 0000000000..b1b5d9a6fb --- /dev/null +++ b/src/bin/package/package.h @@ -0,0 +1,17 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef PACKAGE_H +#define PACKAGE_H + + +void print_usage_and_exit(bool error); + +int command_create(int argc, const char* const* argv); +int command_dump(int argc, const char* const* argv); +int command_extract(int argc, const char* const* argv); +int command_list(int argc, const char* const* argv); + + +#endif // PACKAGE_H