/*
* Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
* Distributed under the terms of the MIT License.
*/
#include <package/hpkg/RepositoryWriterImpl.h>
#include <algorithm>
#include <new>
#include <ByteOrder.h>
#include <Message.h>
#include <Path.h>
#include <AutoDeleter.h>
#include <HashSet.h>
#include <package/hpkg/BlockBufferPoolNoLock.h>
#include <package/hpkg/HPKGDefsPrivate.h>
#include <package/hpkg/PackageDataReader.h>
#include <package/hpkg/PackageEntry.h>
#include <package/hpkg/PackageFileHeapWriter.h>
#include <package/hpkg/PackageInfoAttributeValue.h>
#include <package/hpkg/PackageReader.h>
#include <package/ChecksumAccessors.h>
#include <package/HashableString.h>
#include <package/PackageInfoContentHandler.h>
#include <package/RepositoryInfo.h>
namespace BPackageKit {
namespace BHPKG {
namespace BPrivate {
using BPackageKit::BPrivate::GeneralFileChecksumAccessor;
using BPackageKit::BPrivate::HashableString;
namespace {
// #pragma mark - PackageEntryDataFetcher
struct PackageEntryDataFetcher {
PackageEntryDataFetcher(BErrorOutput* errorOutput,
BPackageData& packageData)
:
fErrorOutput(errorOutput),
fPackageData(packageData)
{
}
status_t ReadIntoString(BAbstractBufferedDataReader* heapReader,
BString& _contents)
{
// create a PackageDataReader
BAbstractBufferedDataReader* reader;
status_t result = BPackageDataReaderFactory()
.CreatePackageDataReader(heapReader, fPackageData, reader);
if (result != B_OK)
return result;
ObjectDeleter<BAbstractBufferedDataReader> readerDeleter(reader);
// copy data into the given string
int32 bufferSize = fPackageData.Size();
char* buffer = _contents.LockBuffer(bufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
result = reader->ReadData(0, buffer, bufferSize);
if (result != B_OK) {
fErrorOutput->PrintError("Error: Failed to read data: %s\n",
strerror(result));
_contents.UnlockBuffer(0);
} else
_contents.UnlockBuffer(bufferSize);
return result;
}
private:
BErrorOutput* fErrorOutput;
BPackageData& fPackageData;
};
// #pragma mark - PackageContentHandler
struct PackageContentHandler : public BPackageInfoContentHandler {
PackageContentHandler(BErrorOutput* errorOutput, BPackageInfo* packageInfo,
BAbstractBufferedDataReader* heapReader,
BRepositoryInfo* repositoryInfo)
:
BPackageInfoContentHandler(*packageInfo, errorOutput),
fHeapReader(heapReader),
fRepositoryInfo(repositoryInfo)
{
}
virtual status_t HandleEntry(BPackageEntry* entry)
{
// if license must be approved, read any license files from package such
// that those can be stored in the repository later
if ((fPackageInfo.Flags() & B_PACKAGE_FLAG_APPROVE_LICENSE) == 0
|| entry == NULL)
return B_OK;
// return if not in ./data/licenses folder
const BPackageEntry* parent = entry->Parent();
BString licenseFolderName("licenses");
if (parent == NULL || licenseFolderName != parent->Name())
return B_OK;
parent = parent->Parent();
BString dataFolderName("data");
if (parent == NULL || dataFolderName != parent->Name())
return B_OK;
if (parent->Parent() != NULL)
return B_OK;
// check if license already is in repository
const BStringList& licenseNames = fRepositoryInfo->LicenseNames();
for (int i = 0; i < licenseNames.CountStrings(); ++i) {
if (licenseNames.StringAt(i).ICompare(entry->Name()) == 0) {
// license already exists
return B_OK;
}
}
// fetch contents of license file
BPackageData& packageData = entry->Data();
PackageEntryDataFetcher dataFetcher(fErrorOutput, packageData);
BString licenseText;
status_t result = dataFetcher.ReadIntoString(fHeapReader, licenseText);
if (result != B_OK)
return result;
// add license to repository
return fRepositoryInfo->AddLicense(entry->Name(), licenseText);
}
virtual status_t HandleEntryAttribute(BPackageEntry* entry,
BPackageEntryAttribute* attribute)
{
return B_OK;
}
virtual status_t HandleEntryDone(BPackageEntry* entry)
{
return B_OK;
}
virtual void HandleErrorOccurred()
{
}
private:
BPackageReader* fPackageReader;
BAbstractBufferedDataReader* fHeapReader;
BRepositoryInfo* fRepositoryInfo;
};
} // anonymous namespace
// #pragma mark - PackageNameSet
struct RepositoryWriterImpl::PackageNameSet
: public ::BPrivate::HashSet<HashableString> {
};
// #pragma mark - RepositoryWriterImpl
RepositoryWriterImpl::RepositoryWriterImpl(BRepositoryWriterListener* listener,
BRepositoryInfo* repositoryInfo)
:
inherited("repository", listener),
fListener(listener),
fRepositoryInfo(repositoryInfo),
fPackageCount(0),
fPackageNames(NULL)
{
}
RepositoryWriterImpl::~RepositoryWriterImpl()
{
delete fPackageNames;
}
status_t
RepositoryWriterImpl::Init(const char* fileName)
{
try {
fPackageNames = new PackageNameSet();
status_t result = fPackageNames->InitCheck();
if (result != B_OK)
return result;
return _Init(fileName);
} catch (status_t error) {
return error;
} catch (std::bad_alloc&) {
fListener->PrintError("Out of memory!\n");
return B_NO_MEMORY;
}
}
status_t
RepositoryWriterImpl::AddPackage(const BEntry& packageEntry)
{
try {
return _AddPackage(packageEntry);
} catch (status_t error) {
return error;
} catch (std::bad_alloc&) {
fListener->PrintError("Out of memory!\n");
return B_NO_MEMORY;
}
}
status_t
RepositoryWriterImpl::AddPackageInfo(const BPackageInfo& packageInfo)
{
try {
return _AddPackageInfo(packageInfo);
} catch (status_t error) {
return error;
} catch (std::bad_alloc&) {
fListener->PrintError("Out of memory!\n");
return B_NO_MEMORY;
}
}
status_t
RepositoryWriterImpl::Finish()
{
try {
return _Finish();
} catch (status_t error) {
return error;
} catch (std::bad_alloc&) {
fListener->PrintError("Out of memory!\n");
return B_NO_MEMORY;
}
}
status_t
RepositoryWriterImpl::_Init(const char* fileName)
{
status_t error = inherited::Init(NULL, false, fileName,
BPackageWriterParameters());
if (error != B_OK)
return error;
return InitHeapReader(sizeof(hpkg_repo_header));
}
status_t
RepositoryWriterImpl::_Finish()
{
hpkg_repo_header header;
// write repository info
uint64 infoLength;
status_t result = _WriteRepositoryInfo(header, infoLength);
if (result != B_OK)
return result;
// write package attributes
uint64 packagesLength;
_WritePackageAttributes(header, packagesLength);
// flush the heap writer
result = fHeapWriter->Finish();
if (result != B_OK)
return result;
uint64 compressedHeapSize = fHeapWriter->CompressedHeapSize();
uint64 totalSize = fHeapWriter->HeapOffset() + compressedHeapSize;
header.heap_compression = B_HOST_TO_BENDIAN_INT16(
Parameters().Compression());
header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize());
header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64(compressedHeapSize);
header.heap_size_uncompressed = B_HOST_TO_BENDIAN_INT64(
fHeapWriter->UncompressedHeapSize());
fListener->OnRepositoryDone(sizeof(header), infoLength,
fRepositoryInfo->LicenseNames().CountStrings(), fPackageCount,
packagesLength, totalSize);
// update the general header info and write the header
header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_REPO_MAGIC);
header.header_size = B_HOST_TO_BENDIAN_INT16((uint16)sizeof(header));
header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_VERSION);
header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
header.minor_version = B_HOST_TO_BENDIAN_INT16(B_HPKG_REPO_MINOR_VERSION);
RawWriteBuffer(&header, sizeof(header), 0);
SetFinished(true);
return B_OK;
}
status_t
RepositoryWriterImpl::_AddPackage(const BEntry& packageEntry)
{
status_t result = packageEntry.InitCheck();
if (result != B_OK) {
fListener->PrintError("entry not initialized!\n");
return result;
}
BPath packagePath;
if ((result = packageEntry.GetPath(&packagePath)) != B_OK) {
fListener->PrintError("can't get path for entry '%s'!\n",
packageEntry.Name());
return result;
}
BPackageReader packageReader(fListener);
if ((result = packageReader.Init(packagePath.Path())) != B_OK) {
fListener->PrintError("can't create package reader for '%s'!\n",
packagePath.Path());
return result;
}
fPackageInfo.Clear();
// parse package
PackageContentHandler contentHandler(fListener, &fPackageInfo,
packageReader.HeapReader(), fRepositoryInfo);
if ((result = packageReader.ParseContent(&contentHandler)) != B_OK)
return result;
// determine package's checksum
GeneralFileChecksumAccessor checksumAccessor(packageEntry);
BString checksum;
if ((result = checksumAccessor.GetChecksum(checksum)) != B_OK) {
fListener->PrintError("can't compute checksum of file '%s'!\n",
packagePath.Path());
return result;
}
fPackageInfo.SetChecksum(checksum);
// register package's attributes
if ((result = _RegisterCurrentPackageInfo()) != B_OK)
return result;
return B_OK;
}
status_t
RepositoryWriterImpl::_AddPackageInfo(const BPackageInfo& packageInfo)
{
fPackageInfo = packageInfo;
// register package's attributes
status_t result = _RegisterCurrentPackageInfo();
if (result != B_OK)
return result;
return B_OK;
}
status_t
RepositoryWriterImpl::_RegisterCurrentPackageInfo()
{
status_t result = fPackageInfo.InitCheck();
if (result != B_OK) {
fListener->PrintError("package %s has incomplete package-info!\n",
fPackageInfo.Name().String());
return result;
}
// reject package with a name that we've seen already
if (fPackageNames->Contains(fPackageInfo.Name())) {
fListener->PrintError("package %s has already been added!\n",
fPackageInfo.Name().String());
return B_NAME_IN_USE;
}
// all packages must have the same vendor as the repository
const BString& expectedVendor = fRepositoryInfo->Vendor();
if (fPackageInfo.Vendor().ICompare(expectedVendor) != 0) {
fListener->PrintError("package '%s' has unexpected vendor '%s' "
"(expected '%s')!\n", fPackageInfo.Name().String(),
fPackageInfo.Vendor().String(), expectedVendor.String());
return B_BAD_DATA;
}
// all packages must have an architecture that's compatible with the one
// used by the repository
BPackageArchitecture expectedArchitecture = fRepositoryInfo->Architecture();
if (fPackageInfo.Architecture() != expectedArchitecture
&& fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_ANY
&& fPackageInfo.Architecture() != B_PACKAGE_ARCHITECTURE_SOURCE) {
fListener->PrintError(
"package '%s' has non-matching architecture '%s' "
"(expected '%s', '%s', or '%s')!\n", fPackageInfo.Name().String(),
BPackageInfo::kArchitectureNames[fPackageInfo.Architecture()],
BPackageInfo::kArchitectureNames[expectedArchitecture],
BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_ANY],
BPackageInfo::kArchitectureNames[B_PACKAGE_ARCHITECTURE_SOURCE]);
return B_BAD_DATA;
}
if ((result = fPackageNames->Add(fPackageInfo.Name())) != B_OK)
return result;
PackageAttribute* packageAttribute = AddStringAttribute(
B_HPKG_ATTRIBUTE_ID_PACKAGE, fPackageInfo.Name(), PackageAttributes());
RegisterPackageInfo(packageAttribute->children, fPackageInfo);
fPackageCount++;
fListener->OnPackageAdded(fPackageInfo);
return B_OK;
}
status_t
RepositoryWriterImpl::_WriteRepositoryInfo(hpkg_repo_header& header,
uint64& _length)
{
// archive and flatten the repository info and write it
BMessage archive;
status_t result = fRepositoryInfo->Archive(&archive);
if (result != B_OK) {
fListener->PrintError("can't archive repository header!\n");
return result;
}
ssize_t flattenedSize = archive.FlattenedSize();
char buffer[flattenedSize];
if ((result = archive.Flatten(buffer, flattenedSize)) != B_OK) {
fListener->PrintError("can't flatten repository header!\n");
return result;
}
WriteBuffer(buffer, flattenedSize);
// notify listener
fListener->OnRepositoryInfoSectionDone(flattenedSize);
// update the header
header.info_length = B_HOST_TO_BENDIAN_INT32(flattenedSize);
_length = flattenedSize;
return B_OK;
}
void
RepositoryWriterImpl::_WritePackageAttributes(hpkg_repo_header& header,
uint64& _length)
{
// write the package attributes (zlib writer on top of a file writer)
uint64 startOffset = fHeapWriter->UncompressedHeapSize();
uint32 stringsLength;
uint32 stringsCount = WritePackageAttributes(PackageAttributes(),
stringsLength);
uint64 sectionSize = fHeapWriter->UncompressedHeapSize() - startOffset;
fListener->OnPackageAttributesSectionDone(stringsCount, sectionSize);
// update the header
header.packages_length = B_HOST_TO_BENDIAN_INT64(sectionSize);
header.packages_strings_count = B_HOST_TO_BENDIAN_INT64(stringsCount);
header.packages_strings_length = B_HOST_TO_BENDIAN_INT64(stringsLength);
_length = sectionSize;
}
} // namespace BPrivate
} // namespace BHPKG
} // namespace BPackageKit
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fPackageReader.