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