Add support for adding/updating package entries

Add flags parameter to BPackageWriter::Init() (and the private
implementation classes) to indicate that an existing package file shall
be updated instead of created. Currently that always happens in-place.
This commit is contained in:
Ingo Weinhold 2011-07-03 04:26:54 +02:00
parent de9e64b235
commit 00bc8e9cbd
8 changed files with 881 additions and 54 deletions

View File

@ -157,6 +157,17 @@ enum {
};
// Writer Init() flags
enum {
B_HPKG_WRITER_UPDATE_PACKAGE = 0x01,
// update the package (don't truncate)
B_HPKG_WRITER_FORCE_ADD = 0x02,
// when updating a pre-existing entry, don't fail, but replace the
// entry, if possible (directories will be merged, but won't replace a
// non-directory)
};
} // namespace BHPKG
} // namespace BPackageKit

View File

@ -47,7 +47,7 @@ public:
BPackageWriterListener* listener);
~BPackageWriter();
status_t Init(const char* fileName);
status_t Init(const char* fileName, uint32 flags = 0);
status_t AddEntry(const char* fileName, int fd = -1);
status_t Finish();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
* Distributed under the terms of the MIT License.
*/
@ -15,6 +15,11 @@
#include <package/hpkg/WriterImplBase.h>
namespace BPrivate {
template<typename Value> class RangeArray;
}
namespace BPackageKit {
namespace BHPKG {
@ -37,19 +42,21 @@ public:
BPackageWriterListener* listener);
~PackageWriterImpl();
status_t Init(const char* fileName);
status_t Init(const char* fileName, uint32 flags);
status_t AddEntry(const char* fileName, int fd = -1);
status_t Finish();
private:
struct Attribute;
struct PackageContentHandler;
struct Entry;
struct SubPathAdder;
struct HeapAttributeOffsetter;
typedef DoublyLinkedList<Entry> EntryList;
private:
status_t _Init(const char* fileName);
status_t _Init(const char* fileName, uint32 flags);
status_t _Finish();
status_t _RegisterEntry(const char* fileName, int fd);
@ -58,6 +65,18 @@ private:
bool isImplicit);
status_t _CheckLicenses();
bool _IsEntryInPackage(const char* fileName);
void _UpdateReadPackageInfo();
void _UpdateCheckEntryCollisions();
void _UpdateCheckEntryCollisions(
Attribute* parentAttribute, int dirFD,
Entry* entry, const char* fileName,
char* pathBuffer);
void _CompactHeap();
void _MoveHeapChunk(off_t fromOffset, off_t toOffset,
off_t size);
void _AttributeRemoved(Attribute* attribute);
void _WriteTOC(hpkg_header& header);
int32 _WriteTOCSections(uint64& _stringsSize,
@ -74,6 +93,8 @@ private:
void _AddEntry(int dirFD, Entry* entry,
const char* fileName, char* pathBuffer);
void _AddDirectoryChildren(Entry* entry, int fd,
char* pathBuffer);
Attribute* _AddAttribute(BHPKGAttributeID attributeID,
const AttributeValue& value);
@ -105,6 +126,8 @@ private:
off_t fHeapOffset;
off_t fHeapEnd;
::BPrivate::RangeArray<off_t>* fHeapRangesToRemove;
void* fDataBuffer;
const size_t fDataBufferSize;

View File

@ -147,7 +147,8 @@ protected:
typedef DoublyLinkedList<PackageAttribute> PackageAttributeList;
protected:
status_t Init(const char* fileName, const char* type);
status_t Init(const char* fileName, const char* type,
uint32 flags);
void RegisterPackageInfo(
PackageAttributeList& attributeList,
@ -188,6 +189,7 @@ protected:
off_t offset);
inline int FD() const;
inline uint32 Flags() const;
inline const PackageAttributeList& PackageAttributes() const;
inline PackageAttributeList& PackageAttributes();
@ -211,6 +213,7 @@ private:
private:
BErrorOutput* fErrorOutput;
const char* fFileName;
uint32 fFlags;
int fFD;
bool fFinished;
@ -266,6 +269,13 @@ WriterImplBase::FD() const
}
inline uint32
WriterImplBase::Flags() const
{
return fFlags;
}
inline WriterImplBase::AbstractDataWriter*
WriterImplBase::DataWriter() const
{

View File

@ -30,12 +30,12 @@ BPackageWriter::~BPackageWriter()
status_t
BPackageWriter::Init(const char* fileName)
BPackageWriter::Init(const char* fileName, uint32 flags)
{
if (fImpl == NULL)
return B_NO_MEMORY;
return fImpl->Init(fileName);
return fImpl->Init(fileName, flags);
}

View File

@ -26,12 +26,21 @@
#include <fs_attr.h>
#include <Path.h>
#include <package/BlockBufferCacheNoLock.h>
#include <package/hpkg/PackageAttributeValue.h>
#include <package/hpkg/PackageContentHandler.h>
#include <package/hpkg/PackageData.h>
#include <package/hpkg/PackageDataReader.h>
#include <AutoDeleter.h>
#include <RangeArray.h>
#include <package/hpkg/HPKGDefsPrivate.h>
#include <package/hpkg/DataOutput.h>
#include <package/hpkg/DataReader.h>
#include <package/hpkg/PackageReaderImpl.h>
#include <package/hpkg/Stacker.h>
@ -79,11 +88,175 @@ struct PackageWriterImpl::Attribute
children.Add(child);
}
void RemoveChild(Attribute* child)
{
children.Remove(child);
}
void DeleteChildren()
{
while (Attribute* child = children.RemoveHead())
delete child;
}
Attribute* FindEntryChild(const char* fileName) const
{
for (DoublyLinkedList<Attribute>::ConstIterator it
= children.GetIterator(); Attribute* child = it.Next();) {
if (child->id != B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY)
continue;
if (child->value.type != B_HPKG_ATTRIBUTE_TYPE_STRING)
continue;
const char* childName = child->value.string->string;
if (strcmp(fileName, childName) == 0)
return child;
}
return NULL;
}
Attribute* FindEntryChild(const char* fileName, size_t nameLength) const
{
BString name(fileName, nameLength);
return (size_t)name.Length() == nameLength
? FindEntryChild(name) : NULL;
}
Attribute* FindNodeAttributeChild(const char* attributeName) const
{
for (DoublyLinkedList<Attribute>::ConstIterator it
= children.GetIterator(); Attribute* child = it.Next();) {
if (child->id != B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE)
continue;
if (child->value.type != B_HPKG_ATTRIBUTE_TYPE_STRING)
continue;
const char* childName = child->value.string->string;
if (strcmp(attributeName, childName) == 0)
return child;
}
return NULL;
}
Attribute* ChildWithID(BHPKGAttributeID id) const
{
for (DoublyLinkedList<Attribute>::ConstIterator it
= children.GetIterator(); Attribute* child = it.Next();) {
if (child->id == id)
return child;
}
return NULL;
}
};
// #pragma mark - PackageContentHandler
struct PackageWriterImpl::PackageContentHandler
: BLowLevelPackageContentHandler {
PackageContentHandler(Attribute* rootAttribute, BErrorOutput* errorOutput,
StringCache& stringCache, uint64 heapOffset)
:
fErrorOutput(errorOutput),
fStringCache(stringCache),
fRootAttribute(rootAttribute),
fHeapOffset(heapOffset),
fErrorOccurred(false)
{
}
static const char* AttributeNameForID(uint8 id)
{
return BLowLevelPackageContentHandler::AttributeNameForID(id);
}
virtual status_t HandleSectionStart(BHPKGPackageSectionID sectionID,
bool& _handleSection)
{
// we're only interested in the TOC
_handleSection = sectionID == B_HPKG_SECTION_PACKAGE_TOC;
return B_OK;
}
virtual status_t HandleSectionEnd(BHPKGPackageSectionID sectionID)
{
return B_OK;
}
virtual status_t HandleAttribute(BHPKGAttributeID attributeID,
const BPackageAttributeValue& value, void* parentToken, void*& _token)
{
if (fErrorOccurred)
return B_OK;
Attribute* parentAttribute = parentToken != NULL
? (Attribute*)parentToken : fRootAttribute;
Attribute* attribute = new Attribute(attributeID);
parentAttribute->AddChild(attribute);
switch (value.type) {
case B_HPKG_ATTRIBUTE_TYPE_INT:
attribute->value.SetTo(value.signedInt);
break;
case B_HPKG_ATTRIBUTE_TYPE_UINT:
attribute->value.SetTo(value.unsignedInt);
break;
case B_HPKG_ATTRIBUTE_TYPE_STRING:
{
CachedString* string = fStringCache.Get(value.string);
if (string == NULL)
throw std::bad_alloc();
attribute->value.SetTo(string);
break;
}
case B_HPKG_ATTRIBUTE_TYPE_RAW:
if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
attribute->value.SetToData(value.data.size,
value.data.offset - fHeapOffset);
} else if (value.encoding
== B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
attribute->value.SetToData(value.data.size, value.data.raw);
} else {
fErrorOutput->PrintError("Invalid attribute value encoding "
"%d (attribute %d)\n", value.encoding, attributeID);
return B_BAD_DATA;
}
break;
case B_HPKG_ATTRIBUTE_TYPE_INVALID:
default:
fErrorOutput->PrintError("Invalid attribute value type %d "
"(attribute %d)\n", value.type, attributeID);
return B_BAD_DATA;
}
_token = attribute;
return B_OK;
}
virtual status_t HandleAttributeDone(BHPKGAttributeID attributeID,
const BPackageAttributeValue& value, void* parentToken, void* token)
{
return B_OK;
}
virtual void HandleErrorOccurred()
{
fErrorOccurred = true;
}
private:
BErrorOutput* fErrorOutput;
StringCache& fStringCache;
Attribute* fRootAttribute;
uint64 fHeapOffset;
bool fErrorOccurred;
};
@ -223,6 +396,40 @@ private:
};
struct PackageWriterImpl::HeapAttributeOffsetter {
HeapAttributeOffsetter(const RangeArray<off_t>& ranges,
const Array<off_t>& deltas)
:
fRanges(ranges),
fDeltas(deltas)
{
}
void ProcessAttribute(Attribute* attribute)
{
// If the attribute refers to a heap value, adjust it
AttributeValue& value = attribute->value;
if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW
&& value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
off_t delta = fDeltas[fRanges.InsertionIndex(value.data.offset)];
value.data.offset -= delta;
}
// recurse
for (DoublyLinkedList<Attribute>::Iterator it
= attribute->children.GetIterator();
Attribute* child = it.Next();) {
ProcessAttribute(child);
}
}
private:
const RangeArray<off_t>& fRanges;
const Array<off_t>& fDeltas;
};
// #pragma mark - PackageWriterImpl (Inline Methods)
@ -243,6 +450,7 @@ PackageWriterImpl::PackageWriterImpl(BPackageWriterListener* listener)
:
inherited(listener),
fListener(listener),
fHeapRangesToRemove(NULL),
fDataBuffer(NULL),
fDataBufferSize(2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB),
fRootEntry(NULL),
@ -263,10 +471,10 @@ PackageWriterImpl::~PackageWriterImpl()
status_t
PackageWriterImpl::Init(const char* fileName)
PackageWriterImpl::Init(const char* fileName, uint32 flags)
{
try {
return _Init(fileName);
return _Init(fileName, flags);
} catch (status_t error) {
return error;
} catch (std::bad_alloc) {
@ -343,6 +551,16 @@ status_t
PackageWriterImpl::Finish()
{
try {
RangeArray<off_t> heapRangesToRemove;
fHeapRangesToRemove = &heapRangesToRemove;
if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
_UpdateCheckEntryCollisions();
if (fPackageInfo.InitCheck() != B_OK)
_UpdateReadPackageInfo();
}
if (fPackageInfo.InitCheck() != B_OK) {
fListener->PrintError("No package-info file found (%s)!\n",
B_HPKG_PACKAGE_INFO_FILE_NAME);
@ -355,6 +573,11 @@ PackageWriterImpl::Finish()
if (result != B_OK)
return result;
if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0)
_CompactHeap();
fHeapRangesToRemove = NULL;
return _Finish();
} catch (status_t error) {
return error;
@ -366,9 +589,9 @@ PackageWriterImpl::Finish()
status_t
PackageWriterImpl::_Init(const char* fileName)
PackageWriterImpl::_Init(const char* fileName, uint32 flags)
{
status_t result = inherited::Init(fileName, "package");
status_t result = inherited::Init(fileName, "package", flags);
if (result != B_OK)
return result;
@ -388,6 +611,29 @@ PackageWriterImpl::_Init(const char* fileName)
fHeapOffset = fHeapEnd = sizeof(hpkg_header);
fTopAttribute = fRootAttribute;
// in update mode, parse the TOC
if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
PackageReaderImpl packageReader(fListener);
result = packageReader.Init(FD(), false);
if (result != B_OK)
return result;
fHeapOffset = packageReader.HeapOffset();
fHeapEnd = fHeapOffset + packageReader.HeapSize();
PackageContentHandler handler(fRootAttribute, fListener, fStringCache,
fHeapOffset);
result = packageReader.ParseContent(&handler);
if (result != B_OK)
return result;
if ((uint64)fHeapOffset > packageReader.HeapOffset()) {
fListener->PrintError("Unexpected heap offset in package file.\n");
return B_BAD_DATA;
}
}
return B_OK;
}
@ -412,7 +658,6 @@ PackageWriterImpl::_CheckLicenses()
}
BDirectory systemLicenseDir(systemLicensePath.Path());
BDirectory packageLicenseDir("./data/licenses");
const BObjectList<BString>& licenseList = fPackageInfo.LicenseList();
for (int i = 0; i < licenseList.CountItems(); ++i) {
@ -425,8 +670,10 @@ PackageWriterImpl::_CheckLicenses()
continue;
// license is not a system license, so it must be contained in package
if (packageLicenseDir.FindEntry(licenseName.String(),
&license) != B_OK) {
BString licensePath("data/licenses/");
licensePath << licenseName;
if (!_IsEntryInPackage(licensePath)) {
fListener->PrintError("License '%s' isn't contained in package!\n",
licenseName.String());
return B_BAD_DATA;
@ -437,6 +684,507 @@ PackageWriterImpl::_CheckLicenses()
}
bool
PackageWriterImpl::_IsEntryInPackage(const char* fileName)
{
const char* originalFileName = fileName;
// find the closest ancestor of the entry that is in the added entries
bool added = false;
Entry* entry = fRootEntry;
while (entry != NULL) {
if (!entry->IsImplicit()) {
added = true;
break;
}
if (*fileName == '\0')
break;
const char* nextSlash = strchr(fileName, '/');
if (nextSlash == NULL) {
// no slash, just the file name
size_t length = strlen(fileName);
entry = entry->GetChild(fileName, length);
fileName += length;
continue;
}
// find the start of the next component, skipping slashes
const char* nextComponent = nextSlash + 1;
while (*nextComponent == '/')
nextComponent++;
entry = entry->GetChild(fileName, nextSlash - fileName);
fileName = nextComponent;
}
if (added) {
// the entry itself or one of its ancestors has been added to the
// package explicitly -- stat it, to see, if it exists
struct stat st;
if (entry->FD() >= 0) {
if (fstatat(entry->FD(), *fileName != '\0' ? fileName : NULL, &st,
AT_SYMLINK_NOFOLLOW) == 0) {
return true;
}
} else {
if (lstat(originalFileName, &st) == 0)
return true;
}
}
// In update mode the entry might already be in the package.
Attribute* attribute = fRootAttribute;
fileName = originalFileName;
while (attribute != NULL) {
if (*fileName == '\0')
return true;
const char* nextSlash = strchr(fileName, '/');
if (nextSlash == NULL) {
// no slash, just the file name
return attribute->FindEntryChild(fileName) != NULL;
}
// find the start of the next component, skipping slashes
const char* nextComponent = nextSlash + 1;
while (*nextComponent == '/')
nextComponent++;
attribute = attribute->FindEntryChild(fileName, nextSlash - fileName);
fileName = nextComponent;
}
return false;
}
void
PackageWriterImpl::_UpdateReadPackageInfo()
{
// get the .PackageInfo entry attribute
Attribute* attribute = fRootAttribute->FindEntryChild(
B_HPKG_PACKAGE_INFO_FILE_NAME);
if (attribute == NULL) {
fListener->PrintError("No %s in package file.\n",
B_HPKG_PACKAGE_INFO_FILE_NAME);
throw status_t(B_BAD_DATA);
}
// get the data attribute
Attribute* dataAttribute = attribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_DATA);
if (dataAttribute == NULL) {
fListener->PrintError("%s entry in package file doesn't have data.\n",
B_HPKG_PACKAGE_INFO_FILE_NAME);
throw status_t(B_BAD_DATA);
}
AttributeValue& value = dataAttribute->value;
if (value.type != B_HPKG_ATTRIBUTE_TYPE_RAW) {
fListener->PrintError("%s entry in package file has an invalid data "
"attribute (not of type raw).\n", B_HPKG_PACKAGE_INFO_FILE_NAME);
throw status_t(B_BAD_DATA);
}
BPackageData data;
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 + fHeapOffset);
// get the compression
uint8 compression = B_HPKG_DEFAULT_DATA_COMPRESSION;
if (Attribute* compressionAttribute = dataAttribute->ChildWithID(
B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION)) {
if (compressionAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) {
fListener->PrintError("%s entry in package file has an invalid "
"data compression attribute (not of type uint).\n",
B_HPKG_PACKAGE_INFO_FILE_NAME);
throw status_t(B_BAD_DATA);
}
compression = compressionAttribute->value.unsignedInt;
}
data.SetCompression(compression);
// get the size
uint64 size;
Attribute* sizeAttribute = dataAttribute->ChildWithID(
B_HPKG_ATTRIBUTE_ID_DATA_SIZE);
if (sizeAttribute == NULL) {
size = value.data.size;
} else if (sizeAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) {
fListener->PrintError("%s entry in package file has an invalid data "
"size attribute (not of type uint).\n",
B_HPKG_PACKAGE_INFO_FILE_NAME);
throw status_t(B_BAD_DATA);
} else
size = sizeAttribute->value.unsignedInt;
data.SetUncompressedSize(size);
// get the chunk size
uint64 chunkSize = compression == B_HPKG_COMPRESSION_ZLIB
? B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB : 0;
if (Attribute* chunkSizeAttribute = dataAttribute->ChildWithID(
B_HPKG_ATTRIBUTE_ID_DATA_CHUNK_SIZE)) {
if (chunkSizeAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) {
fListener->PrintError("%s entry in package file has an invalid "
"data chunk size attribute (not of type uint).\n",
B_HPKG_PACKAGE_INFO_FILE_NAME);
throw status_t(B_BAD_DATA);
}
chunkSize = chunkSizeAttribute->value.unsignedInt;
}
data.SetChunkSize(chunkSize);
// read the value into a string
BString valueString;
char* valueBuffer = valueString.LockBuffer(size);
if (valueBuffer == NULL)
throw std::bad_alloc();
if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
// data encoded inline -- just copy to buffer
if (size != value.data.size) {
fListener->PrintError("%s entry in package file has an invalid "
"data attribute (mismatching size).\n",
B_HPKG_PACKAGE_INFO_FILE_NAME);
throw status_t(B_BAD_DATA);
}
memcpy(valueBuffer, value.data.raw, value.data.size);
} else {
// data on heap -- read from there
BBlockBufferCacheNoLock bufferCache(16 * 1024, 1);
status_t error = bufferCache.Init();
if (error != B_OK) {
fListener->PrintError("Failed to initialize buffer cache: %s\n",
strerror(error));
throw status_t(error);
}
// create a BPackageDataReader
BFDDataReader packageFileReader(FD());
BPackageDataReader* reader;
error = BPackageDataReaderFactory(&bufferCache)
.CreatePackageDataReader(&packageFileReader, data, reader);
if (error != B_OK) {
fListener->PrintError("Failed to create package data reader: %s\n",
strerror(error));
throw status_t(error);
}
ObjectDeleter<BPackageDataReader> readerDeleter(reader);
// read the data
error = reader->ReadData(0, valueBuffer, size);
if (error != B_OK) {
fListener->PrintError("Failed to read data of %s entry in package "
"file: %s\n", B_HPKG_PACKAGE_INFO_FILE_NAME, strerror(error));
throw status_t(error);
}
}
valueString.UnlockBuffer();
// parse the package info
status_t error = fPackageInfo.ReadFromConfigString(valueString);
if (error != B_OK) {
fListener->PrintError("Failed to parse package info data from package "
"file: %s\n", strerror(error));
throw status_t(error);
}
}
void
PackageWriterImpl::_UpdateCheckEntryCollisions()
{
for (EntryList::ConstIterator it = fRootEntry->ChildIterator();
Entry* entry = it.Next();) {
char pathBuffer[B_PATH_NAME_LENGTH];
pathBuffer[0] = '\0';
_UpdateCheckEntryCollisions(fRootAttribute, AT_FDCWD, entry,
entry->Name(), pathBuffer);
}
}
void
PackageWriterImpl::_UpdateCheckEntryCollisions(Attribute* parentAttribute,
int dirFD, Entry* entry, const char* fileName, char* pathBuffer)
{
bool isImplicitEntry = entry != NULL && entry->IsImplicit();
SubPathAdder pathAdder(fListener, pathBuffer, fileName);
// Check wether there's an entry attribute for this entry. If not, we can
// ignore this entry.
Attribute* entryAttribute = parentAttribute->FindEntryChild(fileName);
if (entryAttribute == NULL)
return;
// open the node
int fd;
FileDescriptorCloser fdCloser;
if (entry != NULL && entry->FD() >= 0) {
// a file descriptor is already given -- use that
fd = entry->FD();
} else {
fd = openat(dirFD, fileName,
O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE));
if (fd < 0) {
fListener->PrintError("Failed to open entry \"%s\": %s\n",
pathBuffer, strerror(errno));
throw status_t(errno);
}
fdCloser.SetTo(fd);
}
// stat the node
struct stat st;
if (fstat(fd, &st) < 0) {
fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer,
strerror(errno));
throw status_t(errno);
}
// implicit entries must be directories
if (isImplicitEntry && !S_ISDIR(st.st_mode)) {
fListener->PrintError("Non-leaf path component \"%s\" is not a "
"directory.\n", pathBuffer);
throw status_t(B_BAD_VALUE);
}
// get the pre-existing node's file type
uint32 preExistingFileType = B_HPKG_DEFAULT_FILE_TYPE;
if (Attribute* fileTypeAttribute
= entryAttribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_FILE_TYPE)) {
if (fileTypeAttribute->value.type == B_HPKG_ATTRIBUTE_TYPE_UINT)
preExistingFileType = fileTypeAttribute->value.unsignedInt;
}
// Compare the node type with that of the pre-existing one.
if (!S_ISDIR(st.st_mode)) {
// the pre-existing must not a directory either -- we'll remove it
if (preExistingFileType == B_HPKG_FILE_TYPE_DIRECTORY) {
fListener->PrintError("Specified file \"%s\" clashes with an "
"archived directory.\n", pathBuffer);
throw status_t(B_BAD_VALUE);
}
if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) {
fListener->PrintError("Specified file \"%s\" clashes with an "
"archived file.\n", pathBuffer);
throw status_t(B_FILE_EXISTS);
}
parentAttribute->RemoveChild(entryAttribute);
_AttributeRemoved(entryAttribute);
return;
}
// the pre-existing entry needs to be a directory too -- we will merge
if (preExistingFileType != B_HPKG_FILE_TYPE_DIRECTORY) {
fListener->PrintError("Specified directory \"%s\" clashes with an "
"archived non-directory.\n", pathBuffer);
throw status_t(B_BAD_VALUE);
}
// directory -- recursively add children
if (isImplicitEntry) {
// this is an implicit entry -- just check the child entries
for (EntryList::ConstIterator it = entry->ChildIterator();
Entry* child = it.Next();) {
_UpdateCheckEntryCollisions(entryAttribute, fd, child,
child->Name(), pathBuffer);
}
} else {
// explicitly specified directory -- we need to read the directory
// first we check for colliding node attributes, though
if (DIR* attrDir = fs_fopen_attr_dir(fd)) {
CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir);
while (dirent* entry = fs_read_attr_dir(attrDir)) {
attr_info attrInfo;
if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) {
fListener->PrintError(
"Failed to stat attribute \"%s\" of directory \"%s\": "
"%s\n", entry->d_name, pathBuffer, strerror(errno));
throw status_t(errno);
}
// check whether the attribute exists
Attribute* attributeAttribute
= entryAttribute->FindNodeAttributeChild(entry->d_name);
if (attributeAttribute == NULL)
continue;
if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) {
fListener->PrintError("Attribute \"%s\" of specified "
"directory \"%s\" clashes with an archived "
"attribute.\n", pathBuffer);
throw status_t(B_FILE_EXISTS);
}
// remove it
entryAttribute->RemoveChild(attributeAttribute);
_AttributeRemoved(attributeAttribute);
}
}
// we need to clone the directory FD for fdopendir()
int clonedFD = dup(fd);
if (clonedFD < 0) {
fListener->PrintError(
"Failed to dup() directory FD: %s\n", strerror(errno));
throw status_t(errno);
}
DIR* dir = fdopendir(clonedFD);
if (dir == NULL) {
fListener->PrintError(
"Failed to open directory \"%s\": %s\n", pathBuffer,
strerror(errno));
close(clonedFD);
throw status_t(errno);
}
CObjectDeleter<DIR, int> dirCloser(dir, closedir);
while (dirent* entry = readdir(dir)) {
// skip "." and ".."
if (strcmp(entry->d_name, ".") == 0
|| strcmp(entry->d_name, "..") == 0) {
continue;
}
_AddEntry(fd, NULL, entry->d_name, pathBuffer);
_UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name,
pathBuffer);
}
}
}
void
PackageWriterImpl::_CompactHeap()
{
int32 count = fHeapRangesToRemove->CountRanges();
if (count == 0)
return;
// compute the move deltas for the ranges
Array<off_t> deltas;
off_t delta = 0;
for (int32 i = 0; i < count; i++) {
if (!deltas.Add(delta))
throw std::bad_alloc();
delta += fHeapRangesToRemove->RangeAt(i).size;
}
if (!deltas.Add(delta))
throw std::bad_alloc();
// offset the attributes
HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute(
fRootAttribute);
// move the heap chunks in the file around
off_t chunkOffset = fHeapOffset;
delta = 0;
for (int32 i = 0; i < count; i++) {
const Range<off_t>& range = fHeapRangesToRemove->RangeAt(i);
if (delta > 0 && chunkOffset < range.offset) {
// move chunk
_MoveHeapChunk(chunkOffset, chunkOffset - delta,
range.offset - chunkOffset);
}
chunkOffset = range.EndOffset();
delta += range.size;
}
// move the final chunk
if (delta > 0 && chunkOffset < fHeapEnd) {
_MoveHeapChunk(chunkOffset, chunkOffset - delta,
fHeapEnd - chunkOffset);
}
fHeapEnd -= delta;
}
void
PackageWriterImpl::_MoveHeapChunk(off_t fromOffset, off_t toOffset, off_t size)
{
// convert heap offsets to file offsets
fromOffset += fHeapOffset;
toOffset += fHeapOffset;
while (size > 0) {
size_t toCopy = std::min(size, (off_t)fDataBufferSize);
// read data into buffer
ssize_t bytesRead = read_pos(FD(), fromOffset, fDataBuffer, toCopy);
if (bytesRead < 0) {
fListener->PrintError("Failed to read from package file: %s\n",
strerror(errno));
throw status_t(errno);
}
if ((size_t)bytesRead < toCopy) {
fListener->PrintError("Failed to read from package file.\n");
throw status_t(B_IO_ERROR);
}
// write data to target offset
ssize_t bytesWritten = write_pos(FD(), toOffset, fDataBuffer, toCopy);
if (bytesWritten < 0) {
fListener->PrintError("Failed to write to package file: %s\n",
strerror(errno));
throw status_t(errno);
}
if ((size_t)bytesWritten < toCopy) {
fListener->PrintError("Failed to write to package file.\n");
throw status_t(B_IO_ERROR);
}
fromOffset += toCopy;
toOffset += toCopy;
size -= toCopy;
}
}
void
PackageWriterImpl::_AttributeRemoved(Attribute* attribute)
{
AttributeValue& value = attribute->value;
if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW
&& value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
if (!fHeapRangesToRemove->AddRange(value.data.offset, value.data.size))
throw std::bad_alloc();
}
for (DoublyLinkedList<Attribute>::Iterator it
= attribute->children.GetIterator();
Attribute* child = it.Next();) {
_AttributeRemoved(child);
}
}
status_t
PackageWriterImpl::_Finish()
{
@ -458,6 +1206,16 @@ PackageWriterImpl::_Finish()
off_t totalSize = fHeapEnd;
// Truncate the file to the size it is supposed to have. In update mode, it
// can be greater when one or more files are shrunk. In creation mode, when
// writing compressed TOC or package attributes yields a larger size than
// uncompressed, the file size may also be greater than it should be.
if (ftruncate(FD(), totalSize) != 0) {
fListener->PrintError("Failed to truncate package file to new "
"size: %s\n", strerror(errno));
return errno;
}
fListener->OnPackageSizeInfo(fHeapOffset, heapSize,
B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed),
B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed),
@ -761,6 +1519,18 @@ PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName,
throw status_t(B_BAD_VALUE);
}
// In update mode we don't need to add an entry attribute for an implicit
// directory, if there already is one.
if (isImplicitEntry && (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
Attribute* entryAttribute = fTopAttribute->FindEntryChild(fileName);
if (entryAttribute != NULL) {
Stacker<Attribute> entryAttributeStacker(fTopAttribute,
entryAttribute);
_AddDirectoryChildren(entry, fd, pathBuffer);
return;
}
}
// check/translate the node type
uint8 fileType;
uint32 defaultPermissions;
@ -856,42 +1626,48 @@ PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName,
}
}
if (S_ISDIR(st.st_mode)) {
// directory -- recursively add children
if (isImplicitEntry) {
// this is an implicit entry -- just add it's children
for (EntryList::ConstIterator it = entry->ChildIterator();
Entry* child = it.Next();) {
_AddEntry(fd, child, child->Name(), pathBuffer);
}
} else {
// we need to clone the directory FD for fdopendir()
int clonedFD = dup(fd);
if (clonedFD < 0) {
fListener->PrintError(
"Failed to dup() directory FD: %s\n", strerror(errno));
throw status_t(errno);
if (S_ISDIR(st.st_mode))
_AddDirectoryChildren(entry, fd, pathBuffer);
}
void
PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer)
{
// directory -- recursively add children
if (entry != NULL && entry->IsImplicit()) {
// this is an implicit entry -- just add it's children
for (EntryList::ConstIterator it = entry->ChildIterator();
Entry* child = it.Next();) {
_AddEntry(fd, child, child->Name(), pathBuffer);
}
} else {
// we need to clone the directory FD for fdopendir()
int clonedFD = dup(fd);
if (clonedFD < 0) {
fListener->PrintError(
"Failed to dup() directory FD: %s\n", strerror(errno));
throw status_t(errno);
}
DIR* dir = fdopendir(clonedFD);
if (dir == NULL) {
fListener->PrintError(
"Failed to open directory \"%s\": %s\n", pathBuffer,
strerror(errno));
close(clonedFD);
throw status_t(errno);
}
CObjectDeleter<DIR, int> dirCloser(dir, closedir);
while (dirent* entry = readdir(dir)) {
// skip "." and ".."
if (strcmp(entry->d_name, ".") == 0
|| strcmp(entry->d_name, "..") == 0) {
continue;
}
DIR* dir = fdopendir(clonedFD);
if (dir == NULL) {
fListener->PrintError(
"Failed to open directory \"%s\": %s\n", pathBuffer,
strerror(errno));
close(clonedFD);
throw status_t(errno);
}
CObjectDeleter<DIR, int> dirCloser(dir, closedir);
while (dirent* entry = readdir(dir)) {
// skip "." and ".."
if (strcmp(entry->d_name, ".") == 0
|| strcmp(entry->d_name, "..") == 0) {
continue;
}
_AddEntry(fd, NULL, entry->d_name, pathBuffer);
}
_AddEntry(fd, NULL, entry->d_name, pathBuffer);
}
}
}

View File

@ -328,7 +328,7 @@ RepositoryWriterImpl::Finish()
status_t
RepositoryWriterImpl::_Init(const char* fileName)
{
return inherited::Init(fileName, "repository");
return inherited::Init(fileName, "repository", 0);
}

View File

@ -314,6 +314,7 @@ WriterImplBase::WriterImplBase(BErrorOutput* errorOutput)
:
fErrorOutput(errorOutput),
fFileName(NULL),
fFlags(0),
fFD(-1),
fFinished(false),
fDataWriter(NULL)
@ -326,20 +327,25 @@ WriterImplBase::~WriterImplBase()
if (fFD >= 0)
close(fFD);
if (!fFinished && fFileName != NULL)
if (!fFinished && fFileName != NULL
&& (fFlags & B_HPKG_WRITER_UPDATE_PACKAGE) == 0) {
unlink(fFileName);
}
}
status_t
WriterImplBase::Init(const char* fileName, const char* type)
WriterImplBase::Init(const char* fileName, const char* type, uint32 flags)
{
if (fPackageStringCache.Init() != B_OK)
throw std::bad_alloc();
// open file
fFD = open(fileName, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
// open file (don't truncate in update mode)
int openMode = O_RDWR;
if ((flags & B_HPKG_WRITER_UPDATE_PACKAGE) == 0)
openMode |= O_CREAT | O_TRUNC;
fFD = open(fileName, openMode, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fFD < 0) {
fErrorOutput->PrintError("Failed to open %s file \"%s\": %s\n", type,
fileName, strerror(errno));
@ -347,6 +353,7 @@ WriterImplBase::Init(const char* fileName, const char* type)
}
fFileName = fileName;
fFlags = flags;
return B_OK;
}