1/*
2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <package/hpkg/PackageWriterImpl.h>
9
10#include <dirent.h>
11#include <errno.h>
12#include <fcntl.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/stat.h>
17#include <unistd.h>
18
19#include <algorithm>
20#include <new>
21
22#include <ByteOrder.h>
23#include <Directory.h>
24#include <Entry.h>
25#include <FindDirectory.h>
26#include <fs_attr.h>
27#include <Path.h>
28
29#include <package/hpkg/BlockBufferPoolNoLock.h>
30#include <package/hpkg/PackageAttributeValue.h>
31#include <package/hpkg/PackageContentHandler.h>
32#include <package/hpkg/PackageData.h>
33#include <package/hpkg/PackageDataReader.h>
34
35#include <AutoDeleter.h>
36#include <AutoDeleterPosix.h>
37#include <RangeArray.h>
38
39#include <package/hpkg/HPKGDefsPrivate.h>
40
41#include <package/hpkg/DataReader.h>
42#include <package/hpkg/PackageFileHeapReader.h>
43#include <package/hpkg/PackageFileHeapWriter.h>
44#include <package/hpkg/PackageReaderImpl.h>
45#include <package/hpkg/Stacker.h>
46
47
48using BPrivate::FileDescriptorCloser;
49
50
51static const char* const kPublicDomainLicenseName = "Public Domain";
52
53
54#include <typeinfo>
55
56namespace BPackageKit {
57
58namespace BHPKG {
59
60namespace BPrivate {
61
62
63// #pragma mark - Attributes
64
65
66struct PackageWriterImpl::Attribute
67	: public DoublyLinkedListLinkImpl<Attribute> {
68	BHPKGAttributeID			id;
69	AttributeValue				value;
70	DoublyLinkedList<Attribute>	children;
71
72	Attribute(BHPKGAttributeID id_ = B_HPKG_ATTRIBUTE_ID_ENUM_COUNT)
73		:
74		id(id_)
75	{
76	}
77
78	~Attribute()
79	{
80		DeleteChildren();
81	}
82
83	void AddChild(Attribute* child)
84	{
85		children.Add(child);
86	}
87
88	void RemoveChild(Attribute* child)
89	{
90		children.Remove(child);
91	}
92
93	void DeleteChildren()
94	{
95		while (Attribute* child = children.RemoveHead())
96			delete child;
97	}
98
99	Attribute* FindEntryChild(const char* fileName) const
100	{
101		for (DoublyLinkedList<Attribute>::ConstIterator it
102				= children.GetIterator(); Attribute* child = it.Next();) {
103			if (child->id != B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY)
104				continue;
105			if (child->value.type != B_HPKG_ATTRIBUTE_TYPE_STRING)
106				continue;
107			const char* childName = child->value.string->string;
108			if (strcmp(fileName, childName) == 0)
109				return child;
110		}
111
112		return NULL;
113	}
114
115	Attribute* FindEntryChild(const char* fileName, size_t nameLength) const
116	{
117		BString name(fileName, nameLength);
118		return (size_t)name.Length() == nameLength
119			? FindEntryChild(name) : NULL;
120	}
121
122	Attribute* FindNodeAttributeChild(const char* attributeName) const
123	{
124		for (DoublyLinkedList<Attribute>::ConstIterator it
125				= children.GetIterator(); Attribute* child = it.Next();) {
126			if (child->id != B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE)
127				continue;
128			if (child->value.type != B_HPKG_ATTRIBUTE_TYPE_STRING)
129				continue;
130			const char* childName = child->value.string->string;
131			if (strcmp(attributeName, childName) == 0)
132				return child;
133		}
134
135		return NULL;
136	}
137
138	Attribute* ChildWithID(BHPKGAttributeID id) const
139	{
140		for (DoublyLinkedList<Attribute>::ConstIterator it
141				= children.GetIterator(); Attribute* child = it.Next();) {
142			if (child->id == id)
143				return child;
144		}
145
146		return NULL;
147	}
148};
149
150
151// #pragma mark - PackageContentHandler
152
153
154struct PackageWriterImpl::PackageContentHandler
155	: BLowLevelPackageContentHandler {
156	PackageContentHandler(Attribute* rootAttribute, BErrorOutput* errorOutput,
157		StringCache& stringCache)
158		:
159		fErrorOutput(errorOutput),
160		fStringCache(stringCache),
161		fRootAttribute(rootAttribute),
162		fErrorOccurred(false)
163	{
164	}
165
166	virtual status_t HandleSectionStart(BHPKGPackageSectionID sectionID,
167		bool& _handleSection)
168	{
169		// we're only interested in the TOC
170		_handleSection = sectionID == B_HPKG_SECTION_PACKAGE_TOC;
171		return B_OK;
172	}
173
174	virtual status_t HandleSectionEnd(BHPKGPackageSectionID sectionID)
175	{
176		return B_OK;
177	}
178
179	virtual status_t HandleAttribute(BHPKGAttributeID attributeID,
180		const BPackageAttributeValue& value, void* parentToken, void*& _token)
181	{
182		if (fErrorOccurred)
183			return B_OK;
184
185		Attribute* parentAttribute = parentToken != NULL
186			? (Attribute*)parentToken : fRootAttribute;
187
188		Attribute* attribute = new Attribute(attributeID);
189		parentAttribute->AddChild(attribute);
190
191		switch (value.type) {
192			case B_HPKG_ATTRIBUTE_TYPE_INT:
193				attribute->value.SetTo(value.signedInt);
194				break;
195
196			case B_HPKG_ATTRIBUTE_TYPE_UINT:
197				attribute->value.SetTo(value.unsignedInt);
198				break;
199
200			case B_HPKG_ATTRIBUTE_TYPE_STRING:
201			{
202				CachedString* string = fStringCache.Get(value.string);
203				if (string == NULL)
204					throw std::bad_alloc();
205				attribute->value.SetTo(string);
206				break;
207			}
208
209			case B_HPKG_ATTRIBUTE_TYPE_RAW:
210				if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
211					attribute->value.SetToData(value.data.size,
212						value.data.offset);
213				} else if (value.encoding
214						== B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
215					attribute->value.SetToData(value.data.size, value.data.raw);
216				} else {
217					fErrorOutput->PrintError("Invalid attribute value encoding "
218						"%d (attribute %d)\n", value.encoding, attributeID);
219					return B_BAD_DATA;
220				}
221				break;
222
223			case B_HPKG_ATTRIBUTE_TYPE_INVALID:
224			default:
225				fErrorOutput->PrintError("Invalid attribute value type %d "
226					"(attribute %d)\n", value.type, attributeID);
227				return B_BAD_DATA;
228		}
229
230		_token = attribute;
231		return B_OK;
232	}
233
234	virtual status_t HandleAttributeDone(BHPKGAttributeID attributeID,
235		const BPackageAttributeValue& value, void* parentToken, void* token)
236	{
237		return B_OK;
238	}
239
240	virtual void HandleErrorOccurred()
241	{
242		fErrorOccurred = true;
243	}
244
245private:
246	BErrorOutput*	fErrorOutput;
247	StringCache&	fStringCache;
248	Attribute*		fRootAttribute;
249	bool			fErrorOccurred;
250};
251
252
253// #pragma mark - Entry
254
255
256struct PackageWriterImpl::Entry : DoublyLinkedListLinkImpl<Entry> {
257	Entry(char* name, size_t nameLength, int fd, bool isImplicit)
258		:
259		fName(name),
260		fNameLength(nameLength),
261		fFD(fd),
262		fIsImplicit(isImplicit)
263	{
264	}
265
266	~Entry()
267	{
268		DeleteChildren();
269		free(fName);
270	}
271
272	static Entry* Create(const char* name, size_t nameLength, int fd,
273		bool isImplicit)
274	{
275		char* clonedName = (char*)malloc(nameLength + 1);
276		if (clonedName == NULL)
277			throw std::bad_alloc();
278		memcpy(clonedName, name, nameLength);
279		clonedName[nameLength] = '\0';
280
281		Entry* entry = new(std::nothrow) Entry(clonedName, nameLength, fd,
282			isImplicit);
283		if (entry == NULL) {
284			free(clonedName);
285			throw std::bad_alloc();
286		}
287
288		return entry;
289	}
290
291	const char* Name() const
292	{
293		return fName;
294	}
295
296	int FD() const
297	{
298		return fFD;
299	}
300
301	void SetFD(int fd)
302	{
303		fFD = fd;
304	}
305
306	bool IsImplicit() const
307	{
308		return fIsImplicit;
309	}
310
311	void SetImplicit(bool isImplicit)
312	{
313		fIsImplicit = isImplicit;
314	}
315
316	bool HasName(const char* name, size_t nameLength)
317	{
318		return nameLength == fNameLength
319			&& strncmp(name, fName, nameLength) == 0;
320	}
321
322	void AddChild(Entry* child)
323	{
324		fChildren.Add(child);
325	}
326
327	void DeleteChildren()
328	{
329		while (Entry* child = fChildren.RemoveHead())
330			delete child;
331	}
332
333	Entry* GetChild(const char* name, size_t nameLength) const
334	{
335		EntryList::ConstIterator it = fChildren.GetIterator();
336		while (Entry* child = it.Next()) {
337			if (child->HasName(name, nameLength))
338				return child;
339		}
340
341		return NULL;
342	}
343
344	EntryList::ConstIterator ChildIterator() const
345	{
346		return fChildren.GetIterator();
347	}
348
349private:
350	char*		fName;
351	size_t		fNameLength;
352	int			fFD;
353	bool		fIsImplicit;
354	EntryList	fChildren;
355};
356
357
358// #pragma mark - SubPathAdder
359
360
361struct PackageWriterImpl::SubPathAdder {
362	SubPathAdder(BErrorOutput* errorOutput, char* pathBuffer,
363		const char* subPath)
364		:
365		fOriginalPathEnd(pathBuffer + strlen(pathBuffer))
366	{
367		if (fOriginalPathEnd != pathBuffer)
368			strlcat(pathBuffer, "/", B_PATH_NAME_LENGTH);
369
370		if (strlcat(pathBuffer, subPath, B_PATH_NAME_LENGTH)
371				>= B_PATH_NAME_LENGTH) {
372			*fOriginalPathEnd = '\0';
373			errorOutput->PrintError("Path too long: \"%s/%s\"\n", pathBuffer,
374				subPath);
375			throw status_t(B_BUFFER_OVERFLOW);
376		}
377	}
378
379	~SubPathAdder()
380	{
381		*fOriginalPathEnd = '\0';
382	}
383
384private:
385	char* fOriginalPathEnd;
386};
387
388
389// #pragma mark - HeapAttributeOffsetter
390
391
392struct PackageWriterImpl::HeapAttributeOffsetter {
393	HeapAttributeOffsetter(const RangeArray<uint64>& ranges,
394		const Array<uint64>& deltas)
395		:
396		fRanges(ranges),
397		fDeltas(deltas)
398	{
399	}
400
401	void ProcessAttribute(Attribute* attribute)
402	{
403		// If the attribute refers to a heap value, adjust it
404		AttributeValue& value = attribute->value;
405
406		if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW
407			&& value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
408			uint64 delta = fDeltas[fRanges.InsertionIndex(value.data.offset)];
409			value.data.offset -= delta;
410		}
411
412		// recurse
413		for (DoublyLinkedList<Attribute>::Iterator it
414					= attribute->children.GetIterator();
415				Attribute* child = it.Next();) {
416			ProcessAttribute(child);
417		}
418	}
419
420private:
421	const RangeArray<uint64>&	fRanges;
422	const Array<uint64>&		fDeltas;
423};
424
425
426// #pragma mark - PackageWriterImpl (Inline Methods)
427
428
429template<typename Type>
430inline PackageWriterImpl::Attribute*
431PackageWriterImpl::_AddAttribute(BHPKGAttributeID attributeID, Type value)
432{
433	AttributeValue attributeValue;
434	attributeValue.SetTo(value);
435	return _AddAttribute(attributeID, attributeValue);
436}
437
438
439// #pragma mark - PackageWriterImpl
440
441
442PackageWriterImpl::PackageWriterImpl(BPackageWriterListener* listener)
443	:
444	inherited("package", listener),
445	fListener(listener),
446	fHeapRangesToRemove(NULL),
447	fRootEntry(NULL),
448	fRootAttribute(NULL),
449	fTopAttribute(NULL),
450	fCheckLicenses(true)
451{
452}
453
454
455PackageWriterImpl::~PackageWriterImpl()
456{
457	delete fHeapRangesToRemove;
458	delete fRootAttribute;
459	delete fRootEntry;
460}
461
462
463status_t
464PackageWriterImpl::Init(const char* fileName,
465	const BPackageWriterParameters& parameters)
466{
467	try {
468		return _Init(NULL, false, fileName, parameters);
469	} catch (status_t error) {
470		return error;
471	} catch (std::bad_alloc&) {
472		fListener->PrintError("Out of memory!\n");
473		return B_NO_MEMORY;
474	}
475}
476
477
478status_t
479PackageWriterImpl::Init(BPositionIO* file, bool keepFile,
480	const BPackageWriterParameters& parameters)
481{
482	try {
483		return _Init(file, keepFile, NULL, parameters);
484	} catch (status_t error) {
485		return error;
486	} catch (std::bad_alloc&) {
487		fListener->PrintError("Out of memory!\n");
488		return B_NO_MEMORY;
489	}
490}
491
492
493status_t
494PackageWriterImpl::SetInstallPath(const char* installPath)
495{
496	fInstallPath = installPath;
497	return installPath == NULL
498		|| (size_t)fInstallPath.Length() == strlen(installPath)
499		? B_OK : B_NO_MEMORY;
500}
501
502
503void
504PackageWriterImpl::SetCheckLicenses(bool checkLicenses)
505{
506	fCheckLicenses = checkLicenses;
507}
508
509
510status_t
511PackageWriterImpl::AddEntry(const char* fileName, int fd)
512{
513	try {
514		// if it's ".PackageInfo", parse it
515		if (strcmp(fileName, B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
516			struct ErrorListener : public BPackageInfo::ParseErrorListener {
517				ErrorListener(BPackageWriterListener* _listener)
518					:
519					listener(_listener),
520					errorSeen(false)
521				{
522				}
523
524				virtual void OnError(const BString& msg, int line, int col) {
525					listener->PrintError("Parse error in %s(%d:%d) -> %s\n",
526						B_HPKG_PACKAGE_INFO_FILE_NAME, line, col, msg.String());
527					errorSeen = true;
528				}
529
530				BPackageWriterListener* listener;
531				bool errorSeen;
532			} errorListener(fListener);
533
534			if (fd >= 0) {
535				// a file descriptor is given -- read the config from there
536				// stat the file to get the file size
537				struct stat st;
538				if (fstat(fd, &st) != 0)
539					return errno;
540
541				BString packageInfoString;
542				char* buffer = packageInfoString.LockBuffer(st.st_size);
543				if (buffer == NULL)
544					return B_NO_MEMORY;
545
546				ssize_t result = read_pos(fd, 0, buffer, st.st_size);
547				if (result < 0) {
548					packageInfoString.UnlockBuffer(0);
549					return errno;
550				}
551
552				buffer[st.st_size] = '\0';
553				packageInfoString.UnlockBuffer(st.st_size);
554
555				result = fPackageInfo.ReadFromConfigString(packageInfoString,
556					&errorListener);
557				if (result != B_OK)
558					return result;
559			} else {
560				// use the file name
561				BEntry packageInfoEntry(fileName);
562				status_t result = fPackageInfo.ReadFromConfigFile(
563					packageInfoEntry, &errorListener);
564				if (result != B_OK
565					|| (result = fPackageInfo.InitCheck()) != B_OK) {
566					if (!errorListener.errorSeen) {
567						fListener->PrintError("Failed to read %s: %s\n",
568							fileName, strerror(result));
569					}
570					return result;
571				}
572			}
573		}
574
575		return _RegisterEntry(fileName, fd);
576	} catch (status_t error) {
577		return error;
578	} catch (std::bad_alloc&) {
579		fListener->PrintError("Out of memory!\n");
580		return B_NO_MEMORY;
581	}
582}
583
584
585status_t
586PackageWriterImpl::Finish()
587{
588	try {
589		if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
590			_UpdateCheckEntryCollisions();
591
592			if (fPackageInfo.InitCheck() != B_OK)
593				_UpdateReadPackageInfo();
594		}
595
596		if (fPackageInfo.InitCheck() != B_OK) {
597			fListener->PrintError("No package-info file found (%s)!\n",
598				B_HPKG_PACKAGE_INFO_FILE_NAME);
599			return B_BAD_DATA;
600		}
601
602		fPackageInfo.SetInstallPath(fInstallPath);
603
604		RegisterPackageInfo(PackageAttributes(), fPackageInfo);
605
606		if (fCheckLicenses) {
607			status_t result = _CheckLicenses();
608			if (result != B_OK)
609				return result;
610		}
611
612		if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0)
613			_CompactHeap();
614
615		return _Finish();
616	} catch (status_t error) {
617		return error;
618	} catch (std::bad_alloc&) {
619		fListener->PrintError("Out of memory!\n");
620		return B_NO_MEMORY;
621	}
622}
623
624
625status_t
626PackageWriterImpl::Recompress(BPositionIO* inputFile)
627{
628	if (inputFile == NULL)
629		return B_BAD_VALUE;
630
631	try {
632		return _Recompress(inputFile);
633	} catch (status_t error) {
634		return error;
635	} catch (std::bad_alloc&) {
636		fListener->PrintError("Out of memory!\n");
637		return B_NO_MEMORY;
638	}
639}
640
641
642status_t
643PackageWriterImpl::_Init(BPositionIO* file, bool keepFile, const char* fileName,
644	const BPackageWriterParameters& parameters)
645{
646	status_t result = inherited::Init(file, keepFile, fileName, parameters);
647	if (result != B_OK)
648		return result;
649
650	if (fStringCache.Init() != B_OK)
651		throw std::bad_alloc();
652
653	// create entry list
654	fRootEntry = new Entry(NULL, 0, -1, true);
655
656	fRootAttribute = new Attribute();
657
658	fHeapOffset = fHeaderSize = sizeof(hpkg_header);
659	fTopAttribute = fRootAttribute;
660
661	fHeapRangesToRemove = new RangeArray<uint64>;
662
663	// in update mode, parse the TOC
664	if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
665		PackageReaderImpl packageReader(fListener);
666		hpkg_header header;
667		result = packageReader.Init(File(), false, 0, &header);
668		if (result != B_OK)
669			return result;
670
671		fHeapOffset = packageReader.HeapOffset();
672
673		PackageContentHandler handler(fRootAttribute, fListener, fStringCache);
674
675		result = packageReader.ParseContent(&handler);
676		if (result != B_OK)
677			return result;
678
679		// While the compression level can change, we have to reuse the
680		// compression algorithm at least.
681		SetCompression(B_BENDIAN_TO_HOST_INT16(header.heap_compression));
682
683		result = InitHeapReader(fHeapOffset);
684		if (result != B_OK)
685			return result;
686
687		fHeapWriter->Reinit(packageReader.RawHeapReader());
688
689		// Remove the old packages attributes and TOC section from the heap.
690		// We'll write new ones later.
691		const PackageFileSection& attributesSection
692			= packageReader.PackageAttributesSection();
693		const PackageFileSection& tocSection = packageReader.TOCSection();
694		if (!fHeapRangesToRemove->AddRange(attributesSection.offset,
695				attributesSection.uncompressedLength)
696		   || !fHeapRangesToRemove->AddRange(tocSection.offset,
697				tocSection.uncompressedLength)) {
698			throw std::bad_alloc();
699		}
700	} else {
701		result = InitHeapReader(fHeapOffset);
702		if (result != B_OK)
703			return result;
704	}
705
706	return B_OK;
707}
708
709
710status_t
711PackageWriterImpl::_Finish()
712{
713	// write entries
714	for (EntryList::ConstIterator it = fRootEntry->ChildIterator();
715			Entry* entry = it.Next();) {
716		char pathBuffer[B_PATH_NAME_LENGTH];
717		pathBuffer[0] = '\0';
718		_AddEntry(AT_FDCWD, entry, entry->Name(), pathBuffer);
719	}
720
721	hpkg_header header;
722
723	// write the TOC and package attributes
724	uint64 tocLength;
725	_WriteTOC(header, tocLength);
726
727	uint64 attributesLength;
728	_WritePackageAttributes(header, attributesLength);
729
730	// flush the heap
731	status_t error = fHeapWriter->Finish();
732	if (error != B_OK)
733		return error;
734
735	uint64 compressedHeapSize = fHeapWriter->CompressedHeapSize();
736
737	header.heap_compression = B_HOST_TO_BENDIAN_INT16(
738		Parameters().Compression());
739	header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize());
740	header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64(compressedHeapSize);
741	header.heap_size_uncompressed = B_HOST_TO_BENDIAN_INT64(
742		fHeapWriter->UncompressedHeapSize());
743
744	// Truncate the file to the size it is supposed to have. In update mode, it
745	// can be greater when one or more files are shrunk. In creation mode it
746	// should already have the correct size.
747	off_t totalSize = fHeapWriter->HeapOffset() + (off_t)compressedHeapSize;
748	error = File()->SetSize(totalSize);
749	if (error != B_OK) {
750		fListener->PrintError("Failed to truncate package file to new "
751			"size: %s\n", strerror(errno));
752		return errno;
753	}
754
755	fListener->OnPackageSizeInfo(fHeaderSize, compressedHeapSize, tocLength,
756		attributesLength, totalSize);
757
758	// prepare the header
759
760	// general
761	header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_MAGIC);
762	header.header_size = B_HOST_TO_BENDIAN_INT16(fHeaderSize);
763	header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_VERSION);
764	header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
765	header.minor_version = B_HOST_TO_BENDIAN_INT16(B_HPKG_MINOR_VERSION);
766
767	// write the header
768	RawWriteBuffer(&header, sizeof(hpkg_header), 0);
769
770	SetFinished(true);
771	return B_OK;
772}
773
774
775status_t
776PackageWriterImpl::_Recompress(BPositionIO* inputFile)
777{
778	if (inputFile == NULL)
779		return B_BAD_VALUE;
780
781	// create a package reader for the input file
782	PackageReaderImpl reader(fListener);
783	hpkg_header header;
784	status_t error = reader.Init(inputFile, false, 0, &header);
785	if (error != B_OK) {
786		fListener->PrintError("Failed to open hpkg file: %s\n",
787			strerror(error));
788		return error;
789	}
790
791	// Update some header fields, assuming no compression. We'll rewrite the
792	// header later, should compression have been used. Doing it this way allows
793	// for streaming an uncompressed package.
794	uint64 uncompressedHeapSize
795		= reader.RawHeapReader()->UncompressedHeapSize();
796	uint64 compressedHeapSize = uncompressedHeapSize;
797
798	off_t totalSize = fHeapWriter->HeapOffset() + (off_t)compressedHeapSize;
799
800	header.heap_compression = B_HOST_TO_BENDIAN_INT16(
801		Parameters().Compression());
802	header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize());
803	header.heap_size_uncompressed
804		= B_HOST_TO_BENDIAN_INT64(uncompressedHeapSize);
805
806	if (Parameters().Compression() == B_HPKG_COMPRESSION_NONE) {
807		header.heap_size_compressed
808			= B_HOST_TO_BENDIAN_INT64(compressedHeapSize);
809		header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
810
811		// write the header
812		RawWriteBuffer(&header, sizeof(hpkg_header), 0);
813	}
814
815	// copy the heap data
816	uint64 bytesCompressed;
817	error = fHeapWriter->AddData(*reader.RawHeapReader(), uncompressedHeapSize,
818		bytesCompressed);
819	if (error != B_OK)
820		return error;
821
822	// flush the heap
823	error = fHeapWriter->Finish();
824	if (error != B_OK)
825		return error;
826
827	// If compression is enabled, update and write the header.
828	if (Parameters().Compression() != B_HPKG_COMPRESSION_NONE) {
829		compressedHeapSize = fHeapWriter->CompressedHeapSize();
830		totalSize = fHeapWriter->HeapOffset() + (off_t)compressedHeapSize;
831		header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64(compressedHeapSize);
832		header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
833
834		// write the header
835		RawWriteBuffer(&header, sizeof(hpkg_header), 0);
836	}
837
838	SetFinished(true);
839	return B_OK;
840}
841
842
843status_t
844PackageWriterImpl::_CheckLicenses()
845{
846	BPath systemLicensePath;
847	status_t result
848#ifdef HAIKU_TARGET_PLATFORM_HAIKU
849		= find_directory(B_SYSTEM_DATA_DIRECTORY, &systemLicensePath);
850#else
851		= systemLicensePath.SetTo(HAIKU_BUILD_SYSTEM_DATA_DIRECTORY);
852#endif
853	if (result != B_OK) {
854		fListener->PrintError("unable to find system data path: %s!\n",
855			strerror(result));
856		return result;
857	}
858	if ((result = systemLicensePath.Append("licenses")) != B_OK) {
859		fListener->PrintError("unable to append to system data path!\n");
860		return result;
861	}
862
863	BDirectory systemLicenseDir(systemLicensePath.Path());
864
865	const BStringList& licenseList = fPackageInfo.LicenseList();
866	for (int i = 0; i < licenseList.CountStrings(); ++i) {
867		const BString& licenseName = licenseList.StringAt(i);
868		if (licenseName == kPublicDomainLicenseName)
869			continue;
870
871		BEntry license;
872		if (systemLicenseDir.FindEntry(licenseName.String(), &license) == B_OK)
873			continue;
874
875		// license is not a system license, so it must be contained in package
876		BString licensePath("data/licenses/");
877		licensePath << licenseName;
878
879		if (!_IsEntryInPackage(licensePath)) {
880			fListener->PrintError("License '%s' isn't contained in package!\n",
881				licenseName.String());
882			return B_BAD_DATA;
883		}
884	}
885
886	return B_OK;
887}
888
889
890bool
891PackageWriterImpl::_IsEntryInPackage(const char* fileName)
892{
893	const char* originalFileName = fileName;
894
895	// find the closest ancestor of the entry that is in the added entries
896	bool added = false;
897	Entry* entry = fRootEntry;
898	while (entry != NULL) {
899		if (!entry->IsImplicit()) {
900			added = true;
901			break;
902		}
903
904		if (*fileName == '\0')
905			break;
906
907		const char* nextSlash = strchr(fileName, '/');
908
909		if (nextSlash == NULL) {
910			// no slash, just the file name
911			size_t length = strlen(fileName);
912			entry  = entry->GetChild(fileName, length);
913			fileName += length;
914			continue;
915		}
916
917		// find the start of the next component, skipping slashes
918		const char* nextComponent = nextSlash + 1;
919		while (*nextComponent == '/')
920			nextComponent++;
921
922		entry = entry->GetChild(fileName, nextSlash - fileName);
923
924		fileName = nextComponent;
925	}
926
927	if (added) {
928		// the entry itself or one of its ancestors has been added to the
929		// package explicitly -- stat it, to see, if it exists
930		struct stat st;
931		if (entry->FD() >= 0) {
932			if (fstatat(entry->FD(), *fileName != '\0' ? fileName : NULL, &st,
933					AT_SYMLINK_NOFOLLOW) == 0) {
934				return true;
935			}
936		} else {
937			if (lstat(originalFileName, &st) == 0)
938				return true;
939		}
940	}
941
942	// In update mode the entry might already be in the package.
943	Attribute* attribute = fRootAttribute;
944	fileName = originalFileName;
945
946	while (attribute != NULL) {
947		if (*fileName == '\0')
948			return true;
949
950		const char* nextSlash = strchr(fileName, '/');
951
952		if (nextSlash == NULL) {
953			// no slash, just the file name
954			return attribute->FindEntryChild(fileName) != NULL;
955		}
956
957		// find the start of the next component, skipping slashes
958		const char* nextComponent = nextSlash + 1;
959		while (*nextComponent == '/')
960			nextComponent++;
961
962		attribute = attribute->FindEntryChild(fileName, nextSlash - fileName);
963
964		fileName = nextComponent;
965	}
966
967	return false;
968}
969
970
971void
972PackageWriterImpl::_UpdateReadPackageInfo()
973{
974	// get the .PackageInfo entry attribute
975	Attribute* attribute = fRootAttribute->FindEntryChild(
976		B_HPKG_PACKAGE_INFO_FILE_NAME);
977	if (attribute == NULL) {
978		fListener->PrintError("No %s in package file.\n",
979			B_HPKG_PACKAGE_INFO_FILE_NAME);
980		throw status_t(B_BAD_DATA);
981	}
982
983	// get the data attribute
984	Attribute* dataAttribute = attribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_DATA);
985	if (dataAttribute == NULL)  {
986		fListener->PrintError("%s entry in package file doesn't have data.\n",
987			B_HPKG_PACKAGE_INFO_FILE_NAME);
988		throw status_t(B_BAD_DATA);
989	}
990
991	AttributeValue& value = dataAttribute->value;
992	if (value.type != B_HPKG_ATTRIBUTE_TYPE_RAW) {
993		fListener->PrintError("%s entry in package file has an invalid data "
994			"attribute (not of type raw).\n", B_HPKG_PACKAGE_INFO_FILE_NAME);
995		throw status_t(B_BAD_DATA);
996	}
997
998	BPackageData data;
999	if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE)
1000		data.SetData(value.data.size, value.data.raw);
1001	else
1002		data.SetData(value.data.size, value.data.offset);
1003
1004	// read the value into a string
1005	BString valueString;
1006	char* valueBuffer = valueString.LockBuffer(value.data.size);
1007	if (valueBuffer == NULL)
1008		throw std::bad_alloc();
1009
1010	if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
1011		// data encoded inline -- just copy to buffer
1012		memcpy(valueBuffer, value.data.raw, value.data.size);
1013	} else {
1014		// data on heap -- read from there
1015		status_t error = fHeapWriter->ReadData(data.Offset(), valueBuffer,
1016			data.Size());
1017		if (error != B_OK)
1018			throw error;
1019	}
1020
1021	valueString.UnlockBuffer();
1022
1023	// parse the package info
1024	status_t error = fPackageInfo.ReadFromConfigString(valueString);
1025	if (error != B_OK) {
1026		fListener->PrintError("Failed to parse package info data from package "
1027			"file: %s\n", strerror(error));
1028		throw status_t(error);
1029	}
1030}
1031
1032
1033void
1034PackageWriterImpl::_UpdateCheckEntryCollisions()
1035{
1036	for (EntryList::ConstIterator it = fRootEntry->ChildIterator();
1037			Entry* entry = it.Next();) {
1038		char pathBuffer[B_PATH_NAME_LENGTH];
1039		pathBuffer[0] = '\0';
1040		_UpdateCheckEntryCollisions(fRootAttribute, AT_FDCWD, entry,
1041			entry->Name(), pathBuffer);
1042	}
1043}
1044
1045
1046void
1047PackageWriterImpl::_UpdateCheckEntryCollisions(Attribute* parentAttribute,
1048	int dirFD, Entry* entry, const char* fileName, char* pathBuffer)
1049{
1050	bool isImplicitEntry = entry != NULL && entry->IsImplicit();
1051
1052	SubPathAdder pathAdder(fListener, pathBuffer, fileName);
1053
1054	// Check whether there's an entry attribute for this entry. If not, we can
1055	// ignore this entry.
1056	Attribute* entryAttribute = parentAttribute->FindEntryChild(fileName);
1057	if (entryAttribute == NULL)
1058		return;
1059
1060	// open the node
1061	int fd;
1062	FileDescriptorCloser fdCloser;
1063
1064	if (entry != NULL && entry->FD() >= 0) {
1065		// a file descriptor is already given -- use that
1066		fd = entry->FD();
1067	} else {
1068		fd = openat(dirFD, fileName,
1069			O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE));
1070		if (fd < 0) {
1071			fListener->PrintError("Failed to open entry \"%s\": %s\n",
1072				pathBuffer, strerror(errno));
1073			throw status_t(errno);
1074		}
1075		fdCloser.SetTo(fd);
1076	}
1077
1078	// stat the node
1079	struct stat st;
1080	if (fstat(fd, &st) < 0) {
1081		fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer,
1082			strerror(errno));
1083		throw status_t(errno);
1084	}
1085
1086	// implicit entries must be directories
1087	if (isImplicitEntry && !S_ISDIR(st.st_mode)) {
1088		fListener->PrintError("Non-leaf path component \"%s\" is not a "
1089			"directory.\n", pathBuffer);
1090		throw status_t(B_BAD_VALUE);
1091	}
1092
1093	// get the pre-existing node's file type
1094	uint32 preExistingFileType = B_HPKG_DEFAULT_FILE_TYPE;
1095	if (Attribute* fileTypeAttribute
1096			= entryAttribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_FILE_TYPE)) {
1097		if (fileTypeAttribute->value.type == B_HPKG_ATTRIBUTE_TYPE_UINT)
1098			preExistingFileType = fileTypeAttribute->value.unsignedInt;
1099	}
1100
1101	// Compare the node type with that of the pre-existing one.
1102	if (!S_ISDIR(st.st_mode)) {
1103		// the pre-existing must not a directory either -- we'll remove it
1104		if (preExistingFileType == B_HPKG_FILE_TYPE_DIRECTORY) {
1105			fListener->PrintError("Specified file \"%s\" clashes with an "
1106				"archived directory.\n", pathBuffer);
1107			throw status_t(B_BAD_VALUE);
1108		}
1109
1110		if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) {
1111			fListener->PrintError("Specified file \"%s\" clashes with an "
1112				"archived file.\n", pathBuffer);
1113			throw status_t(B_FILE_EXISTS);
1114		}
1115
1116		parentAttribute->RemoveChild(entryAttribute);
1117		_AttributeRemoved(entryAttribute);
1118
1119		return;
1120	}
1121
1122	// the pre-existing entry needs to be a directory too -- we will merge
1123	if (preExistingFileType != B_HPKG_FILE_TYPE_DIRECTORY) {
1124		fListener->PrintError("Specified directory \"%s\" clashes with an "
1125			"archived non-directory.\n", pathBuffer);
1126		throw status_t(B_BAD_VALUE);
1127	}
1128
1129	// directory -- recursively add children
1130	if (isImplicitEntry) {
1131		// this is an implicit entry -- just check the child entries
1132		for (EntryList::ConstIterator it = entry->ChildIterator();
1133				Entry* child = it.Next();) {
1134			_UpdateCheckEntryCollisions(entryAttribute, fd, child,
1135				child->Name(), pathBuffer);
1136		}
1137	} else {
1138		// explicitly specified directory -- we need to read the directory
1139
1140		// first we check for colliding node attributes, though
1141		AttrDirCloser attrDir(fs_fopen_attr_dir(fd));
1142		if (attrDir.IsSet()) {
1143			while (dirent* entry = fs_read_attr_dir(attrDir.Get())) {
1144				attr_info attrInfo;
1145				if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) {
1146					fListener->PrintError(
1147						"Failed to stat attribute \"%s\" of directory \"%s\": "
1148						"%s\n", entry->d_name, pathBuffer, strerror(errno));
1149					throw status_t(errno);
1150				}
1151
1152				// check whether the attribute exists
1153				Attribute* attributeAttribute
1154					= entryAttribute->FindNodeAttributeChild(entry->d_name);
1155				if (attributeAttribute == NULL)
1156					continue;
1157
1158				if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) {
1159					fListener->PrintError("Attribute \"%s\" of specified "
1160						"directory \"%s\" clashes with an archived "
1161						"attribute.\n", pathBuffer);
1162					throw status_t(B_FILE_EXISTS);
1163				}
1164
1165				// remove it
1166				entryAttribute->RemoveChild(attributeAttribute);
1167				_AttributeRemoved(attributeAttribute);
1168			}
1169		}
1170
1171		// we need to clone the directory FD for fdopendir()
1172		int clonedFD = dup(fd);
1173		if (clonedFD < 0) {
1174			fListener->PrintError(
1175				"Failed to dup() directory FD: %s\n", strerror(errno));
1176			throw status_t(errno);
1177		}
1178
1179		DirCloser dir(fdopendir(clonedFD));
1180		if (!dir.IsSet()) {
1181			fListener->PrintError(
1182				"Failed to open directory \"%s\": %s\n", pathBuffer,
1183				strerror(errno));
1184			close(clonedFD);
1185			throw status_t(errno);
1186		}
1187
1188		while (dirent* entry = readdir(dir.Get())) {
1189			// skip "." and ".."
1190			if (strcmp(entry->d_name, ".") == 0
1191				|| strcmp(entry->d_name, "..") == 0) {
1192				continue;
1193			}
1194
1195			_UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name,
1196				pathBuffer);
1197		}
1198	}
1199}
1200
1201
1202void
1203PackageWriterImpl::_CompactHeap()
1204{
1205	int32 count = fHeapRangesToRemove->CountRanges();
1206	if (count == 0)
1207		return;
1208
1209	// compute the move deltas for the ranges
1210	Array<uint64> deltas;
1211	uint64 delta = 0;
1212	for (int32 i = 0; i < count; i++) {
1213		if (!deltas.Add(delta))
1214			throw std::bad_alloc();
1215
1216		delta += fHeapRangesToRemove->RangeAt(i).size;
1217	}
1218
1219	if (!deltas.Add(delta))
1220		throw std::bad_alloc();
1221
1222	// offset the attributes
1223	HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute(
1224		fRootAttribute);
1225
1226	// remove the ranges from the heap
1227	fHeapWriter->RemoveDataRanges(*fHeapRangesToRemove);
1228}
1229
1230
1231void
1232PackageWriterImpl::_AttributeRemoved(Attribute* attribute)
1233{
1234	AttributeValue& value = attribute->value;
1235	if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW
1236		&& value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
1237		if (!fHeapRangesToRemove->AddRange(value.data.offset, value.data.size))
1238			throw std::bad_alloc();
1239	} else if (value.type == B_HPKG_ATTRIBUTE_TYPE_STRING)
1240		fStringCache.Put(value.string);
1241
1242	for (DoublyLinkedList<Attribute>::Iterator it
1243				= attribute->children.GetIterator();
1244			Attribute* child = it.Next();) {
1245		_AttributeRemoved(child);
1246	}
1247}
1248
1249
1250status_t
1251PackageWriterImpl::_RegisterEntry(const char* fileName, int fd)
1252{
1253	if (*fileName == '\0') {
1254		fListener->PrintError("Invalid empty file name\n");
1255		return B_BAD_VALUE;
1256	}
1257
1258	// add all components of the path
1259	Entry* entry = fRootEntry;
1260	while (*fileName != 0) {
1261		const char* nextSlash = strchr(fileName, '/');
1262		// no slash, just add the file name
1263		if (nextSlash == NULL) {
1264			entry = _RegisterEntry(entry, fileName, strlen(fileName), fd,
1265				false);
1266			break;
1267		}
1268
1269		// find the start of the next component, skipping slashes
1270		const char* nextComponent = nextSlash + 1;
1271		while (*nextComponent == '/')
1272			nextComponent++;
1273
1274		bool lastComponent = *nextComponent != '\0';
1275
1276		if (nextSlash == fileName) {
1277			// the FS root
1278			entry = _RegisterEntry(entry, fileName, 1, lastComponent ? fd : -1,
1279				lastComponent);
1280		} else {
1281			entry = _RegisterEntry(entry, fileName, nextSlash - fileName,
1282				lastComponent ? fd : -1, lastComponent);
1283		}
1284
1285		fileName = nextComponent;
1286	}
1287
1288	return B_OK;
1289}
1290
1291
1292PackageWriterImpl::Entry*
1293PackageWriterImpl::_RegisterEntry(Entry* parent, const char* name,
1294	size_t nameLength, int fd, bool isImplicit)
1295{
1296	// check the component name -- don't allow "." or ".."
1297	if (name[0] == '.'
1298		&& (nameLength == 1 || (nameLength == 2 && name[1] == '.'))) {
1299		fListener->PrintError("Invalid file name: \".\" and \"..\" "
1300			"are not allowed as path components\n");
1301		throw status_t(B_BAD_VALUE);
1302	}
1303
1304	// the entry might already exist
1305	Entry* entry = parent->GetChild(name, nameLength);
1306	if (entry != NULL) {
1307		// If the entry was implicit and is no longer, we mark it non-implicit
1308		// and delete all of it's children.
1309		if (entry->IsImplicit() && !isImplicit) {
1310			entry->DeleteChildren();
1311			entry->SetImplicit(false);
1312			entry->SetFD(fd);
1313		}
1314	} else {
1315		// nope -- create it
1316		entry = Entry::Create(name, nameLength, fd, isImplicit);
1317		parent->AddChild(entry);
1318	}
1319
1320	return entry;
1321}
1322
1323
1324void
1325PackageWriterImpl::_WriteTOC(hpkg_header& header, uint64& _length)
1326{
1327	// write the subsections
1328	uint64 startOffset = fHeapWriter->UncompressedHeapSize();
1329
1330	// cached strings
1331	uint64 cachedStringsOffset = fHeapWriter->UncompressedHeapSize();
1332	int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2);
1333
1334	// main TOC section
1335	uint64 mainOffset = fHeapWriter->UncompressedHeapSize();
1336	_WriteAttributeChildren(fRootAttribute);
1337
1338	// notify the listener
1339	uint64 endOffset = fHeapWriter->UncompressedHeapSize();
1340	uint64 stringsSize = mainOffset - cachedStringsOffset;
1341	uint64 mainSize = endOffset - mainOffset;
1342	uint64 tocSize = endOffset - startOffset;
1343	fListener->OnTOCSizeInfo(stringsSize, mainSize, tocSize);
1344
1345	// update the header
1346	header.toc_length = B_HOST_TO_BENDIAN_INT64(tocSize);
1347	header.toc_strings_length = B_HOST_TO_BENDIAN_INT64(stringsSize);
1348	header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten);
1349
1350	_length = tocSize;
1351}
1352
1353
1354void
1355PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute)
1356{
1357	DoublyLinkedList<Attribute>::Iterator it
1358		= attribute->children.GetIterator();
1359	while (Attribute* child = it.Next()) {
1360		// write tag
1361		uint8 encoding = child->value.ApplicableEncoding();
1362		WriteUnsignedLEB128(compose_attribute_tag(child->id,
1363			child->value.type, encoding, !child->children.IsEmpty()));
1364
1365		// write value
1366		WriteAttributeValue(child->value, encoding);
1367
1368		if (!child->children.IsEmpty())
1369			_WriteAttributeChildren(child);
1370	}
1371
1372	WriteUnsignedLEB128(0);
1373}
1374
1375
1376void
1377PackageWriterImpl::_WritePackageAttributes(hpkg_header& header, uint64& _length)
1378{
1379	// write cached strings and package attributes tree
1380	off_t startOffset = fHeapWriter->UncompressedHeapSize();
1381
1382	uint32 stringsLength;
1383	uint32 stringsCount = WritePackageAttributes(PackageAttributes(),
1384		stringsLength);
1385
1386	// notify listener
1387	uint32 attributesLength = fHeapWriter->UncompressedHeapSize() - startOffset;
1388	fListener->OnPackageAttributesSizeInfo(stringsCount, attributesLength);
1389
1390	// update the header
1391	header.attributes_length = B_HOST_TO_BENDIAN_INT32(attributesLength);
1392	header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount);
1393	header.attributes_strings_length = B_HOST_TO_BENDIAN_INT32(stringsLength);
1394
1395	_length = attributesLength;
1396}
1397
1398
1399void
1400PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName,
1401	char* pathBuffer)
1402{
1403	bool isImplicitEntry = entry != NULL && entry->IsImplicit();
1404
1405	SubPathAdder pathAdder(fListener, pathBuffer, fileName);
1406	if (!isImplicitEntry)
1407		fListener->OnEntryAdded(pathBuffer);
1408
1409	// open the node
1410	int fd;
1411	FileDescriptorCloser fdCloser;
1412
1413	if (entry != NULL && entry->FD() >= 0) {
1414		// a file descriptor is already given -- use that
1415		fd = entry->FD();
1416	} else {
1417		fd = openat(dirFD, fileName,
1418			O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE));
1419		if (fd < 0) {
1420			fListener->PrintError("Failed to open entry \"%s\": %s\n",
1421				pathBuffer, strerror(errno));
1422			throw status_t(errno);
1423		}
1424		fdCloser.SetTo(fd);
1425	}
1426
1427	// stat the node
1428	struct stat st;
1429	if (fstat(fd, &st) < 0) {
1430		fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer,
1431			strerror(errno));
1432		throw status_t(errno);
1433	}
1434
1435	// implicit entries must be directories
1436	if (isImplicitEntry && !S_ISDIR(st.st_mode)) {
1437		fListener->PrintError("Non-leaf path component \"%s\" is not a "
1438			"directory\n", pathBuffer);
1439		throw status_t(B_BAD_VALUE);
1440	}
1441
1442	// In update mode we don't need to add an entry attribute for an implicit
1443	// directory, if there already is one.
1444	Attribute* entryAttribute = NULL;
1445	if (S_ISDIR(st.st_mode) && (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
1446		entryAttribute = fTopAttribute->FindEntryChild(fileName);
1447		if (entryAttribute != NULL && isImplicitEntry) {
1448			Stacker<Attribute> entryAttributeStacker(fTopAttribute,
1449				entryAttribute);
1450			_AddDirectoryChildren(entry, fd, pathBuffer);
1451			return;
1452		}
1453	}
1454
1455	// check/translate the node type
1456	uint8 fileType;
1457	uint32 defaultPermissions;
1458	if (S_ISREG(st.st_mode)) {
1459		fileType = B_HPKG_FILE_TYPE_FILE;
1460		defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS;
1461	} else if (S_ISLNK(st.st_mode)) {
1462		fileType = B_HPKG_FILE_TYPE_SYMLINK;
1463		defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS;
1464	} else if (S_ISDIR(st.st_mode)) {
1465		fileType = B_HPKG_FILE_TYPE_DIRECTORY;
1466		defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS;
1467	} else {
1468		// unsupported node type
1469		fListener->PrintError("Unsupported node type, entry: \"%s\"\n",
1470			pathBuffer);
1471		throw status_t(B_UNSUPPORTED);
1472	}
1473
1474	// add attribute entry, if it doesn't already exist (update mode, directory)
1475	bool isNewEntry = entryAttribute == NULL;
1476	if (entryAttribute == NULL) {
1477		entryAttribute = _AddStringAttribute(
1478			B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName);
1479	}
1480
1481	Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute);
1482
1483	if (isNewEntry) {
1484		// add stat data
1485		if (fileType != B_HPKG_DEFAULT_FILE_TYPE)
1486			_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType);
1487		if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) {
1488			_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS,
1489				uint32(st.st_mode & ALLPERMS));
1490		}
1491		_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime));
1492		_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime));
1493#ifdef __HAIKU__
1494		_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime));
1495#else
1496		_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime));
1497#endif
1498		// TODO: File user/group!
1499
1500		// add file data/symlink path
1501		if (S_ISREG(st.st_mode)) {
1502			// regular file -- add data
1503			if (st.st_size > 0) {
1504				BFDDataReader dataReader(fd);
1505				status_t error = _AddData(dataReader, st.st_size);
1506				if (error != B_OK)
1507					throw status_t(error);
1508			}
1509		} else if (S_ISLNK(st.st_mode)) {
1510			// symlink -- add link address
1511			char path[B_PATH_NAME_LENGTH + 1];
1512			ssize_t bytesRead = readlinkat(dirFD, fileName, path,
1513				B_PATH_NAME_LENGTH);
1514			if (bytesRead < 0) {
1515				fListener->PrintError("Failed to read symlink \"%s\": %s\n",
1516					pathBuffer, strerror(errno));
1517				throw status_t(errno);
1518			}
1519
1520			path[bytesRead] = '\0';
1521			_AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path);
1522		}
1523	}
1524
1525	// add attributes
1526	AttrDirCloser attrDir(fs_fopen_attr_dir(fd));
1527	if (attrDir.IsSet()) {
1528		while (dirent* entry = fs_read_attr_dir(attrDir.Get())) {
1529			attr_info attrInfo;
1530			if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) {
1531				fListener->PrintError(
1532					"Failed to stat attribute \"%s\" of file \"%s\": %s\n",
1533					entry->d_name, pathBuffer, strerror(errno));
1534				throw status_t(errno);
1535			}
1536
1537			// create attribute entry
1538			Attribute* attributeAttribute = _AddStringAttribute(
1539				B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name);
1540			Stacker<Attribute> attributeAttributeStacker(fTopAttribute,
1541				attributeAttribute);
1542
1543			// add type
1544			_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE,
1545				(uint32)attrInfo.type);
1546
1547			// add data
1548			BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type);
1549			status_t error = _AddData(dataReader, attrInfo.size);
1550			if (error != B_OK)
1551				throw status_t(error);
1552		}
1553	}
1554
1555	if (S_ISDIR(st.st_mode))
1556		_AddDirectoryChildren(entry, fd, pathBuffer);
1557}
1558
1559
1560void
1561PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer)
1562{
1563	// directory -- recursively add children
1564	if (entry != NULL && entry->IsImplicit()) {
1565		// this is an implicit entry -- just add it's children
1566		for (EntryList::ConstIterator it = entry->ChildIterator();
1567				Entry* child = it.Next();) {
1568			_AddEntry(fd, child, child->Name(), pathBuffer);
1569		}
1570	} else {
1571		// we need to clone the directory FD for fdopendir()
1572		int clonedFD = dup(fd);
1573		if (clonedFD < 0) {
1574			fListener->PrintError(
1575				"Failed to dup() directory FD: %s\n", strerror(errno));
1576			throw status_t(errno);
1577		}
1578
1579		DirCloser dir(fdopendir(clonedFD));
1580		if (!dir.IsSet()) {
1581			fListener->PrintError(
1582				"Failed to open directory \"%s\": %s\n", pathBuffer,
1583				strerror(errno));
1584			close(clonedFD);
1585			throw status_t(errno);
1586		}
1587
1588		while (dirent* entry = readdir(dir.Get())) {
1589			// skip "." and ".."
1590			if (strcmp(entry->d_name, ".") == 0
1591				|| strcmp(entry->d_name, "..") == 0) {
1592				continue;
1593			}
1594
1595			_AddEntry(fd, NULL, entry->d_name, pathBuffer);
1596		}
1597	}
1598}
1599
1600
1601PackageWriterImpl::Attribute*
1602PackageWriterImpl::_AddAttribute(BHPKGAttributeID id,
1603	const AttributeValue& value)
1604{
1605	Attribute* attribute = new Attribute(id);
1606
1607	attribute->value = value;
1608	fTopAttribute->AddChild(attribute);
1609
1610	return attribute;
1611}
1612
1613
1614PackageWriterImpl::Attribute*
1615PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID,
1616	const char* value)
1617{
1618	AttributeValue attributeValue;
1619	attributeValue.SetTo(fStringCache.Get(value));
1620	return _AddAttribute(attributeID, attributeValue);
1621}
1622
1623
1624PackageWriterImpl::Attribute*
1625PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID,
1626	uint64 dataSize, uint64 dataOffset)
1627{
1628	AttributeValue attributeValue;
1629	attributeValue.SetToData(dataSize, dataOffset);
1630	return _AddAttribute(attributeID, attributeValue);
1631}
1632
1633
1634PackageWriterImpl::Attribute*
1635PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID,
1636	uint64 dataSize, const uint8* data)
1637{
1638	AttributeValue attributeValue;
1639	attributeValue.SetToData(dataSize, data);
1640	return _AddAttribute(attributeID, attributeValue);
1641}
1642
1643
1644status_t
1645PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size)
1646{
1647	// add short data inline
1648	if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) {
1649		uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE];
1650		status_t error = dataReader.ReadData(0, buffer, size);
1651		if (error != B_OK) {
1652			fListener->PrintError("Failed to read data: %s\n", strerror(error));
1653			return error;
1654		}
1655
1656		_AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer);
1657		return B_OK;
1658	}
1659
1660	// add data to heap
1661	uint64 dataOffset;
1662	status_t error = fHeapWriter->AddData(dataReader, size, dataOffset);
1663	if (error != B_OK)
1664		return error;
1665
1666	_AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, dataOffset);
1667	return B_OK;
1668}
1669
1670
1671}	// namespace BPrivate
1672
1673}	// namespace BHPKG
1674
1675}	// namespace BPackageKit
1676