#include <package/hpkg/v1/PackageReaderImpl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#include <new>
#include <ByteOrder.h>
#include <package/hpkg/v1/HPKGDefsPrivate.h>
#include <package/hpkg/ErrorOutput.h>
#include <package/hpkg/v1/PackageData.h>
#include <package/hpkg/v1/PackageEntry.h>
#include <package/hpkg/v1/PackageEntryAttribute.h>
namespace BPackageKit {
namespace BHPKG {
namespace V1 {
namespace BPrivate {
#define TRACE(format...) do {} while (false)
static const size_t kMaxTOCSize = 64 * 1024 * 1024;
static const size_t kMaxPackageAttributesSize = 1 * 1024 * 1024;
struct PackageReaderImpl::DataAttributeHandler : AttributeHandler {
DataAttributeHandler(BPackageData* data)
:
fData(data)
{
}
static status_t InitData(AttributeHandlerContext* context,
BPackageData* 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,
BPackageData* 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 HandleAttribute(AttributeHandlerContext* context,
uint8 id, const AttributeValue& value, AttributeHandler** _handler)
{
switch (id) {
case B_HPKG_ATTRIBUTE_ID_DATA_SIZE:
fData->SetUncompressedSize(value.unsignedInt);
return B_OK;
case B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION:
{
switch (value.unsignedInt) {
case B_HPKG_COMPRESSION_NONE:
case B_HPKG_COMPRESSION_ZLIB:
break;
default:
context->errorOutput->PrintError("Error: Invalid "
"compression type for data (%llu)\n",
value.unsignedInt);
return B_BAD_DATA;
}
fData->SetCompression(value.unsignedInt);
return B_OK;
}
case B_HPKG_ATTRIBUTE_ID_DATA_CHUNK_SIZE:
fData->SetChunkSize(value.unsignedInt);
return B_OK;
}
return AttributeHandler::HandleAttribute(context, id, value, _handler);
}
private:
BPackageData* fData;
};
struct PackageReaderImpl::AttributeAttributeHandler : AttributeHandler {
AttributeAttributeHandler(BPackageEntry* entry, const char* name)
:
fEntry(entry),
fAttribute(name)
{
}
virtual status_t HandleAttribute(AttributeHandlerContext* context,
uint8 id, const AttributeValue& value, AttributeHandler** _handler)
{
switch (id) {
case B_HPKG_ATTRIBUTE_ID_DATA:
if (_handler != NULL) {
return DataAttributeHandler::Create(context,
&fAttribute.Data(), value, *_handler);
}
return DataAttributeHandler::InitData(context,
&fAttribute.Data(), value);
case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE:
fAttribute.SetType(value.unsignedInt);
return B_OK;
}
return AttributeHandler::HandleAttribute(context, id, value, _handler);
}
virtual status_t Delete(AttributeHandlerContext* context)
{
status_t error = context->packageContentHandler->HandleEntryAttribute(
fEntry, &fAttribute);
delete this;
return error;
}
private:
BPackageEntry* fEntry;
BPackageEntryAttribute fAttribute;
};
struct PackageReaderImpl::EntryAttributeHandler : AttributeHandler {
EntryAttributeHandler(AttributeHandlerContext* context,
BPackageEntry* parentEntry, const char* name)
:
fEntry(parentEntry, name),
fNotified(false)
{
_SetFileType(context, B_HPKG_DEFAULT_FILE_TYPE);
}
static status_t Create(AttributeHandlerContext* context,
BPackageEntry* parentEntry, const char* name,
AttributeHandler*& _handler)
{
if (name[0] == '\0' || strcmp(name, ".") == 0
|| strcmp(name, "..") == 0 || strchr(name, '/') != NULL) {
context->errorOutput->PrintError("Error: Invalid package: Invalid "
"entry name: \"%s\"\n", name);
return B_BAD_DATA;
}
EntryAttributeHandler* handler = new(std::nothrow)
EntryAttributeHandler(context, parentEntry, name);
if (handler == NULL)
return B_NO_MEMORY;
_handler = handler;
return B_OK;
}
virtual status_t HandleAttribute(AttributeHandlerContext* context,
uint8 id, const AttributeValue& value, AttributeHandler** _handler)
{
switch (id) {
case B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY:
{
status_t error = _Notify(context);
if (error != B_OK)
return error;
if (_handler != NULL) {
return EntryAttributeHandler::Create(context, &fEntry,
value.string, *_handler);
}
return B_OK;
}
case B_HPKG_ATTRIBUTE_ID_FILE_TYPE:
return _SetFileType(context, value.unsignedInt);
case B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS:
fEntry.SetPermissions(value.unsignedInt);
return B_OK;
case B_HPKG_ATTRIBUTE_ID_FILE_USER:
case B_HPKG_ATTRIBUTE_ID_FILE_GROUP:
break;
case B_HPKG_ATTRIBUTE_ID_FILE_ATIME:
fEntry.SetAccessTime(value.unsignedInt);
return B_OK;
case B_HPKG_ATTRIBUTE_ID_FILE_MTIME:
fEntry.SetModifiedTime(value.unsignedInt);
return B_OK;
case B_HPKG_ATTRIBUTE_ID_FILE_CRTIME:
fEntry.SetCreationTime(value.unsignedInt);
return B_OK;
case B_HPKG_ATTRIBUTE_ID_FILE_ATIME_NANOS:
fEntry.SetAccessTimeNanos(value.unsignedInt);
return B_OK;
case B_HPKG_ATTRIBUTE_ID_FILE_MTIME_NANOS:
fEntry.SetModifiedTimeNanos(value.unsignedInt);
return B_OK;
case B_HPKG_ATTRIBUTE_ID_FILE_CRTIM_NANOS:
fEntry.SetCreationTimeNanos(value.unsignedInt);
return B_OK;
case B_HPKG_ATTRIBUTE_ID_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 {
BPackageEntryAttribute attribute(value.string);
return context->packageContentHandler->HandleEntryAttribute(
&fEntry, &attribute);
}
}
case B_HPKG_ATTRIBUTE_ID_DATA:
if (_handler != NULL) {
return DataAttributeHandler::Create(context, &fEntry.Data(),
value, *_handler);
}
return DataAttributeHandler::InitData(context, &fEntry.Data(),
value);
case B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH:
fEntry.SetSymlinkPath(value.string);
return B_OK;
}
return AttributeHandler::HandleAttribute(context, id, value, _handler);
}
virtual status_t Delete(AttributeHandlerContext* context)
{
status_t error = _Notify(context);
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(AttributeHandlerContext* context, 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:
context->errorOutput->PrintError("Error: Invalid file type for "
"package entry (%llu)\n", fileType);
return B_BAD_DATA;
}
return B_OK;
}
private:
BPackageEntry fEntry;
bool fNotified;
};
struct PackageReaderImpl::RootAttributeHandler : PackageAttributeHandler {
typedef PackageAttributeHandler inherited;
virtual status_t HandleAttribute(AttributeHandlerContext* context,
uint8 id, const AttributeValue& value, AttributeHandler** _handler)
{
if (id == B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY) {
if (_handler != NULL) {
return EntryAttributeHandler::Create(context, NULL,
value.string, *_handler);
}
return B_OK;
}
return inherited::HandleAttribute(context, id, value, _handler);
}
};
PackageReaderImpl::PackageReaderImpl(BErrorOutput* errorOutput)
:
inherited(errorOutput),
fTOCSection("TOC")
{
}
PackageReaderImpl::~PackageReaderImpl()
{
}
status_t
PackageReaderImpl::Init(const char* fileName)
{
int fd = open(fileName, O_RDONLY);
if (fd < 0) {
ErrorOutput()->PrintError("Error: Failed to open package file \"%s\": "
"%s\n", fileName, strerror(errno));
return errno;
}
return Init(fd, true);
}
status_t
PackageReaderImpl::Init(int fd, bool keepFD)
{
status_t error = inherited::Init(fd, keepFD);
if (error != B_OK)
return error;
struct stat st;
if (fstat(FD(), &st) < 0) {
ErrorOutput()->PrintError("Error: Failed to access package file: %s\n",
strerror(errno));
return errno;
}
hpkg_header header;
if ((error = ReadBuffer(0, &header, sizeof(header))) != B_OK)
return error;
if (B_BENDIAN_TO_HOST_INT32(header.magic) != B_HPKG_MAGIC) {
ErrorOutput()->PrintError("Error: Invalid package file: Invalid "
"magic\n");
return B_BAD_DATA;
}
if (B_BENDIAN_TO_HOST_INT16(header.version) != B_HPKG_VERSION) {
ErrorOutput()->PrintError("Error: Invalid/unsupported package file "
"version (%d)\n", B_BENDIAN_TO_HOST_INT16(header.version));
return B_MISMATCHED_VALUES;
}
fHeapOffset = B_BENDIAN_TO_HOST_INT16(header.header_size);
if ((size_t)fHeapOffset < sizeof(hpkg_header)) {
ErrorOutput()->PrintError("Error: Invalid package file: Invalid header "
"size (%llu)\n", fHeapOffset);
return B_BAD_DATA;
}
fTotalSize = B_BENDIAN_TO_HOST_INT64(header.total_size);
if (fTotalSize != (uint64)st.st_size) {
ErrorOutput()->PrintError("Error: Invalid package file: Total size in "
"header (%llu) doesn't agree with total file size (%lld)\n",
fTotalSize, st.st_size);
return B_BAD_DATA;
}
fPackageAttributesSection.compression
= B_BENDIAN_TO_HOST_INT32(header.attributes_compression);
fPackageAttributesSection.compressedLength
= B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed);
fPackageAttributesSection.uncompressedLength
= B_BENDIAN_TO_HOST_INT32(header.attributes_length_uncompressed);
fPackageAttributesSection.stringsLength
= B_BENDIAN_TO_HOST_INT32(header.attributes_strings_length);
fPackageAttributesSection.stringsCount
= B_BENDIAN_TO_HOST_INT32(header.attributes_strings_count);
if (const char* errorString = CheckCompression(
fPackageAttributesSection)) {
ErrorOutput()->PrintError("Error: Invalid package file: package "
"attributes section: %s\n", errorString);
return B_BAD_DATA;
}
fTOCSection.compression = B_BENDIAN_TO_HOST_INT32(header.toc_compression);
fTOCSection.compressedLength
= B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed);
fTOCSection.uncompressedLength
= B_BENDIAN_TO_HOST_INT64(header.toc_length_uncompressed);
if (const char* errorString = CheckCompression(fTOCSection)) {
ErrorOutput()->PrintError("Error: Invalid package file: TOC section: "
"%s\n", errorString);
return B_BAD_DATA;
}
fTOCSection.stringsLength
= B_BENDIAN_TO_HOST_INT64(header.toc_strings_length);
fTOCSection.stringsCount
= B_BENDIAN_TO_HOST_INT64(header.toc_strings_count);
if (fTOCSection.stringsLength > fTOCSection.uncompressedLength
|| fTOCSection.stringsCount > fTOCSection.stringsLength) {
ErrorOutput()->PrintError("Error: Invalid package file: Invalid TOC "
"subsections description\n");
return B_BAD_DATA;
}
if (fPackageAttributesSection.compressedLength > fTotalSize
|| fTOCSection.compressedLength
> fTotalSize - fPackageAttributesSection.compressedLength
|| fHeapOffset
> fTotalSize - fPackageAttributesSection.compressedLength
- fTOCSection.compressedLength) {
ErrorOutput()->PrintError("Error: Invalid package file: The sum of the "
"sections sizes is greater than the package size\n");
return B_BAD_DATA;
}
fPackageAttributesSection.offset
= fTotalSize - fPackageAttributesSection.compressedLength;
fTOCSection.offset = fPackageAttributesSection.offset
- fTOCSection.compressedLength;
fHeapSize = fTOCSection.offset - fHeapOffset;
if (fTOCSection.uncompressedLength > kMaxTOCSize) {
ErrorOutput()->PrintError("Error: Package file TOC section size "
"is %llu bytes. This is beyond the reader's sanity limit\n",
fTOCSection.uncompressedLength);
return B_UNSUPPORTED;
}
if (fPackageAttributesSection.uncompressedLength
> kMaxPackageAttributesSize) {
ErrorOutput()->PrintError(
"Error: Package file package attributes section size "
"is %llu bytes. This is beyond the reader's sanity limit\n",
fPackageAttributesSection.uncompressedLength);
return B_UNSUPPORTED;
}
fTOCSection.data
= new(std::nothrow) uint8[fTOCSection.uncompressedLength];
if (fTOCSection.data == NULL) {
ErrorOutput()->PrintError("Error: Out of memory!\n");
return B_NO_MEMORY;
}
error = ReadCompressedBuffer(fTOCSection);
if (error != B_OK)
return error;
fPackageAttributesSection.data
= new(std::nothrow) uint8[fPackageAttributesSection.uncompressedLength];
if (fPackageAttributesSection.data == NULL) {
ErrorOutput()->PrintError("Error: Out of memory!\n");
return B_NO_MEMORY;
}
error = ReadCompressedBuffer(fPackageAttributesSection);
if (error != B_OK)
return error;
fTOCSection.currentOffset = 0;
SetCurrentSection(&fTOCSection);
error = ParseStrings();
if (error != B_OK)
return error;
fPackageAttributesSection.currentOffset = 0;
SetCurrentSection(&fPackageAttributesSection);
error = ParseStrings();
if (error != B_OK)
return error;
SetCurrentSection(NULL);
return B_OK;
}
status_t
PackageReaderImpl::ParseContent(BPackageContentHandler* contentHandler)
{
AttributeHandlerContext context(ErrorOutput(), contentHandler,
B_HPKG_SECTION_PACKAGE_ATTRIBUTES);
RootAttributeHandler rootAttributeHandler;
status_t error
= ParsePackageAttributesSection(&context, &rootAttributeHandler);
if (error == B_OK) {
context.section = B_HPKG_SECTION_PACKAGE_TOC;
error = _ParseTOC(&context, &rootAttributeHandler);
}
return error;
}
status_t
PackageReaderImpl::ParseContent(BLowLevelPackageContentHandler* contentHandler)
{
AttributeHandlerContext context(ErrorOutput(), contentHandler,
B_HPKG_SECTION_PACKAGE_ATTRIBUTES);
LowLevelAttributeHandler rootAttributeHandler;
status_t error
= ParsePackageAttributesSection(&context, &rootAttributeHandler);
if (error == B_OK) {
context.section = B_HPKG_SECTION_PACKAGE_TOC;
error = _ParseTOC(&context, &rootAttributeHandler);
}
return error;
}
status_t
PackageReaderImpl::_ParseTOC(AttributeHandlerContext* context,
AttributeHandler* rootAttributeHandler)
{
fTOCSection.currentOffset = fTOCSection.stringsLength;
SetCurrentSection(&fTOCSection);
context->heapOffset = fHeapOffset;
context->heapSize = fHeapSize;
rootAttributeHandler->SetLevel(0);
ClearAttributeHandlerStack();
PushAttributeHandler(rootAttributeHandler);
bool sectionHandled;
status_t error = ParseAttributeTree(context, sectionHandled);
if (error == B_OK && sectionHandled) {
if (fTOCSection.currentOffset < fTOCSection.uncompressedLength) {
ErrorOutput()->PrintError("Error: %llu excess byte(s) in TOC "
"section\n",
fTOCSection.uncompressedLength - fTOCSection.currentOffset);
error = B_BAD_DATA;
}
}
if (error != B_OK) {
context->ErrorOccurred();
while (AttributeHandler* handler = PopAttributeHandler()) {
if (handler != rootAttributeHandler)
handler->Delete(context);
}
return error;
}
return B_OK;
}
status_t
PackageReaderImpl::ReadAttributeValue(uint8 type, uint8 encoding,
AttributeValue& _value)
{
switch (type) {
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) {
ErrorOutput()->PrintError("Error: Invalid %s section: "
"invalid data reference\n", CurrentSection()->name);
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) {
ErrorOutput()->PrintError("Error: Invalid %s section: "
"inline data too long\n", CurrentSection()->name);
return B_BAD_DATA;
}
const void* buffer;
error = _GetTOCBuffer(size, buffer);
if (error != B_OK)
return error;
_value.SetToData(size, buffer);
} else {
ErrorOutput()->PrintError("Error: Invalid %s section: invalid "
"raw encoding (%u)\n", CurrentSection()->name, encoding);
return B_BAD_DATA;
}
return B_OK;
}
default:
return inherited::ReadAttributeValue(type, encoding, _value);
}
}
status_t
PackageReaderImpl::_GetTOCBuffer(size_t size, const void*& _buffer)
{
if (size > fTOCSection.uncompressedLength - fTOCSection.currentOffset) {
ErrorOutput()->PrintError("_GetTOCBuffer(%lu): read beyond TOC end\n",
size);
return B_BAD_DATA;
}
_buffer = fTOCSection.data + fTOCSection.currentOffset;
fTOCSection.currentOffset += size;
return B_OK;
}
}
}
}
}