/*
* Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "Package.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <package/hpkg/ErrorOutput.h>
#include <package/hpkg/PackageDataReader.h>
#include <package/hpkg/PackageEntry.h>
#include <package/hpkg/PackageEntryAttribute.h>
#include <AutoDeleter.h>
#include <FdIO.h>
#include <package/hpkg/PackageFileHeapReader.h>
#include <package/hpkg/PackageReaderImpl.h>
#include <util/AutoLock.h>
#include "CachedDataReader.h"
#include "DebugSupport.h"
#include "PackageDirectory.h"
#include "PackageFile.h"
#include "PackagesDirectory.h"
#include "PackageSettings.h"
#include "PackageSymlink.h"
#include "Version.h"
#include "Volume.h"
using namespace BPackageKit;
using BPackageKit::BHPKG::BErrorOutput;
using BPackageKit::BHPKG::BFDDataReader;
using BPackageKit::BHPKG::BPackageInfoAttributeValue;
using BPackageKit::BHPKG::BPackageVersionData;
using BPackageKit::BHPKG::BPrivate::PackageFileHeapReader;
// current format version types
typedef BPackageKit::BHPKG::BPackageContentHandler BPackageContentHandler;
typedef BPackageKit::BHPKG::BPackageEntry BPackageEntry;
typedef BPackageKit::BHPKG::BPackageEntryAttribute BPackageEntryAttribute;
typedef BPackageKit::BHPKG::BPrivate::PackageReaderImpl PackageReaderImpl;
const char* const kArchitectureNames[B_PACKAGE_ARCHITECTURE_ENUM_COUNT] = {
"any",
"x86",
"x86_gcc2",
"source",
"x86_64",
"ppc",
"arm",
"m68k",
"sparc",
"arm64",
"riscv64"
};
// #pragma mark - LoaderErrorOutput
struct Package::LoaderErrorOutput : BErrorOutput {
LoaderErrorOutput(Package* package)
:
fPackage(package)
{
}
virtual void PrintErrorVarArgs(const char* format, va_list args)
{
ERRORV(format, args);
}
private:
Package* fPackage;
};
// #pragma mark - LoaderContentHandler
struct Package::LoaderContentHandler : BPackageContentHandler {
LoaderContentHandler(Package* package, const PackageSettings& settings)
:
fPackage(package),
fSettings(settings),
fSettingsItem(NULL),
fLastSettingsEntry(NULL),
fLastSettingsEntryEntry(NULL),
fErrorOccurred(false)
{
}
status_t Init()
{
return B_OK;
}
virtual status_t HandleEntry(BPackageEntry* entry)
{
if (fErrorOccurred
|| (fLastSettingsEntry != NULL
&& fLastSettingsEntry->IsBlackListed())) {
return B_OK;
}
PackageDirectory* parentDir = NULL;
if (entry->Parent() != NULL) {
parentDir = dynamic_cast<PackageDirectory*>(
(PackageNode*)entry->Parent()->UserToken());
if (parentDir == NULL)
RETURN_ERROR(B_BAD_DATA);
}
if (fSettingsItem != NULL
&& (parentDir == NULL
|| entry->Parent() == fLastSettingsEntryEntry)) {
PackageSettingsItem::Entry* settingsEntry
= fSettingsItem->FindEntry(fLastSettingsEntry, entry->Name());
if (settingsEntry != NULL) {
fLastSettingsEntry = settingsEntry;
fLastSettingsEntryEntry = entry;
if (fLastSettingsEntry->IsBlackListed())
return B_OK;
}
}
// get the file mode -- filter out write permissions
mode_t mode = entry->Mode() & ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH);
// create the package node
PackageNode* node;
if (S_ISREG(mode)) {
// file
node = new(std::nothrow) PackageFile(fPackage, mode,
PackageData(entry->Data()));
} else if (S_ISLNK(mode)) {
// symlink
String path;
if (!path.SetTo(entry->SymlinkPath()))
RETURN_ERROR(B_NO_MEMORY);
PackageSymlink* symlink = new(std::nothrow) PackageSymlink(
fPackage, mode);
if (symlink == NULL)
RETURN_ERROR(B_NO_MEMORY);
symlink->SetSymlinkPath(path);
node = symlink;
} else if (S_ISDIR(mode)) {
// directory
node = new(std::nothrow) PackageDirectory(fPackage, mode);
} else
RETURN_ERROR(B_BAD_DATA);
if (node == NULL)
RETURN_ERROR(B_NO_MEMORY);
BReference<PackageNode> nodeReference(node, true);
String entryName;
if (!entryName.SetTo(entry->Name()))
RETURN_ERROR(B_NO_MEMORY);
status_t error = node->Init(parentDir, entryName);
if (error != B_OK)
RETURN_ERROR(error);
node->SetModifiedTime(entry->ModifiedTime());
// add it to the parent directory
if (parentDir != NULL)
parentDir->AddChild(node);
else
fPackage->AddNode(node);
entry->SetUserToken(node);
return B_OK;
}
virtual status_t HandleEntryAttribute(BPackageEntry* entry,
BPackageEntryAttribute* attribute)
{
if (fErrorOccurred
|| (fLastSettingsEntry != NULL
&& fLastSettingsEntry->IsBlackListed())) {
return B_OK;
}
PackageNode* node = (PackageNode*)entry->UserToken();
String name;
if (!name.SetTo(attribute->Name()))
RETURN_ERROR(B_NO_MEMORY);
PackageNodeAttribute* nodeAttribute = new(std::nothrow)
PackageNodeAttribute(attribute->Type(),
PackageData(attribute->Data()));
if (nodeAttribute == NULL)
RETURN_ERROR(B_NO_MEMORY)
nodeAttribute->Init(name);
node->AddAttribute(nodeAttribute);
return B_OK;
}
virtual status_t HandleEntryDone(BPackageEntry* entry)
{
if (entry == fLastSettingsEntryEntry) {
fLastSettingsEntryEntry = entry->Parent();
fLastSettingsEntry = fLastSettingsEntry->Parent();
}
return B_OK;
}
virtual status_t HandlePackageAttribute(
const BPackageInfoAttributeValue& value)
{
switch (value.attributeID) {
case B_PACKAGE_INFO_NAME:
{
String name;
if (!name.SetTo(value.string))
return B_NO_MEMORY;
fPackage->SetName(name);
fSettingsItem = fSettings.PackageItemFor(fPackage->Name());
return B_OK;
}
case B_PACKAGE_INFO_INSTALL_PATH:
{
String path;
if (!path.SetTo(value.string))
return B_NO_MEMORY;
fPackage->SetInstallPath(path);
return B_OK;
}
case B_PACKAGE_INFO_VERSION:
{
::Version* version;
status_t error = Version::Create(value.version.major,
value.version.minor, value.version.micro,
value.version.preRelease, value.version.revision, version);
if (error != B_OK)
RETURN_ERROR(error);
fPackage->SetVersion(version);
break;
}
case B_PACKAGE_INFO_FLAGS:
fPackage->SetFlags(value.unsignedInt);
break;
case B_PACKAGE_INFO_ARCHITECTURE:
if (value.unsignedInt >= B_PACKAGE_ARCHITECTURE_ENUM_COUNT)
RETURN_ERROR(B_BAD_VALUE);
fPackage->SetArchitecture(
(BPackageArchitecture)value.unsignedInt);
break;
case B_PACKAGE_INFO_PROVIDES:
{
// create a version object, if a version is specified
::Version* version = NULL;
if (value.resolvable.haveVersion) {
const BPackageVersionData& versionInfo
= value.resolvable.version;
status_t error = Version::Create(versionInfo.major,
versionInfo.minor, versionInfo.micro,
versionInfo.preRelease, versionInfo.revision, version);
if (error != B_OK)
RETURN_ERROR(error);
}
ObjectDeleter< ::Version> versionDeleter(version);
// create a version object, if a compatible version is specified
::Version* compatibleVersion = NULL;
if (value.resolvable.haveCompatibleVersion) {
const BPackageVersionData& versionInfo
= value.resolvable.compatibleVersion;
status_t error = Version::Create(versionInfo.major,
versionInfo.minor, versionInfo.micro,
versionInfo.preRelease, versionInfo.revision,
compatibleVersion);
if (error != B_OK)
RETURN_ERROR(error);
}
ObjectDeleter< ::Version> compatibleVersionDeleter(
compatibleVersion);
// create the resolvable
Resolvable* resolvable = new(std::nothrow) Resolvable(fPackage);
if (resolvable == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<Resolvable> resolvableDeleter(resolvable);
status_t error = resolvable->Init(value.resolvable.name,
versionDeleter.Detach(), compatibleVersionDeleter.Detach());
if (error != B_OK)
RETURN_ERROR(error);
fPackage->AddResolvable(resolvableDeleter.Detach());
break;
}
case B_PACKAGE_INFO_REQUIRES:
{
// create the dependency
Dependency* dependency = new(std::nothrow) Dependency(fPackage);
if (dependency == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<Dependency> dependencyDeleter(dependency);
status_t error = dependency->Init(
value.resolvableExpression.name);
if (error != B_OK)
RETURN_ERROR(error);
// create a version object, if a version is specified
::Version* version = NULL;
if (value.resolvableExpression.haveOpAndVersion) {
const BPackageVersionData& versionInfo
= value.resolvableExpression.version;
status_t error = Version::Create(versionInfo.major,
versionInfo.minor, versionInfo.micro,
versionInfo.preRelease, versionInfo.revision, version);
if (error != B_OK)
RETURN_ERROR(error);
dependency->SetVersionRequirement(
value.resolvableExpression.op, version);
}
fPackage->AddDependency(dependencyDeleter.Detach());
break;
}
default:
break;
}
return B_OK;
}
virtual void HandleErrorOccurred()
{
fErrorOccurred = true;
}
private:
Package* fPackage;
const PackageSettings& fSettings;
const PackageSettingsItem* fSettingsItem;
PackageSettingsItem::Entry* fLastSettingsEntry;
const BPackageEntry* fLastSettingsEntryEntry;
bool fErrorOccurred;
};
// #pragma mark - HeapReader
struct Package::HeapReader {
virtual ~HeapReader()
{
}
virtual void UpdateFD(int fd) = 0;
virtual status_t CreateDataReader(const PackageData& data,
BAbstractBufferedDataReader*& _reader) = 0;
};
// #pragma mark - HeapReaderV2
struct Package::HeapReaderV2 : public HeapReader, public CachedDataReader,
private BErrorOutput, private BFdIO {
public:
HeapReaderV2()
:
fHeapReader(NULL)
{
}
~HeapReaderV2()
{
delete fHeapReader;
}
status_t Init(const PackageFileHeapReader* heapReader, int fd)
{
fHeapReader = heapReader->Clone();
if (fHeapReader == NULL)
return B_NO_MEMORY;
BFdIO::SetTo(fd, false);
fHeapReader->SetErrorOutput(this);
fHeapReader->SetFile(this);
status_t error = CachedDataReader::Init(fHeapReader,
fHeapReader->UncompressedHeapSize());
if (error != B_OK)
return error;
return B_OK;
}
virtual void UpdateFD(int fd)
{
BFdIO::SetTo(fd, false);
}
virtual status_t CreateDataReader(const PackageData& data,
BAbstractBufferedDataReader*& _reader)
{
return BPackageKit::BHPKG::BPackageDataReaderFactory()
.CreatePackageDataReader(this, data.DataV2(), _reader);
}
private:
// BErrorOutput
virtual void PrintErrorVarArgs(const char* format, va_list args)
{
ERRORV(format, args);
}
private:
PackageFileHeapReader* fHeapReader;
};
// #pragma mark - Package
struct Package::CachingPackageReader : public PackageReaderImpl {
CachingPackageReader(BErrorOutput* errorOutput)
:
PackageReaderImpl(errorOutput),
fCachedHeapReader(NULL),
fFD(-1)
{
}
~CachingPackageReader()
{
}
status_t Init(int fd, bool keepFD, uint32 flags)
{
fFD = fd;
return PackageReaderImpl::Init(fd, keepFD, flags);
}
virtual status_t CreateCachedHeapReader(
PackageFileHeapReader* rawHeapReader,
BAbstractBufferedDataReader*& _cachedReader)
{
fCachedHeapReader = new(std::nothrow) HeapReaderV2;
if (fCachedHeapReader == NULL)
RETURN_ERROR(B_NO_MEMORY);
status_t error = fCachedHeapReader->Init(rawHeapReader, fFD);
if (error != B_OK)
RETURN_ERROR(error);
_cachedReader = fCachedHeapReader;
return B_OK;
}
HeapReaderV2* DetachCachedHeapReader()
{
PackageFileHeapReader* rawHeapReader;
DetachHeapReader(rawHeapReader);
// We don't need the raw heap reader anymore, since the cached reader
// is not a wrapper around it, but completely independent from it.
delete rawHeapReader;
HeapReaderV2* cachedHeapReader = fCachedHeapReader;
fCachedHeapReader = NULL;
return cachedHeapReader;
}
private:
HeapReaderV2* fCachedHeapReader;
int fFD;
};
// #pragma mark - Package
Package::Package(::Volume* volume, PackagesDirectory* directory, dev_t deviceID,
ino_t nodeID)
:
fVolume(volume),
fPackagesDirectory(directory),
fFileName(),
fName(),
fInstallPath(),
fVersionedName(),
fVersion(NULL),
fFlags(0),
fArchitecture(B_PACKAGE_ARCHITECTURE_ENUM_COUNT),
fLinkDirectory(NULL),
fFD(-1),
fOpenCount(0),
fHeapReader(NULL),
fNodeID(nodeID),
fDeviceID(deviceID)
{
mutex_init(&fLock, "packagefs package");
fPackagesDirectory->AcquireReference();
}
Package::~Package()
{
delete fHeapReader;
while (PackageNode* node = fNodes.RemoveHead())
node->ReleaseReference();
while (Resolvable* resolvable = fResolvables.RemoveHead())
delete resolvable;
while (Dependency* dependency = fDependencies.RemoveHead())
delete dependency;
delete fVersion;
fPackagesDirectory->ReleaseReference();
mutex_destroy(&fLock);
}
status_t
Package::Init(const char* fileName)
{
if (!fFileName.SetTo(fileName))
RETURN_ERROR(B_NO_MEMORY);
return B_OK;
}
status_t
Package::Load(const PackageSettings& settings)
{
status_t error = _Load(settings);
if (error != B_OK)
return error;
if (!_InitVersionedName())
RETURN_ERROR(B_NO_MEMORY);
return B_OK;
}
void
Package::SetName(const String& name)
{
fName = name;
}
void
Package::SetInstallPath(const String& installPath)
{
fInstallPath = installPath;
}
void
Package::SetVersion(::Version* version)
{
if (fVersion != NULL)
delete fVersion;
fVersion = version;
}
const char*
Package::ArchitectureName() const
{
if (fArchitecture < 0
|| fArchitecture >= B_PACKAGE_ARCHITECTURE_ENUM_COUNT) {
return NULL;
}
return kArchitectureNames[fArchitecture];
}
void
Package::AddNode(PackageNode* node)
{
fNodes.Add(node);
node->AcquireReference();
}
void
Package::AddResolvable(Resolvable* resolvable)
{
fResolvables.Add(resolvable);
}
void
Package::AddDependency(Dependency* dependency)
{
fDependencies.Add(dependency);
}
int
Package::Open()
{
MutexLocker locker(fLock);
if (fOpenCount > 0) {
fOpenCount++;
return fFD;
}
// open the file
fFD = openat(fPackagesDirectory->DirectoryFD(), fFileName,
O_RDONLY | O_NOCACHE);
if (fFD < 0) {
ERROR("Failed to open package file \"%s\": %s\n", fFileName.Data(),
strerror(errno));
return errno;
}
// stat it to verify that it's still the same file
struct stat st;
if (fstat(fFD, &st) < 0) {
ERROR("Failed to stat package file \"%s\": %s\n", fFileName.Data(),
strerror(errno));
close(fFD);
fFD = -1;
return errno;
}
if (st.st_dev != fDeviceID || st.st_ino != fNodeID) {
close(fFD);
fFD = -1;
RETURN_ERROR(B_ENTRY_NOT_FOUND);
}
fOpenCount = 1;
if (fHeapReader != NULL)
fHeapReader->UpdateFD(fFD);
return fFD;
}
void
Package::Close()
{
MutexLocker locker(fLock);
if (fOpenCount == 0) {
ERROR("Package open count already 0!\n");
return;
}
if (--fOpenCount == 0) {
close(fFD);
fFD = -1;
if (fHeapReader != NULL)
fHeapReader->UpdateFD(fFD);
}
}
status_t
Package::CreateDataReader(const PackageData& data,
BAbstractBufferedDataReader*& _reader)
{
if (fHeapReader == NULL)
return B_BAD_VALUE;
return fHeapReader->CreateDataReader(data, _reader);
}
status_t
Package::_Load(const PackageSettings& settings)
{
// open package file
int fd = Open();
if (fd < 0)
RETURN_ERROR(fd);
PackageCloser packageCloser(this);
// initialize package reader
LoaderErrorOutput errorOutput(this);
// try current package file format version
{
CachingPackageReader packageReader(&errorOutput);
status_t error = packageReader.Init(fd, false,
BHPKG::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE);
if (error == B_OK) {
// parse content
LoaderContentHandler handler(this, settings);
error = handler.Init();
if (error != B_OK)
RETURN_ERROR(error);
error = packageReader.ParseContent(&handler);
if (error != B_OK)
RETURN_ERROR(error);
// get the heap reader
fHeapReader = packageReader.DetachCachedHeapReader();
return B_OK;
}
if (error != B_MISMATCHED_VALUES)
RETURN_ERROR(error);
}
// we don't support this package file format
RETURN_ERROR(B_BAD_DATA);
}
bool
Package::_InitVersionedName()
{
// compute the allocation size needed for the versioned name
size_t nameLength = strlen(fName);
size_t size = nameLength + 1;
if (fVersion != NULL) {
size += 1 + fVersion->ToString(NULL, 0);
// + 1 for the '-'
}
// allocate the name and compose it
char* name = (char*)malloc(size);
if (name == NULL)
return false;
MemoryDeleter nameDeleter(name);
memcpy(name, fName, nameLength + 1);
if (fVersion != NULL) {
name[nameLength] = '-';
fVersion->ToString(name + nameLength + 1, size - nameLength - 1);
}
return fVersionedName.SetTo(name);
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fFileNameHashTableNext.