17575abbcSIngo Weinhold/*
232832cbeSIngo Weinhold * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
37575abbcSIngo Weinhold * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
47575abbcSIngo Weinhold * Distributed under the terms of the MIT License.
57575abbcSIngo Weinhold */
67575abbcSIngo Weinhold
77575abbcSIngo Weinhold
87575abbcSIngo Weinhold#include <package/hpkg/v1/PackageReaderImpl.h>
97575abbcSIngo Weinhold
107575abbcSIngo Weinhold#include <errno.h>
117575abbcSIngo Weinhold#include <fcntl.h>
127575abbcSIngo Weinhold#include <stdio.h>
137575abbcSIngo Weinhold#include <stdlib.h>
147575abbcSIngo Weinhold#include <string.h>
157575abbcSIngo Weinhold#include <sys/stat.h>
167575abbcSIngo Weinhold#include <unistd.h>
177575abbcSIngo Weinhold
187575abbcSIngo Weinhold#include <algorithm>
197575abbcSIngo Weinhold#include <new>
207575abbcSIngo Weinhold
217575abbcSIngo Weinhold#include <ByteOrder.h>
227575abbcSIngo Weinhold
237575abbcSIngo Weinhold#include <package/hpkg/v1/HPKGDefsPrivate.h>
247575abbcSIngo Weinhold
257575abbcSIngo Weinhold#include <package/hpkg/ErrorOutput.h>
267575abbcSIngo Weinhold#include <package/hpkg/v1/PackageData.h>
277575abbcSIngo Weinhold#include <package/hpkg/v1/PackageEntry.h>
287575abbcSIngo Weinhold#include <package/hpkg/v1/PackageEntryAttribute.h>
297575abbcSIngo Weinhold
307575abbcSIngo Weinhold
317575abbcSIngo Weinholdnamespace BPackageKit {
327575abbcSIngo Weinhold
337575abbcSIngo Weinholdnamespace BHPKG {
347575abbcSIngo Weinhold
357575abbcSIngo Weinholdnamespace V1 {
367575abbcSIngo Weinhold
377575abbcSIngo Weinholdnamespace BPrivate {
387575abbcSIngo Weinhold
397575abbcSIngo Weinhold
407575abbcSIngo Weinhold//#define TRACE(format...)	printf(format)
417575abbcSIngo Weinhold#define TRACE(format...)	do {} while (false)
427575abbcSIngo Weinhold
437575abbcSIngo Weinhold
447575abbcSIngo Weinhold// maximum TOC size we support reading
457575abbcSIngo Weinholdstatic const size_t kMaxTOCSize					= 64 * 1024 * 1024;
467575abbcSIngo Weinhold
477575abbcSIngo Weinhold// maximum package attributes size we support reading
487575abbcSIngo Weinholdstatic const size_t kMaxPackageAttributesSize	= 1 * 1024 * 1024;
497575abbcSIngo Weinhold
507575abbcSIngo Weinhold
517575abbcSIngo Weinhold// #pragma mark - DataAttributeHandler
527575abbcSIngo Weinhold
537575abbcSIngo Weinhold
547575abbcSIngo Weinholdstruct PackageReaderImpl::DataAttributeHandler : AttributeHandler {
557575abbcSIngo Weinhold	DataAttributeHandler(BPackageData* data)
567575abbcSIngo Weinhold		:
577575abbcSIngo Weinhold		fData(data)
587575abbcSIngo Weinhold	{
597575abbcSIngo Weinhold	}
607575abbcSIngo Weinhold
617575abbcSIngo Weinhold	static status_t InitData(AttributeHandlerContext* context,
627575abbcSIngo Weinhold		BPackageData* data, const AttributeValue& value)
637575abbcSIngo Weinhold	{
647575abbcSIngo Weinhold		if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE)
657575abbcSIngo Weinhold			data->SetData(value.data.size, value.data.raw);
667575abbcSIngo Weinhold		else
677575abbcSIngo Weinhold			data->SetData(value.data.size, value.data.offset);
687575abbcSIngo Weinhold
697575abbcSIngo Weinhold		data->SetUncompressedSize(value.data.size);
707575abbcSIngo Weinhold
717575abbcSIngo Weinhold		return B_OK;
727575abbcSIngo Weinhold	}
737575abbcSIngo Weinhold
747575abbcSIngo Weinhold	static status_t Create(AttributeHandlerContext* context,
757575abbcSIngo Weinhold		BPackageData* data, const AttributeValue& value,
767575abbcSIngo Weinhold		AttributeHandler*& _handler)
777575abbcSIngo Weinhold	{
787575abbcSIngo Weinhold		DataAttributeHandler* handler = new(std::nothrow) DataAttributeHandler(
797575abbcSIngo Weinhold			data);
807575abbcSIngo Weinhold		if (handler == NULL)
817575abbcSIngo Weinhold			return B_NO_MEMORY;
827575abbcSIngo Weinhold
837575abbcSIngo Weinhold		InitData(context, data, value);
847575abbcSIngo Weinhold
857575abbcSIngo Weinhold		_handler = handler;
867575abbcSIngo Weinhold		return B_OK;
877575abbcSIngo Weinhold	}
887575abbcSIngo Weinhold
897575abbcSIngo Weinhold	virtual status_t HandleAttribute(AttributeHandlerContext* context,
907575abbcSIngo Weinhold		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
917575abbcSIngo Weinhold	{
927575abbcSIngo Weinhold		switch (id) {
937575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_DATA_SIZE:
947575abbcSIngo Weinhold				fData->SetUncompressedSize(value.unsignedInt);
957575abbcSIngo Weinhold				return B_OK;
967575abbcSIngo Weinhold
977575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION:
987575abbcSIngo Weinhold			{
997575abbcSIngo Weinhold				switch (value.unsignedInt) {
1007575abbcSIngo Weinhold					case B_HPKG_COMPRESSION_NONE:
1017575abbcSIngo Weinhold					case B_HPKG_COMPRESSION_ZLIB:
1027575abbcSIngo Weinhold						break;
1037575abbcSIngo Weinhold					default:
1047575abbcSIngo Weinhold						context->errorOutput->PrintError("Error: Invalid "
1057575abbcSIngo Weinhold							"compression type for data (%llu)\n",
1067575abbcSIngo Weinhold							value.unsignedInt);
1077575abbcSIngo Weinhold						return B_BAD_DATA;
1087575abbcSIngo Weinhold				}
1097575abbcSIngo Weinhold
1107575abbcSIngo Weinhold				fData->SetCompression(value.unsignedInt);
1117575abbcSIngo Weinhold				return B_OK;
1127575abbcSIngo Weinhold			}
1137575abbcSIngo Weinhold
1147575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_DATA_CHUNK_SIZE:
1157575abbcSIngo Weinhold				fData->SetChunkSize(value.unsignedInt);
1167575abbcSIngo Weinhold				return B_OK;
1177575abbcSIngo Weinhold		}
1187575abbcSIngo Weinhold
1197575abbcSIngo Weinhold		return AttributeHandler::HandleAttribute(context, id, value, _handler);
1207575abbcSIngo Weinhold	}
1217575abbcSIngo Weinhold
1227575abbcSIngo Weinholdprivate:
1237575abbcSIngo Weinhold	BPackageData*	fData;
1247575abbcSIngo Weinhold};
1257575abbcSIngo Weinhold
1267575abbcSIngo Weinhold
1277575abbcSIngo Weinhold// #pragma mark - AttributeAttributeHandler
1287575abbcSIngo Weinhold
1297575abbcSIngo Weinhold
1307575abbcSIngo Weinholdstruct PackageReaderImpl::AttributeAttributeHandler : AttributeHandler {
1317575abbcSIngo Weinhold	AttributeAttributeHandler(BPackageEntry* entry, const char* name)
1327575abbcSIngo Weinhold		:
1337575abbcSIngo Weinhold		fEntry(entry),
1347575abbcSIngo Weinhold		fAttribute(name)
1357575abbcSIngo Weinhold	{
1367575abbcSIngo Weinhold	}
1377575abbcSIngo Weinhold
1387575abbcSIngo Weinhold	virtual status_t HandleAttribute(AttributeHandlerContext* context,
1397575abbcSIngo Weinhold		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
1407575abbcSIngo Weinhold	{
1417575abbcSIngo Weinhold		switch (id) {
1427575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_DATA:
1437575abbcSIngo Weinhold				if (_handler != NULL) {
1447575abbcSIngo Weinhold					return DataAttributeHandler::Create(context,
1457575abbcSIngo Weinhold						&fAttribute.Data(), value, *_handler);
1467575abbcSIngo Weinhold				}
1477575abbcSIngo Weinhold				return DataAttributeHandler::InitData(context,
1487575abbcSIngo Weinhold					&fAttribute.Data(), value);
1497575abbcSIngo Weinhold
1507575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE:
1517575abbcSIngo Weinhold				fAttribute.SetType(value.unsignedInt);
1527575abbcSIngo Weinhold				return B_OK;
1537575abbcSIngo Weinhold		}
1547575abbcSIngo Weinhold
1557575abbcSIngo Weinhold		return AttributeHandler::HandleAttribute(context, id, value, _handler);
1567575abbcSIngo Weinhold	}
1577575abbcSIngo Weinhold
1587575abbcSIngo Weinhold	virtual status_t Delete(AttributeHandlerContext* context)
1597575abbcSIngo Weinhold	{
1607575abbcSIngo Weinhold		status_t error = context->packageContentHandler->HandleEntryAttribute(
1617575abbcSIngo Weinhold			fEntry, &fAttribute);
1627575abbcSIngo Weinhold
1637575abbcSIngo Weinhold		delete this;
1647575abbcSIngo Weinhold		return error;
1657575abbcSIngo Weinhold	}
1667575abbcSIngo Weinhold
1677575abbcSIngo Weinholdprivate:
1687575abbcSIngo Weinhold	BPackageEntry*			fEntry;
1697575abbcSIngo Weinhold	BPackageEntryAttribute	fAttribute;
1707575abbcSIngo Weinhold};
1717575abbcSIngo Weinhold
1727575abbcSIngo Weinhold
1737575abbcSIngo Weinhold// #pragma mark - EntryAttributeHandler
1747575abbcSIngo Weinhold
1757575abbcSIngo Weinhold
1767575abbcSIngo Weinholdstruct PackageReaderImpl::EntryAttributeHandler : AttributeHandler {
1777575abbcSIngo Weinhold	EntryAttributeHandler(AttributeHandlerContext* context,
1787575abbcSIngo Weinhold		BPackageEntry* parentEntry, const char* name)
1797575abbcSIngo Weinhold		:
1807575abbcSIngo Weinhold		fEntry(parentEntry, name),
1817575abbcSIngo Weinhold		fNotified(false)
1827575abbcSIngo Weinhold	{
1837575abbcSIngo Weinhold		_SetFileType(context, B_HPKG_DEFAULT_FILE_TYPE);
1847575abbcSIngo Weinhold	}
1857575abbcSIngo Weinhold
1867575abbcSIngo Weinhold	static status_t Create(AttributeHandlerContext* context,
1877575abbcSIngo Weinhold		BPackageEntry* parentEntry, const char* name,
1887575abbcSIngo Weinhold		AttributeHandler*& _handler)
1897575abbcSIngo Weinhold	{
1907575abbcSIngo Weinhold		// check name
1917575abbcSIngo Weinhold		if (name[0] == '\0' || strcmp(name, ".") == 0
1927575abbcSIngo Weinhold			|| strcmp(name, "..") == 0 || strchr(name, '/') != NULL) {
1937575abbcSIngo Weinhold			context->errorOutput->PrintError("Error: Invalid package: Invalid "
1947575abbcSIngo Weinhold				"entry name: \"%s\"\n", name);
1957575abbcSIngo Weinhold			return B_BAD_DATA;
1967575abbcSIngo Weinhold		}
1977575abbcSIngo Weinhold
1987575abbcSIngo Weinhold		// create handler
1997575abbcSIngo Weinhold		EntryAttributeHandler* handler = new(std::nothrow)
2007575abbcSIngo Weinhold			EntryAttributeHandler(context, parentEntry, name);
2017575abbcSIngo Weinhold		if (handler == NULL)
2027575abbcSIngo Weinhold			return B_NO_MEMORY;
2037575abbcSIngo Weinhold
2047575abbcSIngo Weinhold		_handler = handler;
2057575abbcSIngo Weinhold		return B_OK;
2067575abbcSIngo Weinhold	}
2077575abbcSIngo Weinhold
2087575abbcSIngo Weinhold	virtual status_t HandleAttribute(AttributeHandlerContext* context,
2097575abbcSIngo Weinhold		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
2107575abbcSIngo Weinhold	{
2117575abbcSIngo Weinhold		switch (id) {
2127575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY:
2137575abbcSIngo Weinhold			{
2147575abbcSIngo Weinhold				status_t error = _Notify(context);
2157575abbcSIngo Weinhold				if (error != B_OK)
2167575abbcSIngo Weinhold					return error;
2177575abbcSIngo Weinhold
2187575abbcSIngo Weinhold//TRACE("%*sentry \"%s\"\n", fLevel * 2, "", value.string);
2197575abbcSIngo Weinhold				if (_handler != NULL) {
2207575abbcSIngo Weinhold					return EntryAttributeHandler::Create(context, &fEntry,
2217575abbcSIngo Weinhold						value.string, *_handler);
2227575abbcSIngo Weinhold				}
2237575abbcSIngo Weinhold				return B_OK;
2247575abbcSIngo Weinhold			}
2257575abbcSIngo Weinhold
2267575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_TYPE:
2277575abbcSIngo Weinhold				return _SetFileType(context, value.unsignedInt);
2287575abbcSIngo Weinhold
2297575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS:
2307575abbcSIngo Weinhold				fEntry.SetPermissions(value.unsignedInt);
2317575abbcSIngo Weinhold				return B_OK;
2327575abbcSIngo Weinhold
2337575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_USER:
2347575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_GROUP:
2357575abbcSIngo Weinhold				// TODO:...
2367575abbcSIngo Weinhold				break;
2377575abbcSIngo Weinhold
2387575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_ATIME:
2397575abbcSIngo Weinhold				fEntry.SetAccessTime(value.unsignedInt);
2407575abbcSIngo Weinhold				return B_OK;
2417575abbcSIngo Weinhold
2427575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_MTIME:
2437575abbcSIngo Weinhold				fEntry.SetModifiedTime(value.unsignedInt);
2447575abbcSIngo Weinhold				return B_OK;
2457575abbcSIngo Weinhold
2467575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_CRTIME:
2477575abbcSIngo Weinhold				fEntry.SetCreationTime(value.unsignedInt);
2487575abbcSIngo Weinhold				return B_OK;
2497575abbcSIngo Weinhold
2507575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_ATIME_NANOS:
2517575abbcSIngo Weinhold				fEntry.SetAccessTimeNanos(value.unsignedInt);
2527575abbcSIngo Weinhold				return B_OK;
2537575abbcSIngo Weinhold
2547575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_MTIME_NANOS:
2557575abbcSIngo Weinhold				fEntry.SetModifiedTimeNanos(value.unsignedInt);
2567575abbcSIngo Weinhold				return B_OK;
2577575abbcSIngo Weinhold
2587575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_CRTIM_NANOS:
2597575abbcSIngo Weinhold				fEntry.SetCreationTimeNanos(value.unsignedInt);
2607575abbcSIngo Weinhold				return B_OK;
2617575abbcSIngo Weinhold
2627575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE:
2637575abbcSIngo Weinhold			{
2647575abbcSIngo Weinhold				status_t error = _Notify(context);
2657575abbcSIngo Weinhold				if (error != B_OK)
2667575abbcSIngo Weinhold					return error;
2677575abbcSIngo Weinhold
2687575abbcSIngo Weinhold				if (_handler != NULL) {
2697575abbcSIngo Weinhold					*_handler = new(std::nothrow) AttributeAttributeHandler(
2707575abbcSIngo Weinhold						&fEntry, value.string);
2717575abbcSIngo Weinhold					if (*_handler == NULL)
2727575abbcSIngo Weinhold						return B_NO_MEMORY;
2737575abbcSIngo Weinhold					return B_OK;
2747575abbcSIngo Weinhold				} else {
2757575abbcSIngo Weinhold					BPackageEntryAttribute attribute(value.string);
2767575abbcSIngo Weinhold					return context->packageContentHandler->HandleEntryAttribute(
2777575abbcSIngo Weinhold						&fEntry, &attribute);
2787575abbcSIngo Weinhold				}
2797575abbcSIngo Weinhold			}
2807575abbcSIngo Weinhold
2817575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_DATA:
2827575abbcSIngo Weinhold				if (_handler != NULL) {
2837575abbcSIngo Weinhold					return DataAttributeHandler::Create(context, &fEntry.Data(),
2847575abbcSIngo Weinhold						value, *_handler);
2857575abbcSIngo Weinhold				}
2867575abbcSIngo Weinhold				return DataAttributeHandler::InitData(context, &fEntry.Data(),
2877575abbcSIngo Weinhold					value);
2887575abbcSIngo Weinhold
2897575abbcSIngo Weinhold			case B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH:
2907575abbcSIngo Weinhold				fEntry.SetSymlinkPath(value.string);
2917575abbcSIngo Weinhold				return B_OK;
2927575abbcSIngo Weinhold		}
2937575abbcSIngo Weinhold
2947575abbcSIngo Weinhold		return AttributeHandler::HandleAttribute(context, id, value, _handler);
2957575abbcSIngo Weinhold	}
2967575abbcSIngo Weinhold
2977575abbcSIngo Weinhold	virtual status_t Delete(AttributeHandlerContext* context)
2987575abbcSIngo Weinhold	{
2997575abbcSIngo Weinhold		// notify if not done yet
3007575abbcSIngo Weinhold		status_t error = _Notify(context);
3017575abbcSIngo Weinhold
3027575abbcSIngo Weinhold		// notify done
3037575abbcSIngo Weinhold		if (error == B_OK)
3047575abbcSIngo Weinhold			error = context->packageContentHandler->HandleEntryDone(&fEntry);
3057575abbcSIngo Weinhold		else
3067575abbcSIngo Weinhold			context->packageContentHandler->HandleEntryDone(&fEntry);
3077575abbcSIngo Weinhold
3087575abbcSIngo Weinhold		delete this;
3097575abbcSIngo Weinhold		return error;
3107575abbcSIngo Weinhold	}
3117575abbcSIngo Weinhold
3127575abbcSIngo Weinholdprivate:
3137575abbcSIngo Weinhold	status_t _Notify(AttributeHandlerContext* context)
3147575abbcSIngo Weinhold	{
3157575abbcSIngo Weinhold		if (fNotified)
3167575abbcSIngo Weinhold			return B_OK;
3177575abbcSIngo Weinhold
3187575abbcSIngo Weinhold		fNotified = true;
3197575abbcSIngo Weinhold		return context->packageContentHandler->HandleEntry(&fEntry);
3207575abbcSIngo Weinhold	}
3217575abbcSIngo Weinhold
3227575abbcSIngo Weinhold	status_t _SetFileType(AttributeHandlerContext* context, uint64 fileType)
3237575abbcSIngo Weinhold	{
3247575abbcSIngo Weinhold		switch (fileType) {
3257575abbcSIngo Weinhold			case B_HPKG_FILE_TYPE_FILE:
3267575abbcSIngo Weinhold				fEntry.SetType(S_IFREG);
3277575abbcSIngo Weinhold				fEntry.SetPermissions(B_HPKG_DEFAULT_FILE_PERMISSIONS);
3287575abbcSIngo Weinhold				break;
3297575abbcSIngo Weinhold
3307575abbcSIngo Weinhold			case B_HPKG_FILE_TYPE_DIRECTORY:
3317575abbcSIngo Weinhold				fEntry.SetType(S_IFDIR);
3327575abbcSIngo Weinhold				fEntry.SetPermissions(B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS);
3337575abbcSIngo Weinhold				break;
3347575abbcSIngo Weinhold
3357575abbcSIngo Weinhold			case B_HPKG_FILE_TYPE_SYMLINK:
3367575abbcSIngo Weinhold				fEntry.SetType(S_IFLNK);
3377575abbcSIngo Weinhold				fEntry.SetPermissions(B_HPKG_DEFAULT_SYMLINK_PERMISSIONS);
3387575abbcSIngo Weinhold				break;
3397575abbcSIngo Weinhold
3407575abbcSIngo Weinhold			default:
3417575abbcSIngo Weinhold				context->errorOutput->PrintError("Error: Invalid file type for "
3427575abbcSIngo Weinhold					"package entry (%llu)\n", fileType);
3437575abbcSIngo Weinhold				return B_BAD_DATA;
3447575abbcSIngo Weinhold		}
3457575abbcSIngo Weinhold		return B_OK;
3467575abbcSIngo Weinhold	}
3477575abbcSIngo Weinhold
3487575abbcSIngo Weinholdprivate:
3497575abbcSIngo Weinhold	BPackageEntry	fEntry;
3507575abbcSIngo Weinhold	bool			fNotified;
3517575abbcSIngo Weinhold};
3527575abbcSIngo Weinhold
3537575abbcSIngo Weinhold
3547575abbcSIngo Weinhold// #pragma mark - RootAttributeHandler
3557575abbcSIngo Weinhold
3567575abbcSIngo Weinhold
3577575abbcSIngo Weinholdstruct PackageReaderImpl::RootAttributeHandler : PackageAttributeHandler {
3587575abbcSIngo Weinhold	typedef PackageAttributeHandler inherited;
3597575abbcSIngo Weinhold
3607575abbcSIngo Weinhold	virtual status_t HandleAttribute(AttributeHandlerContext* context,
3617575abbcSIngo Weinhold		uint8 id, const AttributeValue& value, AttributeHandler** _handler)
3627575abbcSIngo Weinhold	{
3637575abbcSIngo Weinhold		if (id == B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY) {
3647575abbcSIngo Weinhold			if (_handler != NULL) {
3657575abbcSIngo Weinhold				return EntryAttributeHandler::Create(context, NULL,
3667575abbcSIngo Weinhold					value.string, *_handler);
3677575abbcSIngo Weinhold			}
3687575abbcSIngo Weinhold			return B_OK;
3697575abbcSIngo Weinhold		}
3707575abbcSIngo Weinhold
3717575abbcSIngo Weinhold		return inherited::HandleAttribute(context, id, value, _handler);
3727575abbcSIngo Weinhold	}
3737575abbcSIngo Weinhold};
3747575abbcSIngo Weinhold
3757575abbcSIngo Weinhold
3767575abbcSIngo Weinhold// #pragma mark - PackageReaderImpl
3777575abbcSIngo Weinhold
3787575abbcSIngo Weinhold
3797575abbcSIngo WeinholdPackageReaderImpl::PackageReaderImpl(BErrorOutput* errorOutput)
3807575abbcSIngo Weinhold	:
3817575abbcSIngo Weinhold	inherited(errorOutput),
3827575abbcSIngo Weinhold	fTOCSection("TOC")
3837575abbcSIngo Weinhold{
3847575abbcSIngo Weinhold}
3857575abbcSIngo Weinhold
3867575abbcSIngo Weinhold
3877575abbcSIngo WeinholdPackageReaderImpl::~PackageReaderImpl()
3887575abbcSIngo Weinhold{
3897575abbcSIngo Weinhold}
3907575abbcSIngo Weinhold
3917575abbcSIngo Weinhold
3927575abbcSIngo Weinholdstatus_t
3937575abbcSIngo WeinholdPackageReaderImpl::Init(const char* fileName)
3947575abbcSIngo Weinhold{
3957575abbcSIngo Weinhold	// open file
3967575abbcSIngo Weinhold	int fd = open(fileName, O_RDONLY);
3977575abbcSIngo Weinhold	if (fd < 0) {
3987575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Failed to open package file \"%s\": "
3997575abbcSIngo Weinhold			"%s\n", fileName, strerror(errno));
4007575abbcSIngo Weinhold		return errno;
4017575abbcSIngo Weinhold	}
4027575abbcSIngo Weinhold
4037575abbcSIngo Weinhold	return Init(fd, true);
4047575abbcSIngo Weinhold}
4057575abbcSIngo Weinhold
4067575abbcSIngo Weinhold
4077575abbcSIngo Weinholdstatus_t
4087575abbcSIngo WeinholdPackageReaderImpl::Init(int fd, bool keepFD)
4097575abbcSIngo Weinhold{
4107575abbcSIngo Weinhold	status_t error = inherited::Init(fd, keepFD);
4117575abbcSIngo Weinhold	if (error != B_OK)
4127575abbcSIngo Weinhold		return error;
4137575abbcSIngo Weinhold
4147575abbcSIngo Weinhold	// stat it
4157575abbcSIngo Weinhold	struct stat st;
4167575abbcSIngo Weinhold	if (fstat(FD(), &st) < 0) {
4177575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Failed to access package file: %s\n",
4187575abbcSIngo Weinhold			strerror(errno));
4197575abbcSIngo Weinhold		return errno;
4207575abbcSIngo Weinhold	}
4217575abbcSIngo Weinhold
4227575abbcSIngo Weinhold	// read the header
4237575abbcSIngo Weinhold	hpkg_header header;
4247575abbcSIngo Weinhold	if ((error = ReadBuffer(0, &header, sizeof(header))) != B_OK)
4257575abbcSIngo Weinhold		return error;
4267575abbcSIngo Weinhold
4277575abbcSIngo Weinhold	// check the header
4287575abbcSIngo Weinhold
4297575abbcSIngo Weinhold	// magic
4307575abbcSIngo Weinhold	if (B_BENDIAN_TO_HOST_INT32(header.magic) != B_HPKG_MAGIC) {
4317575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Invalid package file: Invalid "
4327575abbcSIngo Weinhold			"magic\n");
4337575abbcSIngo Weinhold		return B_BAD_DATA;
4347575abbcSIngo Weinhold	}
4357575abbcSIngo Weinhold
4367575abbcSIngo Weinhold	// version
4377575abbcSIngo Weinhold	if (B_BENDIAN_TO_HOST_INT16(header.version) != B_HPKG_VERSION) {
4387575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Invalid/unsupported package file "
4397575abbcSIngo Weinhold			"version (%d)\n", B_BENDIAN_TO_HOST_INT16(header.version));
4407575abbcSIngo Weinhold		return B_MISMATCHED_VALUES;
4417575abbcSIngo Weinhold	}
4427575abbcSIngo Weinhold
4437575abbcSIngo Weinhold	// header size
4447575abbcSIngo Weinhold	fHeapOffset = B_BENDIAN_TO_HOST_INT16(header.header_size);
4457575abbcSIngo Weinhold	if ((size_t)fHeapOffset < sizeof(hpkg_header)) {
4467575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Invalid package file: Invalid header "
4477575abbcSIngo Weinhold			"size (%llu)\n", fHeapOffset);
4487575abbcSIngo Weinhold		return B_BAD_DATA;
4497575abbcSIngo Weinhold	}
4507575abbcSIngo Weinhold
4517575abbcSIngo Weinhold	// total size
4527575abbcSIngo Weinhold	fTotalSize = B_BENDIAN_TO_HOST_INT64(header.total_size);
4537575abbcSIngo Weinhold	if (fTotalSize != (uint64)st.st_size) {
4547575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Invalid package file: Total size in "
4557575abbcSIngo Weinhold			"header (%llu) doesn't agree with total file size (%lld)\n",
4567575abbcSIngo Weinhold			fTotalSize, st.st_size);
4577575abbcSIngo Weinhold		return B_BAD_DATA;
4587575abbcSIngo Weinhold	}
4597575abbcSIngo Weinhold
4607575abbcSIngo Weinhold	// package attributes length and compression
4617575abbcSIngo Weinhold	fPackageAttributesSection.compression
4627575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT32(header.attributes_compression);
4637575abbcSIngo Weinhold	fPackageAttributesSection.compressedLength
4647575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed);
4657575abbcSIngo Weinhold	fPackageAttributesSection.uncompressedLength
4667575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT32(header.attributes_length_uncompressed);
4677575abbcSIngo Weinhold	fPackageAttributesSection.stringsLength
4687575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT32(header.attributes_strings_length);
4697575abbcSIngo Weinhold	fPackageAttributesSection.stringsCount
4707575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT32(header.attributes_strings_count);
4717575abbcSIngo Weinhold
4727575abbcSIngo Weinhold	if (const char* errorString = CheckCompression(
4737575abbcSIngo Weinhold		fPackageAttributesSection)) {
4747575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Invalid package file: package "
4757575abbcSIngo Weinhold			"attributes section: %s\n", errorString);
4767575abbcSIngo Weinhold		return B_BAD_DATA;
4777575abbcSIngo Weinhold	}
4787575abbcSIngo Weinhold
4797575abbcSIngo Weinhold	// TOC length and compression
4807575abbcSIngo Weinhold	fTOCSection.compression = B_BENDIAN_TO_HOST_INT32(header.toc_compression);
4817575abbcSIngo Weinhold	fTOCSection.compressedLength
4827575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed);
4837575abbcSIngo Weinhold	fTOCSection.uncompressedLength
4847575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT64(header.toc_length_uncompressed);
4857575abbcSIngo Weinhold
4867575abbcSIngo Weinhold	if (const char* errorString = CheckCompression(fTOCSection)) {
4877575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Invalid package file: TOC section: "
4887575abbcSIngo Weinhold			"%s\n", errorString);
4897575abbcSIngo Weinhold		return B_BAD_DATA;
4907575abbcSIngo Weinhold	}
4917575abbcSIngo Weinhold
4927575abbcSIngo Weinhold	// TOC subsections
4937575abbcSIngo Weinhold	fTOCSection.stringsLength
4947575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT64(header.toc_strings_length);
4957575abbcSIngo Weinhold	fTOCSection.stringsCount
4967575abbcSIngo Weinhold		= B_BENDIAN_TO_HOST_INT64(header.toc_strings_count);
4977575abbcSIngo Weinhold
4987575abbcSIngo Weinhold	if (fTOCSection.stringsLength > fTOCSection.uncompressedLength
4997575abbcSIngo Weinhold		|| fTOCSection.stringsCount > fTOCSection.stringsLength) {
5007575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Invalid package file: Invalid TOC "
5017575abbcSIngo Weinhold			"subsections description\n");
5027575abbcSIngo Weinhold		return B_BAD_DATA;
5037575abbcSIngo Weinhold	}
5047575abbcSIngo Weinhold
5057575abbcSIngo Weinhold	// check whether the sections fit together
5067575abbcSIngo Weinhold	if (fPackageAttributesSection.compressedLength > fTotalSize
5077575abbcSIngo Weinhold		|| fTOCSection.compressedLength
5087575abbcSIngo Weinhold			> fTotalSize - fPackageAttributesSection.compressedLength
5097575abbcSIngo Weinhold		|| fHeapOffset
5107575abbcSIngo Weinhold			> fTotalSize - fPackageAttributesSection.compressedLength
5117575abbcSIngo Weinhold				- fTOCSection.compressedLength) {
5127575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Invalid package file: The sum of the "
5137575abbcSIngo Weinhold			"sections sizes is greater than the package size\n");
5147575abbcSIngo Weinhold		return B_BAD_DATA;
5157575abbcSIngo Weinhold	}
5167575abbcSIngo Weinhold
5177575abbcSIngo Weinhold	fPackageAttributesSection.offset
5187575abbcSIngo Weinhold		= fTotalSize - fPackageAttributesSection.compressedLength;
5197575abbcSIngo Weinhold	fTOCSection.offset = fPackageAttributesSection.offset
5207575abbcSIngo Weinhold		- fTOCSection.compressedLength;
5217575abbcSIngo Weinhold	fHeapSize = fTOCSection.offset - fHeapOffset;
5227575abbcSIngo Weinhold
5237575abbcSIngo Weinhold	// TOC size sanity check
5247575abbcSIngo Weinhold	if (fTOCSection.uncompressedLength > kMaxTOCSize) {
5257575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Package file TOC section size "
5267575abbcSIngo Weinhold			"is %llu bytes. This is beyond the reader's sanity limit\n",
5277575abbcSIngo Weinhold			fTOCSection.uncompressedLength);
5287575abbcSIngo Weinhold		return B_UNSUPPORTED;
5297575abbcSIngo Weinhold	}
5307575abbcSIngo Weinhold
5317575abbcSIngo Weinhold	// package attributes size sanity check
5327575abbcSIngo Weinhold	if (fPackageAttributesSection.uncompressedLength
5337575abbcSIngo Weinhold			> kMaxPackageAttributesSize) {
5347575abbcSIngo Weinhold		ErrorOutput()->PrintError(
5357575abbcSIngo Weinhold			"Error: Package file package attributes section size "
5367575abbcSIngo Weinhold			"is %llu bytes. This is beyond the reader's sanity limit\n",
5377575abbcSIngo Weinhold			fPackageAttributesSection.uncompressedLength);
5387575abbcSIngo Weinhold		return B_UNSUPPORTED;
5397575abbcSIngo Weinhold	}
5407575abbcSIngo Weinhold
5417575abbcSIngo Weinhold	// read in the complete TOC
5427575abbcSIngo Weinhold	fTOCSection.data
5437575abbcSIngo Weinhold		= new(std::nothrow) uint8[fTOCSection.uncompressedLength];
5447575abbcSIngo Weinhold	if (fTOCSection.data == NULL) {
5457575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Out of memory!\n");
5467575abbcSIngo Weinhold		return B_NO_MEMORY;
5477575abbcSIngo Weinhold	}
5487575abbcSIngo Weinhold	error = ReadCompressedBuffer(fTOCSection);
5497575abbcSIngo Weinhold	if (error != B_OK)
5507575abbcSIngo Weinhold		return error;
5517575abbcSIngo Weinhold
5527575abbcSIngo Weinhold	// read in the complete package attributes section
5537575abbcSIngo Weinhold	fPackageAttributesSection.data
5547575abbcSIngo Weinhold		= new(std::nothrow) uint8[fPackageAttributesSection.uncompressedLength];
5557575abbcSIngo Weinhold	if (fPackageAttributesSection.data == NULL) {
5567575abbcSIngo Weinhold		ErrorOutput()->PrintError("Error: Out of memory!\n");
5577575abbcSIngo Weinhold		return B_NO_MEMORY;
5587575abbcSIngo Weinhold	}
5597575abbcSIngo Weinhold	error = ReadCompressedBuffer(fPackageAttributesSection);
5607575abbcSIngo Weinhold	if (error != B_OK)
5617575abbcSIngo Weinhold		return error;
5627575abbcSIngo Weinhold
5637575abbcSIngo Weinhold	// start parsing the TOC
5647575abbcSIngo Weinhold	fTOCSection.currentOffset = 0;
5657575abbcSIngo Weinhold	SetCurrentSection(&fTOCSection);
5667575abbcSIngo Weinhold
5677575abbcSIngo Weinhold	// strings
5687575abbcSIngo Weinhold	error = ParseStrings();
5697575abbcSIngo Weinhold	if (error != B_OK)
5707575abbcSIngo Weinhold		return error;
5717575abbcSIngo Weinhold
5727575abbcSIngo Weinhold	// parse strings from package attributes section
5737575abbcSIngo Weinhold	fPackageAttributesSection.currentOffset = 0;
5747575abbcSIngo Weinhold	SetCurrentSection(&fPackageAttributesSection);
5757575abbcSIngo Weinhold
5767575abbcSIngo Weinhold	// strings
5777575abbcSIngo Weinhold	error = ParseStrings();
5787575abbcSIngo Weinhold	if (error != B_OK)
5797575abbcSIngo Weinhold		return error;
5807575abbcSIngo Weinhold
5817575abbcSIngo Weinhold	SetCurrentSection(NULL);
5827575abbcSIngo Weinhold
5837575abbcSIngo Weinhold	return B_OK;
5847575abbcSIngo Weinhold}
5857575abbcSIngo Weinhold
5867575abbcSIngo Weinhold
5877575abbcSIngo Weinholdstatus_t
5887575abbcSIngo WeinholdPackageReaderImpl::ParseContent(BPackageContentHandler* contentHandler)
5897575abbcSIngo Weinhold{
5907575abbcSIngo Weinhold	AttributeHandlerContext context(ErrorOutput(), contentHandler,
5917575abbcSIngo Weinhold		B_HPKG_SECTION_PACKAGE_ATTRIBUTES);
5927575abbcSIngo Weinhold	RootAttributeHandler rootAttributeHandler;
5937575abbcSIngo Weinhold
5947575abbcSIngo Weinhold	status_t error
5957575abbcSIngo Weinhold		= ParsePackageAttributesSection(&context, &rootAttributeHandler);
5967575abbcSIngo Weinhold
5977575abbcSIngo Weinhold	if (error == B_OK) {
5987575abbcSIngo Weinhold		context.section = B_HPKG_SECTION_PACKAGE_TOC;
5997575abbcSIngo Weinhold		error = _ParseTOC(&context, &rootAttributeHandler);
6007575abbcSIngo Weinhold	}
6017575abbcSIngo Weinhold
6027575abbcSIngo Weinhold	return error;
6037575abbcSIngo Weinhold}
6047575abbcSIngo Weinhold
6057575abbcSIngo Weinhold
6067575abbcSIngo Weinholdstatus_t
6077575abbcSIngo WeinholdPackageReaderImpl::ParseContent(BLowLevelPackageContentHandler* contentHandler)
6087575abbcSIngo Weinhold{
6097575abbcSIngo Weinhold	AttributeHandlerContext context(ErrorOutput(), contentHandler,
6107575abbcSIngo Weinhold		B_HPKG_SECTION_PACKAGE_ATTRIBUTES);
6117575abbcSIngo Weinhold	LowLevelAttributeHandler rootAttributeHandler;
6127575abbcSIngo Weinhold
6137575abbcSIngo Weinhold	status_t error
6147575abbcSIngo Weinhold		= ParsePackageAttributesSection(&context, &rootAttributeHandler);
6157575abbcSIngo Weinhold
6167575abbcSIngo Weinhold	if (error == B_OK) {
6177575abbcSIngo Weinhold		context.section = B_HPKG_SECTION_PACKAGE_TOC;
6187575abbcSIngo Weinhold		error = _ParseTOC(&context, &rootAttributeHandler);
6197575abbcSIngo Weinhold	}
6207575abbcSIngo Weinhold
6217575abbcSIngo Weinhold	return error;
6227575abbcSIngo Weinhold}
6237575abbcSIngo Weinhold
6247575abbcSIngo Weinhold
6257575abbcSIngo Weinholdstatus_t
6267575abbcSIngo WeinholdPackageReaderImpl::_ParseTOC(AttributeHandlerContext* context,
6277575abbcSIngo Weinhold	AttributeHandler* rootAttributeHandler)
6287575abbcSIngo Weinhold{
6297575abbcSIngo Weinhold	// parse the TOC
6307575abbcSIngo Weinhold	fTOCSection.currentOffset = fTOCSection.stringsLength;
6317575abbcSIngo Weinhold	SetCurrentSection(&fTOCSection);
6327575abbcSIngo Weinhold
6337575abbcSIngo Weinhold	// prepare attribute handler context
6347575abbcSIngo Weinhold	context->heapOffset = fHeapOffset;
6357575abbcSIngo Weinhold	context->heapSize = fHeapSize;
6367575abbcSIngo Weinhold
6377575abbcSIngo Weinhold	// init the attribute handler stack
6387575abbcSIngo Weinhold	rootAttributeHandler->SetLevel(0);
6397575abbcSIngo Weinhold	ClearAttributeHandlerStack();
6407575abbcSIngo Weinhold	PushAttributeHandler(rootAttributeHandler);
6417575abbcSIngo Weinhold
6427575abbcSIngo Weinhold	bool sectionHandled;
6437575abbcSIngo Weinhold	status_t error = ParseAttributeTree(context, sectionHandled);
6447575abbcSIngo Weinhold	if (error == B_OK && sectionHandled) {
6457575abbcSIngo Weinhold		if (fTOCSection.currentOffset < fTOCSection.uncompressedLength) {
6467575abbcSIngo Weinhold			ErrorOutput()->PrintError("Error: %llu excess byte(s) in TOC "
6477575abbcSIngo Weinhold				"section\n",
6487575abbcSIngo Weinhold				fTOCSection.uncompressedLength - fTOCSection.currentOffset);
6497575abbcSIngo Weinhold			error = B_BAD_DATA;
6507575abbcSIngo Weinhold		}
6517575abbcSIngo Weinhold	}
6527575abbcSIngo Weinhold
6537575abbcSIngo Weinhold	// clean up on error
6547575abbcSIngo Weinhold	if (error != B_OK) {
6557575abbcSIngo Weinhold		context->ErrorOccurred();
6567575abbcSIngo Weinhold		while (AttributeHandler* handler = PopAttributeHandler()) {
6577575abbcSIngo Weinhold			if (handler != rootAttributeHandler)
6587575abbcSIngo Weinhold				handler->Delete(context);
6597575abbcSIngo Weinhold		}
6607575abbcSIngo Weinhold		return error;
6617575abbcSIngo Weinhold	}
6627575abbcSIngo Weinhold
6637575abbcSIngo Weinhold	return B_OK;
6647575abbcSIngo Weinhold}
6657575abbcSIngo Weinhold
6667575abbcSIngo Weinhold
6677575abbcSIngo Weinholdstatus_t
6687575abbcSIngo WeinholdPackageReaderImpl::ReadAttributeValue(uint8 type, uint8 encoding,
6697575abbcSIngo Weinhold	AttributeValue& _value)
6707575abbcSIngo Weinhold{
6717575abbcSIngo Weinhold	switch (type) {
6727575abbcSIngo Weinhold		case B_HPKG_ATTRIBUTE_TYPE_RAW:
6737575abbcSIngo Weinhold		{
6747575abbcSIngo Weinhold			uint64 size;
6757575abbcSIngo Weinhold			status_t error = ReadUnsignedLEB128(size);
6767575abbcSIngo Weinhold			if (error != B_OK)
6777575abbcSIngo Weinhold				return error;
6787575abbcSIngo Weinhold
6797575abbcSIngo Weinhold			if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
6807575abbcSIngo Weinhold				uint64 offset;
6817575abbcSIngo Weinhold				error = ReadUnsignedLEB128(offset);
6827575abbcSIngo Weinhold				if (error != B_OK)
6837575abbcSIngo Weinhold					return error;
6847575abbcSIngo Weinhold
6857575abbcSIngo Weinhold				if (offset > fHeapSize || size > fHeapSize - offset) {
6867575abbcSIngo Weinhold					ErrorOutput()->PrintError("Error: Invalid %s section: "
6877575abbcSIngo Weinhold						"invalid data reference\n", CurrentSection()->name);
6887575abbcSIngo Weinhold					return B_BAD_DATA;
6897575abbcSIngo Weinhold				}
6907575abbcSIngo Weinhold
6917575abbcSIngo Weinhold				_value.SetToData(size, fHeapOffset + offset);
6927575abbcSIngo Weinhold			} else if (encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
6937575abbcSIngo Weinhold				if (size > B_HPKG_MAX_INLINE_DATA_SIZE) {
6947575abbcSIngo Weinhold					ErrorOutput()->PrintError("Error: Invalid %s section: "
6957575abbcSIngo Weinhold						"inline data too long\n", CurrentSection()->name);
6967575abbcSIngo Weinhold					return B_BAD_DATA;
6977575abbcSIngo Weinhold				}
6987575abbcSIngo Weinhold
6997575abbcSIngo Weinhold				const void* buffer;
7007575abbcSIngo Weinhold				error = _GetTOCBuffer(size, buffer);
7017575abbcSIngo Weinhold				if (error != B_OK)
7027575abbcSIngo Weinhold					return error;
7037575abbcSIngo Weinhold				_value.SetToData(size, buffer);
7047575abbcSIngo Weinhold			} else {
7057575abbcSIngo Weinhold				ErrorOutput()->PrintError("Error: Invalid %s section: invalid "
7067575abbcSIngo Weinhold					"raw encoding (%u)\n", CurrentSection()->name, encoding);
7077575abbcSIngo Weinhold				return B_BAD_DATA;
7087575abbcSIngo Weinhold			}
7097575abbcSIngo Weinhold
7107575abbcSIngo Weinhold			return B_OK;
7117575abbcSIngo Weinhold		}
7127575abbcSIngo Weinhold
7137575abbcSIngo Weinhold		default:
7147575abbcSIngo Weinhold			return inherited::ReadAttributeValue(type, encoding, _value);
7157575abbcSIngo Weinhold	}
7167575abbcSIngo Weinhold}
7177575abbcSIngo Weinhold
7187575abbcSIngo Weinhold
7197575abbcSIngo Weinholdstatus_t
7207575abbcSIngo WeinholdPackageReaderImpl::_GetTOCBuffer(size_t size, const void*& _buffer)
7217575abbcSIngo Weinhold{
7227575abbcSIngo Weinhold	if (size > fTOCSection.uncompressedLength - fTOCSection.currentOffset) {
7237575abbcSIngo Weinhold		ErrorOutput()->PrintError("_GetTOCBuffer(%lu): read beyond TOC end\n",
7247575abbcSIngo Weinhold			size);
7257575abbcSIngo Weinhold		return B_BAD_DATA;
7267575abbcSIngo Weinhold	}
7277575abbcSIngo Weinhold
7287575abbcSIngo Weinhold	_buffer = fTOCSection.data + fTOCSection.currentOffset;
7297575abbcSIngo Weinhold	fTOCSection.currentOffset += size;
7307575abbcSIngo Weinhold	return B_OK;
7317575abbcSIngo Weinhold}
7327575abbcSIngo Weinhold
7337575abbcSIngo Weinhold
7347575abbcSIngo Weinhold}	// namespace BPrivate
7357575abbcSIngo Weinhold
7367575abbcSIngo Weinhold}	// namespace V1
7377575abbcSIngo Weinhold
7387575abbcSIngo Weinhold}	// namespace BHPKG
7397575abbcSIngo Weinhold
7407575abbcSIngo Weinhold}	// namespace BPackageKit
741