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