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