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