⛏️ index : haiku.git

/*
 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "UnpackingAttributeCookie.h"

#include <algorithm>
#include <new>

#include <package/hpkg/DataReader.h>
#include <package/hpkg/PackageData.h>
#include <package/hpkg/PackageDataReader.h>

#include <AutoDeleter.h>

#include "AttributeIndexer.h"
#include "AutoPackageAttributes.h"
#include "DebugSupport.h"
#include "Package.h"
#include "PackageNode.h"


using BPackageKit::BHPKG::BDataReader;
using BPackageKit::BHPKG::BBufferDataReader;
using BPackageKit::BHPKG::BFDDataReader;


static status_t
read_package_data(const PackageData& data, BDataReader* reader, off_t offset,
	void* buffer, size_t* bufferSize)
{
	// check the offset
	if (offset < 0 || (uint64)offset > data.UncompressedSize())
		return B_BAD_VALUE;

	// clamp the size
	size_t toRead = std::min((uint64)*bufferSize,
		data.UncompressedSize() - offset);

	// read
	if (toRead > 0) {
		status_t error = reader->ReadData(offset, buffer, toRead);
		if (error != B_OK)
			RETURN_ERROR(error);
	}

	*bufferSize = toRead;
	return B_OK;
}


UnpackingAttributeCookie::UnpackingAttributeCookie(PackageNode* packageNode,
	PackageNodeAttribute* attribute, int openMode)
	:
	fPackageNode(packageNode),
	fPackage(packageNode->GetPackage()),
	fAttribute(attribute),
	fOpenMode(openMode)
{
	fPackageNode->AcquireReference();
	fPackage->AcquireReference();
}


UnpackingAttributeCookie::~UnpackingAttributeCookie()
{
	fPackageNode->ReleaseReference();
	fPackage->ReleaseReference();
}


/*static*/ status_t
UnpackingAttributeCookie::Open(PackageNode* packageNode, const StringKey& name,
	int openMode, AttributeCookie*& _cookie)
{
	if (packageNode == NULL)
		return B_ENTRY_NOT_FOUND;

	// get the attribute
	PackageNodeAttribute* attribute = packageNode->FindAttribute(name);
	if (attribute == NULL) {
		// We don't know the attribute -- maybe it's an auto-generated one.
		return AutoPackageAttributes::OpenCookie(packageNode->GetPackage(),
			name, openMode, _cookie);
	}

	// allocate the cookie
	UnpackingAttributeCookie* cookie = new(std::nothrow)
		UnpackingAttributeCookie(packageNode, attribute, openMode);
	if (cookie == NULL)
		RETURN_ERROR(B_NO_MEMORY);

	_cookie = cookie;
	return B_OK;
}


status_t
UnpackingAttributeCookie::ReadAttribute(off_t offset, void* buffer,
	size_t* bufferSize)
{
	return ReadAttribute(fPackageNode, fAttribute, offset, buffer, bufferSize);
}


status_t
UnpackingAttributeCookie::ReadAttributeStat(struct stat* st)
{
	st->st_size = fAttribute->Data().UncompressedSize();
	st->st_type = fAttribute->Type();

	return B_OK;
}


/*static*/ status_t
UnpackingAttributeCookie::ReadAttribute(PackageNode* packageNode,
	PackageNodeAttribute* attribute, off_t offset, void* buffer,
	size_t* bufferSize)
{
	const PackageData& data = attribute->Data();
	if (data.IsEncodedInline()) {
		// inline data
		BBufferDataReader dataReader(data.InlineData(),
			data.UncompressedSize());
		return read_package_data(data, &dataReader, offset, buffer, bufferSize);
	}

	// data not inline -- open the package and let it create a data reader for
	// us
	Package* package = packageNode->GetPackage();
	int fd = package->Open();
	if (fd < 0)
		RETURN_ERROR(fd);
	PackageCloser packageCloser(package);

	BAbstractBufferedDataReader* reader;
	status_t error = package->CreateDataReader(data, reader);
	if (error != B_OK)
		return error;
	ObjectDeleter<BAbstractBufferedDataReader> readerDeleter(reader);

	return read_package_data(data, reader, offset, buffer, bufferSize);
}


/*static*/ status_t
UnpackingAttributeCookie::IndexAttribute(PackageNode* packageNode,
	AttributeIndexer* indexer)
{
	if (packageNode == NULL)
		return B_ENTRY_NOT_FOUND;

	// get the attribute
	PackageNodeAttribute* attribute = packageNode->FindAttribute(
		indexer->IndexName());
	if (attribute == NULL)
		return B_ENTRY_NOT_FOUND;

	// create the index cookie
	void* data;
	size_t toRead;
	status_t error = indexer->CreateCookie(packageNode, attribute,
		attribute->Type(), attribute->Data().UncompressedSize(), data, toRead);
	if (error != B_OK)
		return error;

	// read the attribute
	if (toRead > 0) {
		// The package must be open or otherwise reading the attribute data
		// may fail.
		int fd = packageNode->GetPackage()->Open();
		if (fd < 0) {
			indexer->DeleteCookie();
			return fd;
		}
		PackageCloser packageCloser(packageNode->GetPackage());

		error = ReadAttribute(packageNode, attribute, 0, data, &toRead);
		if (error != B_OK) {
			indexer->DeleteCookie();
			return error;
		}
	}

	attribute->SetIndexCookie(indexer->Cookie());

	return B_OK;
}