xref: /haiku/src/kits/package/hpkg/PackageWriterImpl.cpp (revision d7227dfa9e644112f7019d94b4611da400c6c884)
1 /*
2  * Copyright 2009-2011, 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/BlockBufferCacheNoLock.h>
30 
31 #include <package/hpkg/PackageAttributeValue.h>
32 #include <package/hpkg/PackageContentHandler.h>
33 #include <package/hpkg/PackageData.h>
34 #include <package/hpkg/PackageDataReader.h>
35 
36 #include <AutoDeleter.h>
37 #include <RangeArray.h>
38 
39 #include <package/hpkg/HPKGDefsPrivate.h>
40 
41 #include <package/hpkg/DataOutput.h>
42 #include <package/hpkg/DataReader.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, uint64 heapOffset)
161 		:
162 		fErrorOutput(errorOutput),
163 		fStringCache(stringCache),
164 		fRootAttribute(rootAttribute),
165 		fHeapOffset(heapOffset),
166 		fErrorOccurred(false)
167 	{
168 	}
169 
170 static const char* AttributeNameForID(uint8 id)
171 {
172 	return BLowLevelPackageContentHandler::AttributeNameForID(id);
173 }
174 
175 	virtual status_t HandleSectionStart(BHPKGPackageSectionID sectionID,
176 		bool& _handleSection)
177 	{
178 		// we're only interested in the TOC
179 		_handleSection = sectionID == B_HPKG_SECTION_PACKAGE_TOC;
180 		return B_OK;
181 	}
182 
183 	virtual status_t HandleSectionEnd(BHPKGPackageSectionID sectionID)
184 	{
185 		return B_OK;
186 	}
187 
188 	virtual status_t HandleAttribute(BHPKGAttributeID attributeID,
189 		const BPackageAttributeValue& value, void* parentToken, void*& _token)
190 	{
191 		if (fErrorOccurred)
192 			return B_OK;
193 
194 		Attribute* parentAttribute = parentToken != NULL
195 			? (Attribute*)parentToken : fRootAttribute;
196 
197 		Attribute* attribute = new Attribute(attributeID);
198 		parentAttribute->AddChild(attribute);
199 
200 		switch (value.type) {
201 			case B_HPKG_ATTRIBUTE_TYPE_INT:
202 				attribute->value.SetTo(value.signedInt);
203 				break;
204 
205 			case B_HPKG_ATTRIBUTE_TYPE_UINT:
206 				attribute->value.SetTo(value.unsignedInt);
207 				break;
208 
209 			case B_HPKG_ATTRIBUTE_TYPE_STRING:
210 			{
211 				CachedString* string = fStringCache.Get(value.string);
212 				if (string == NULL)
213 					throw std::bad_alloc();
214 				attribute->value.SetTo(string);
215 				break;
216 			}
217 
218 			case B_HPKG_ATTRIBUTE_TYPE_RAW:
219 				if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
220 					attribute->value.SetToData(value.data.size,
221 						value.data.offset - fHeapOffset);
222 				} else if (value.encoding
223 						== B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
224 					attribute->value.SetToData(value.data.size, value.data.raw);
225 				} else {
226 					fErrorOutput->PrintError("Invalid attribute value encoding "
227 						"%d (attribute %d)\n", value.encoding, attributeID);
228 					return B_BAD_DATA;
229 				}
230 				break;
231 
232 			case B_HPKG_ATTRIBUTE_TYPE_INVALID:
233 			default:
234 				fErrorOutput->PrintError("Invalid attribute value type %d "
235 					"(attribute %d)\n", value.type, attributeID);
236 				return B_BAD_DATA;
237 		}
238 
239 		_token = attribute;
240 		return B_OK;
241 	}
242 
243 	virtual status_t HandleAttributeDone(BHPKGAttributeID attributeID,
244 		const BPackageAttributeValue& value, void* parentToken, void* token)
245 	{
246 		return B_OK;
247 	}
248 
249 	virtual void HandleErrorOccurred()
250 	{
251 		fErrorOccurred = true;
252 	}
253 
254 private:
255 	BErrorOutput*	fErrorOutput;
256 	StringCache&	fStringCache;
257 	Attribute*		fRootAttribute;
258 	uint64			fHeapOffset;
259 	bool			fErrorOccurred;
260 };
261 
262 
263 // #pragma mark - Entry
264 
265 
266 struct PackageWriterImpl::Entry : DoublyLinkedListLinkImpl<Entry> {
267 	Entry(char* name, size_t nameLength, int fd, bool isImplicit)
268 		:
269 		fName(name),
270 		fNameLength(nameLength),
271 		fFD(fd),
272 		fIsImplicit(isImplicit)
273 	{
274 	}
275 
276 	~Entry()
277 	{
278 		DeleteChildren();
279 		free(fName);
280 	}
281 
282 	static Entry* Create(const char* name, size_t nameLength, int fd,
283 		bool isImplicit)
284 	{
285 		char* clonedName = (char*)malloc(nameLength + 1);
286 		if (clonedName == NULL)
287 			throw std::bad_alloc();
288 		memcpy(clonedName, name, nameLength);
289 		clonedName[nameLength] = '\0';
290 
291 		Entry* entry = new(std::nothrow) Entry(clonedName, nameLength, fd,
292 			isImplicit);
293 		if (entry == NULL) {
294 			free(clonedName);
295 			throw std::bad_alloc();
296 		}
297 
298 		return entry;
299 	}
300 
301 	const char* Name() const
302 	{
303 		return fName;
304 	}
305 
306 	int FD() const
307 	{
308 		return fFD;
309 	}
310 
311 	void SetFD(int fd)
312 	{
313 		fFD = fd;
314 	}
315 
316 	bool IsImplicit() const
317 	{
318 		return fIsImplicit;
319 	}
320 
321 	void SetImplicit(bool isImplicit)
322 	{
323 		fIsImplicit = isImplicit;
324 	}
325 
326 	bool HasName(const char* name, size_t nameLength)
327 	{
328 		return nameLength == fNameLength
329 			&& strncmp(name, fName, nameLength) == 0;
330 	}
331 
332 	void AddChild(Entry* child)
333 	{
334 		fChildren.Add(child);
335 	}
336 
337 	void DeleteChildren()
338 	{
339 		while (Entry* child = fChildren.RemoveHead())
340 			delete child;
341 	}
342 
343 	Entry* GetChild(const char* name, size_t nameLength) const
344 	{
345 		EntryList::ConstIterator it = fChildren.GetIterator();
346 		while (Entry* child = it.Next()) {
347 			if (child->HasName(name, nameLength))
348 				return child;
349 		}
350 
351 		return NULL;
352 	}
353 
354 	EntryList::ConstIterator ChildIterator() const
355 	{
356 		return fChildren.GetIterator();
357 	}
358 
359 private:
360 	char*		fName;
361 	size_t		fNameLength;
362 	int			fFD;
363 	bool		fIsImplicit;
364 	EntryList	fChildren;
365 };
366 
367 
368 // #pragma mark - SubPathAdder
369 
370 
371 struct PackageWriterImpl::SubPathAdder {
372 	SubPathAdder(BErrorOutput* errorOutput, char* pathBuffer,
373 		const char* subPath)
374 		:
375 		fOriginalPathEnd(pathBuffer + strlen(pathBuffer))
376 	{
377 		if (fOriginalPathEnd != pathBuffer)
378 			strlcat(pathBuffer, "/", B_PATH_NAME_LENGTH);
379 
380 		if (strlcat(pathBuffer, subPath, B_PATH_NAME_LENGTH)
381 				>= B_PATH_NAME_LENGTH) {
382 			*fOriginalPathEnd = '\0';
383 			errorOutput->PrintError("Path too long: \"%s/%s\"\n", pathBuffer,
384 				subPath);
385 			throw status_t(B_BUFFER_OVERFLOW);
386 		}
387 	}
388 
389 	~SubPathAdder()
390 	{
391 		*fOriginalPathEnd = '\0';
392 	}
393 
394 private:
395 	char* fOriginalPathEnd;
396 };
397 
398 
399 struct PackageWriterImpl::HeapAttributeOffsetter {
400 	HeapAttributeOffsetter(const RangeArray<off_t>& ranges,
401 		const Array<off_t>& deltas)
402 		:
403 		fRanges(ranges),
404 		fDeltas(deltas)
405 	{
406 	}
407 
408 	void ProcessAttribute(Attribute* attribute)
409 	{
410 		// If the attribute refers to a heap value, adjust it
411 		AttributeValue& value = attribute->value;
412 
413 		if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW
414 			&& value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
415 			off_t delta = fDeltas[fRanges.InsertionIndex(value.data.offset)];
416 			value.data.offset -= delta;
417 		}
418 
419 		// recurse
420 		for (DoublyLinkedList<Attribute>::Iterator it
421 					= attribute->children.GetIterator();
422 				Attribute* child = it.Next();) {
423 			ProcessAttribute(child);
424 		}
425 	}
426 
427 private:
428 	const RangeArray<off_t>&	fRanges;
429 	const Array<off_t>&			fDeltas;
430 };
431 
432 
433 // #pragma mark - PackageWriterImpl (Inline Methods)
434 
435 
436 template<typename Type>
437 inline PackageWriterImpl::Attribute*
438 PackageWriterImpl::_AddAttribute(BHPKGAttributeID attributeID, Type value)
439 {
440 	AttributeValue attributeValue;
441 	attributeValue.SetTo(value);
442 	return _AddAttribute(attributeID, attributeValue);
443 }
444 
445 
446 // #pragma mark - PackageWriterImpl
447 
448 
449 PackageWriterImpl::PackageWriterImpl(BPackageWriterListener* listener)
450 	:
451 	inherited(listener),
452 	fListener(listener),
453 	fHeapRangesToRemove(NULL),
454 	fDataBuffer(NULL),
455 	fDataBufferSize(2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB),
456 	fRootEntry(NULL),
457 	fRootAttribute(NULL),
458 	fTopAttribute(NULL),
459 	fCheckLicenses(true)
460 {
461 }
462 
463 
464 PackageWriterImpl::~PackageWriterImpl()
465 {
466 	delete fRootAttribute;
467 
468 	delete fRootEntry;
469 
470 	free(fDataBuffer);
471 }
472 
473 
474 status_t
475 PackageWriterImpl::Init(const char* fileName, uint32 flags)
476 {
477 	try {
478 		return _Init(fileName, flags);
479 	} catch (status_t error) {
480 		return error;
481 	} catch (std::bad_alloc) {
482 		fListener->PrintError("Out of memory!\n");
483 		return B_NO_MEMORY;
484 	}
485 }
486 
487 
488 void
489 PackageWriterImpl::SetCheckLicenses(bool checkLicenses)
490 {
491 	fCheckLicenses = checkLicenses;
492 }
493 
494 
495 status_t
496 PackageWriterImpl::AddEntry(const char* fileName, int fd)
497 {
498 	try {
499 		// if it's ".PackageInfo", parse it
500 		if (strcmp(fileName, B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
501 			struct ErrorListener : public BPackageInfo::ParseErrorListener {
502 				ErrorListener(BPackageWriterListener* _listener)
503 					: listener(_listener) {}
504 				virtual void OnError(const BString& msg, int line, int col) {
505 					listener->PrintError("Parse error in %s(%d:%d) -> %s\n",
506 						B_HPKG_PACKAGE_INFO_FILE_NAME, line, col, msg.String());
507 				}
508 				BPackageWriterListener* listener;
509 			} errorListener(fListener);
510 
511 			if (fd >= 0) {
512 				// a file descriptor is given -- read the config from there
513 				// stat the file to get the file size
514 				struct stat st;
515 				if (fstat(fd, &st) != 0)
516 					return errno;
517 
518 				BString packageInfoString;
519 				char* buffer = packageInfoString.LockBuffer(st.st_size);
520 				if (buffer == NULL)
521 					return B_NO_MEMORY;
522 
523 				ssize_t result = read_pos(fd, 0, buffer, st.st_size);
524 				if (result < 0) {
525 					packageInfoString.UnlockBuffer(0);
526 					return errno;
527 				}
528 
529 				buffer[st.st_size] = '\0';
530 				packageInfoString.UnlockBuffer(st.st_size);
531 
532 				result = fPackageInfo.ReadFromConfigString(packageInfoString,
533 					&errorListener);
534 				if (result != B_OK)
535 					return result;
536 			} else {
537 				// use the file name
538 				BEntry packageInfoEntry(fileName);
539 				status_t result = fPackageInfo.ReadFromConfigFile(
540 					packageInfoEntry, &errorListener);
541 				if (result != B_OK
542 					|| (result = fPackageInfo.InitCheck()) != B_OK) {
543 					return result;
544 				}
545 			}
546 		}
547 
548 		return _RegisterEntry(fileName, fd);
549 	} catch (status_t error) {
550 		return error;
551 	} catch (std::bad_alloc) {
552 		fListener->PrintError("Out of memory!\n");
553 		return B_NO_MEMORY;
554 	}
555 }
556 
557 
558 status_t
559 PackageWriterImpl::Finish()
560 {
561 	try {
562 		RangeArray<off_t> heapRangesToRemove;
563 		fHeapRangesToRemove = &heapRangesToRemove;
564 
565 		if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
566 			_UpdateCheckEntryCollisions();
567 
568 			if (fPackageInfo.InitCheck() != B_OK)
569 				_UpdateReadPackageInfo();
570 		}
571 
572 		if (fPackageInfo.InitCheck() != B_OK) {
573 			fListener->PrintError("No package-info file found (%s)!\n",
574 				B_HPKG_PACKAGE_INFO_FILE_NAME);
575 			return B_BAD_DATA;
576 		}
577 
578 		RegisterPackageInfo(PackageAttributes(), fPackageInfo);
579 
580 		if (fCheckLicenses) {
581 			status_t result = _CheckLicenses();
582 			if (result != B_OK)
583 				return result;
584 		}
585 
586 		if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0)
587 			_CompactHeap();
588 
589 		fHeapRangesToRemove = NULL;
590 
591 		return _Finish();
592 	} catch (status_t error) {
593 		return error;
594 	} catch (std::bad_alloc) {
595 		fListener->PrintError("Out of memory!\n");
596 		return B_NO_MEMORY;
597 	}
598 }
599 
600 
601 status_t
602 PackageWriterImpl::_Init(const char* fileName, uint32 flags)
603 {
604 	status_t result = inherited::Init(fileName, "package", flags);
605 	if (result != B_OK)
606 		return result;
607 
608 	// allocate data buffer
609 	fDataBuffer = malloc(fDataBufferSize);
610 	if (fDataBuffer == NULL)
611 		throw std::bad_alloc();
612 
613 	if (fStringCache.Init() != B_OK)
614 		throw std::bad_alloc();
615 
616 	// create entry list
617 	fRootEntry = new Entry(NULL, 0, -1, true);
618 
619 	fRootAttribute = new Attribute();
620 
621 	fHeapOffset = fHeapEnd = sizeof(hpkg_header);
622 	fTopAttribute = fRootAttribute;
623 
624 	// in update mode, parse the TOC
625 	if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
626 		PackageReaderImpl packageReader(fListener);
627 		result = packageReader.Init(FD(), false);
628 		if (result != B_OK)
629 			return result;
630 
631 		fHeapOffset = packageReader.HeapOffset();
632 		fHeapEnd = fHeapOffset + packageReader.HeapSize();
633 
634 		PackageContentHandler handler(fRootAttribute, fListener, fStringCache,
635 			fHeapOffset);
636 
637 		result = packageReader.ParseContent(&handler);
638 		if (result != B_OK)
639 			return result;
640 
641 		if ((uint64)fHeapOffset > packageReader.HeapOffset()) {
642 			fListener->PrintError("Unexpected heap offset in package file.\n");
643 			return B_BAD_DATA;
644 		}
645 	}
646 
647 	return B_OK;
648 }
649 
650 
651 status_t
652 PackageWriterImpl::_CheckLicenses()
653 {
654 	BPath systemLicensePath;
655 	status_t result
656 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
657 		= find_directory(B_SYSTEM_DATA_DIRECTORY, &systemLicensePath);
658 #else
659 		= systemLicensePath.SetTo(HAIKU_BUILD_SYSTEM_DATA_DIRECTORY);
660 #endif
661 	if (result != B_OK) {
662 		fListener->PrintError("unable to find system data path!\n");
663 		return result;
664 	}
665 	if ((result = systemLicensePath.Append("licenses")) != B_OK) {
666 		fListener->PrintError("unable to append to system data path!\n");
667 		return result;
668 	}
669 
670 	BDirectory systemLicenseDir(systemLicensePath.Path());
671 
672 	const BObjectList<BString>& licenseList = fPackageInfo.LicenseList();
673 	for (int i = 0; i < licenseList.CountItems(); ++i) {
674 		const BString& licenseName = *licenseList.ItemAt(i);
675 		if (licenseName == kPublicDomainLicenseName)
676 			continue;
677 
678 		BEntry license;
679 		if (systemLicenseDir.FindEntry(licenseName.String(), &license) == B_OK)
680 			continue;
681 
682 		// license is not a system license, so it must be contained in package
683 		BString licensePath("data/licenses/");
684 		licensePath << licenseName;
685 
686 		if (!_IsEntryInPackage(licensePath)) {
687 			fListener->PrintError("License '%s' isn't contained in package!\n",
688 				licenseName.String());
689 			return B_BAD_DATA;
690 		}
691 	}
692 
693 	return B_OK;
694 }
695 
696 
697 bool
698 PackageWriterImpl::_IsEntryInPackage(const char* fileName)
699 {
700 	const char* originalFileName = fileName;
701 
702 	// find the closest ancestor of the entry that is in the added entries
703 	bool added = false;
704 	Entry* entry = fRootEntry;
705 	while (entry != NULL) {
706 		if (!entry->IsImplicit()) {
707 			added = true;
708 			break;
709 		}
710 
711 		if (*fileName == '\0')
712 			break;
713 
714 		const char* nextSlash = strchr(fileName, '/');
715 
716 		if (nextSlash == NULL) {
717 			// no slash, just the file name
718 			size_t length = strlen(fileName);
719 			entry  = entry->GetChild(fileName, length);
720 			fileName += length;
721 			continue;
722 		}
723 
724 		// find the start of the next component, skipping slashes
725 		const char* nextComponent = nextSlash + 1;
726 		while (*nextComponent == '/')
727 			nextComponent++;
728 
729 		entry = entry->GetChild(fileName, nextSlash - fileName);
730 
731 		fileName = nextComponent;
732 	}
733 
734 	if (added) {
735 		// the entry itself or one of its ancestors has been added to the
736 		// package explicitly -- stat it, to see, if it exists
737 		struct stat st;
738 		if (entry->FD() >= 0) {
739 			if (fstatat(entry->FD(), *fileName != '\0' ? fileName : NULL, &st,
740 					AT_SYMLINK_NOFOLLOW) == 0) {
741 				return true;
742 			}
743 		} else {
744 			if (lstat(originalFileName, &st) == 0)
745 				return true;
746 		}
747 	}
748 
749 	// In update mode the entry might already be in the package.
750 	Attribute* attribute = fRootAttribute;
751 	fileName = originalFileName;
752 
753 	while (attribute != NULL) {
754 		if (*fileName == '\0')
755 			return true;
756 
757 		const char* nextSlash = strchr(fileName, '/');
758 
759 		if (nextSlash == NULL) {
760 			// no slash, just the file name
761 			return attribute->FindEntryChild(fileName) != NULL;
762 		}
763 
764 		// find the start of the next component, skipping slashes
765 		const char* nextComponent = nextSlash + 1;
766 		while (*nextComponent == '/')
767 			nextComponent++;
768 
769 		attribute = attribute->FindEntryChild(fileName, nextSlash - fileName);
770 
771 		fileName = nextComponent;
772 	}
773 
774 	return false;
775 }
776 
777 
778 void
779 PackageWriterImpl::_UpdateReadPackageInfo()
780 {
781 	// get the .PackageInfo entry attribute
782 	Attribute* attribute = fRootAttribute->FindEntryChild(
783 		B_HPKG_PACKAGE_INFO_FILE_NAME);
784 	if (attribute == NULL) {
785 		fListener->PrintError("No %s in package file.\n",
786 			B_HPKG_PACKAGE_INFO_FILE_NAME);
787 		throw status_t(B_BAD_DATA);
788 	}
789 
790 	// get the data attribute
791 	Attribute* dataAttribute = attribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_DATA);
792 	if (dataAttribute == NULL)  {
793 		fListener->PrintError("%s entry in package file doesn't have data.\n",
794 			B_HPKG_PACKAGE_INFO_FILE_NAME);
795 		throw status_t(B_BAD_DATA);
796 	}
797 
798 	AttributeValue& value = dataAttribute->value;
799 	if (value.type != B_HPKG_ATTRIBUTE_TYPE_RAW) {
800 		fListener->PrintError("%s entry in package file has an invalid data "
801 			"attribute (not of type raw).\n", B_HPKG_PACKAGE_INFO_FILE_NAME);
802 		throw status_t(B_BAD_DATA);
803 	}
804 
805 	BPackageData data;
806 	if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE)
807 		data.SetData(value.data.size, value.data.raw);
808 	else
809 		data.SetData(value.data.size, value.data.offset + fHeapOffset);
810 
811 	// get the compression
812 	uint8 compression = B_HPKG_DEFAULT_DATA_COMPRESSION;
813 	if (Attribute* compressionAttribute = dataAttribute->ChildWithID(
814 			B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION)) {
815 		if (compressionAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) {
816 			fListener->PrintError("%s entry in package file has an invalid "
817 				"data compression attribute (not of type uint).\n",
818 				B_HPKG_PACKAGE_INFO_FILE_NAME);
819 			throw status_t(B_BAD_DATA);
820 		}
821 		compression = compressionAttribute->value.unsignedInt;
822 	}
823 
824 	data.SetCompression(compression);
825 
826 	// get the size
827 	uint64 size;
828 	Attribute* sizeAttribute = dataAttribute->ChildWithID(
829 		B_HPKG_ATTRIBUTE_ID_DATA_SIZE);
830 	if (sizeAttribute == NULL) {
831 		size = value.data.size;
832 	} else if (sizeAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) {
833 		fListener->PrintError("%s entry in package file has an invalid data "
834 			"size attribute (not of type uint).\n",
835 			B_HPKG_PACKAGE_INFO_FILE_NAME);
836 		throw status_t(B_BAD_DATA);
837 	} else
838 		size = sizeAttribute->value.unsignedInt;
839 
840 	data.SetUncompressedSize(size);
841 
842 	// get the chunk size
843 	uint64 chunkSize = compression == B_HPKG_COMPRESSION_ZLIB
844 		? B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB : 0;
845 	if (Attribute* chunkSizeAttribute = dataAttribute->ChildWithID(
846 			B_HPKG_ATTRIBUTE_ID_DATA_CHUNK_SIZE)) {
847 		if (chunkSizeAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) {
848 			fListener->PrintError("%s entry in package file has an invalid "
849 				"data chunk size attribute (not of type uint).\n",
850 				B_HPKG_PACKAGE_INFO_FILE_NAME);
851 			throw status_t(B_BAD_DATA);
852 		}
853 		chunkSize = chunkSizeAttribute->value.unsignedInt;
854 	}
855 
856 	data.SetChunkSize(chunkSize);
857 
858 	// read the value into a string
859 	BString valueString;
860 	char* valueBuffer = valueString.LockBuffer(size);
861 	if (valueBuffer == NULL)
862 		throw std::bad_alloc();
863 
864 	if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
865 		// data encoded inline -- just copy to buffer
866 		if (size != value.data.size) {
867 			fListener->PrintError("%s entry in package file has an invalid "
868 				"data attribute (mismatching size).\n",
869 				B_HPKG_PACKAGE_INFO_FILE_NAME);
870 			throw status_t(B_BAD_DATA);
871 		}
872 		memcpy(valueBuffer, value.data.raw, value.data.size);
873 	} else {
874 		// data on heap -- read from there
875 		BBlockBufferCacheNoLock	bufferCache(16 * 1024, 1);
876 		status_t error = bufferCache.Init();
877 		if (error != B_OK) {
878 			fListener->PrintError("Failed to initialize buffer cache: %s\n",
879 				strerror(error));
880 			throw status_t(error);
881 		}
882 
883 		// create a BPackageDataReader
884 		BFDDataReader packageFileReader(FD());
885 		BPackageDataReader* reader;
886 		error = BPackageDataReaderFactory(&bufferCache)
887 			.CreatePackageDataReader(&packageFileReader, data, reader);
888 		if (error != B_OK) {
889 			fListener->PrintError("Failed to create package data reader: %s\n",
890 				strerror(error));
891 			throw status_t(error);
892 		}
893 		ObjectDeleter<BPackageDataReader> readerDeleter(reader);
894 
895 		// read the data
896 		error = reader->ReadData(0, valueBuffer, size);
897 		if (error != B_OK) {
898 			fListener->PrintError("Failed to read data of %s entry in package "
899 				"file: %s\n", B_HPKG_PACKAGE_INFO_FILE_NAME, strerror(error));
900 			throw status_t(error);
901 		}
902 	}
903 
904 	valueString.UnlockBuffer();
905 
906 	// parse the package info
907 	status_t error = fPackageInfo.ReadFromConfigString(valueString);
908 	if (error != B_OK) {
909 		fListener->PrintError("Failed to parse package info data from package "
910 			"file: %s\n", strerror(error));
911 		throw status_t(error);
912 	}
913 }
914 
915 
916 void
917 PackageWriterImpl::_UpdateCheckEntryCollisions()
918 {
919 	for (EntryList::ConstIterator it = fRootEntry->ChildIterator();
920 			Entry* entry = it.Next();) {
921 		char pathBuffer[B_PATH_NAME_LENGTH];
922 		pathBuffer[0] = '\0';
923 		_UpdateCheckEntryCollisions(fRootAttribute, AT_FDCWD, entry,
924 			entry->Name(), pathBuffer);
925 	}
926 }
927 
928 
929 void
930 PackageWriterImpl::_UpdateCheckEntryCollisions(Attribute* parentAttribute,
931 	int dirFD, Entry* entry, const char* fileName, char* pathBuffer)
932 {
933 	bool isImplicitEntry = entry != NULL && entry->IsImplicit();
934 
935 	SubPathAdder pathAdder(fListener, pathBuffer, fileName);
936 
937 	// Check wether there's an entry attribute for this entry. If not, we can
938 	// ignore this entry.
939 	Attribute* entryAttribute = parentAttribute->FindEntryChild(fileName);
940 	if (entryAttribute == NULL)
941 		return;
942 
943 	// open the node
944 	int fd;
945 	FileDescriptorCloser fdCloser;
946 
947 	if (entry != NULL && entry->FD() >= 0) {
948 		// a file descriptor is already given -- use that
949 		fd = entry->FD();
950 	} else {
951 		fd = openat(dirFD, fileName,
952 			O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE));
953 		if (fd < 0) {
954 			fListener->PrintError("Failed to open entry \"%s\": %s\n",
955 				pathBuffer, strerror(errno));
956 			throw status_t(errno);
957 		}
958 		fdCloser.SetTo(fd);
959 	}
960 
961 	// stat the node
962 	struct stat st;
963 	if (fstat(fd, &st) < 0) {
964 		fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer,
965 			strerror(errno));
966 		throw status_t(errno);
967 	}
968 
969 	// implicit entries must be directories
970 	if (isImplicitEntry && !S_ISDIR(st.st_mode)) {
971 		fListener->PrintError("Non-leaf path component \"%s\" is not a "
972 			"directory.\n", pathBuffer);
973 		throw status_t(B_BAD_VALUE);
974 	}
975 
976 	// get the pre-existing node's file type
977 	uint32 preExistingFileType = B_HPKG_DEFAULT_FILE_TYPE;
978 	if (Attribute* fileTypeAttribute
979 			= entryAttribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_FILE_TYPE)) {
980 		if (fileTypeAttribute->value.type == B_HPKG_ATTRIBUTE_TYPE_UINT)
981 			preExistingFileType = fileTypeAttribute->value.unsignedInt;
982 	}
983 
984 	// Compare the node type with that of the pre-existing one.
985 	if (!S_ISDIR(st.st_mode)) {
986 		// the pre-existing must not a directory either -- we'll remove it
987 		if (preExistingFileType == B_HPKG_FILE_TYPE_DIRECTORY) {
988 			fListener->PrintError("Specified file \"%s\" clashes with an "
989 				"archived directory.\n", pathBuffer);
990 			throw status_t(B_BAD_VALUE);
991 		}
992 
993 		if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) {
994 			fListener->PrintError("Specified file \"%s\" clashes with an "
995 				"archived file.\n", pathBuffer);
996 			throw status_t(B_FILE_EXISTS);
997 		}
998 
999 		parentAttribute->RemoveChild(entryAttribute);
1000 		_AttributeRemoved(entryAttribute);
1001 
1002 		return;
1003 	}
1004 
1005 	// the pre-existing entry needs to be a directory too -- we will merge
1006 	if (preExistingFileType != B_HPKG_FILE_TYPE_DIRECTORY) {
1007 		fListener->PrintError("Specified directory \"%s\" clashes with an "
1008 			"archived non-directory.\n", pathBuffer);
1009 		throw status_t(B_BAD_VALUE);
1010 	}
1011 
1012 	// directory -- recursively add children
1013 	if (isImplicitEntry) {
1014 		// this is an implicit entry -- just check the child entries
1015 		for (EntryList::ConstIterator it = entry->ChildIterator();
1016 				Entry* child = it.Next();) {
1017 			_UpdateCheckEntryCollisions(entryAttribute, fd, child,
1018 				child->Name(), pathBuffer);
1019 		}
1020 	} else {
1021 		// explicitly specified directory -- we need to read the directory
1022 
1023 		// first we check for colliding node attributes, though
1024 		if (DIR* attrDir = fs_fopen_attr_dir(fd)) {
1025 			CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir);
1026 
1027 			while (dirent* entry = fs_read_attr_dir(attrDir)) {
1028 				attr_info attrInfo;
1029 				if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) {
1030 					fListener->PrintError(
1031 						"Failed to stat attribute \"%s\" of directory \"%s\": "
1032 						"%s\n", entry->d_name, pathBuffer, strerror(errno));
1033 					throw status_t(errno);
1034 				}
1035 
1036 				// check whether the attribute exists
1037 				Attribute* attributeAttribute
1038 					= entryAttribute->FindNodeAttributeChild(entry->d_name);
1039 				if (attributeAttribute == NULL)
1040 					continue;
1041 
1042 				if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) {
1043 					fListener->PrintError("Attribute \"%s\" of specified "
1044 						"directory \"%s\" clashes with an archived "
1045 						"attribute.\n", pathBuffer);
1046 					throw status_t(B_FILE_EXISTS);
1047 				}
1048 
1049 				// remove it
1050 				entryAttribute->RemoveChild(attributeAttribute);
1051 				_AttributeRemoved(attributeAttribute);
1052 			}
1053 		}
1054 
1055 		// we need to clone the directory FD for fdopendir()
1056 		int clonedFD = dup(fd);
1057 		if (clonedFD < 0) {
1058 			fListener->PrintError(
1059 				"Failed to dup() directory FD: %s\n", strerror(errno));
1060 			throw status_t(errno);
1061 		}
1062 
1063 		DIR* dir = fdopendir(clonedFD);
1064 		if (dir == NULL) {
1065 			fListener->PrintError(
1066 				"Failed to open directory \"%s\": %s\n", pathBuffer,
1067 				strerror(errno));
1068 			close(clonedFD);
1069 			throw status_t(errno);
1070 		}
1071 		CObjectDeleter<DIR, int> dirCloser(dir, closedir);
1072 
1073 		while (dirent* entry = readdir(dir)) {
1074 			// skip "." and ".."
1075 			if (strcmp(entry->d_name, ".") == 0
1076 				|| strcmp(entry->d_name, "..") == 0) {
1077 				continue;
1078 			}
1079 
1080 			_UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name,
1081 				pathBuffer);
1082 		}
1083 	}
1084 }
1085 
1086 
1087 void
1088 PackageWriterImpl::_CompactHeap()
1089 {
1090 	int32 count = fHeapRangesToRemove->CountRanges();
1091 	if (count == 0)
1092 		return;
1093 
1094 	// compute the move deltas for the ranges
1095 	Array<off_t> deltas;
1096 	off_t delta = 0;
1097 	for (int32 i = 0; i < count; i++) {
1098 		if (!deltas.Add(delta))
1099 			throw std::bad_alloc();
1100 
1101 		delta += fHeapRangesToRemove->RangeAt(i).size;
1102 	}
1103 
1104 	if (!deltas.Add(delta))
1105 		throw std::bad_alloc();
1106 
1107 	// offset the attributes
1108 	HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute(
1109 		fRootAttribute);
1110 
1111 	// move the heap chunks in the file around
1112 	off_t chunkOffset = fHeapOffset;
1113 	delta = 0;
1114 
1115 	for (int32 i = 0; i < count; i++) {
1116 		const Range<off_t>& range = fHeapRangesToRemove->RangeAt(i);
1117 
1118 		if (delta > 0 && chunkOffset < range.offset) {
1119 			// move chunk
1120 			_MoveHeapChunk(chunkOffset, chunkOffset - delta,
1121 				range.offset - chunkOffset);
1122 		}
1123 
1124 		chunkOffset = range.EndOffset();
1125 		delta += range.size;
1126 	}
1127 
1128 	// move the final chunk
1129 	off_t heapSize = fHeapEnd - fHeapOffset;
1130 	if (delta > 0 && chunkOffset < heapSize) {
1131 		_MoveHeapChunk(chunkOffset, chunkOffset - delta,
1132 			heapSize - chunkOffset);
1133 	}
1134 
1135 	fHeapEnd -= delta;
1136 }
1137 
1138 
1139 void
1140 PackageWriterImpl::_MoveHeapChunk(off_t fromOffset, off_t toOffset, off_t size)
1141 {
1142 	// convert heap offsets to file offsets
1143 	fromOffset += fHeapOffset;
1144 	toOffset += fHeapOffset;
1145 
1146 	while (size > 0) {
1147 		size_t toCopy = std::min(size, (off_t)fDataBufferSize);
1148 
1149 		// read data into buffer
1150 		ssize_t bytesRead = read_pos(FD(), fromOffset, fDataBuffer, toCopy);
1151 		if (bytesRead < 0) {
1152 			fListener->PrintError("Failed to read from package file: %s\n",
1153 				strerror(errno));
1154 			throw status_t(errno);
1155 		}
1156 		if ((size_t)bytesRead < toCopy) {
1157 			fListener->PrintError("Failed to read from package file (wanted "
1158 				"%zu bytes, got %zd).\n", toCopy, bytesRead);
1159 			throw status_t(B_IO_ERROR);
1160 		}
1161 
1162 		// write data to target offset
1163 		ssize_t bytesWritten = write_pos(FD(), toOffset, fDataBuffer, toCopy);
1164 		if (bytesWritten < 0) {
1165 			fListener->PrintError("Failed to write to package file: %s\n",
1166 				strerror(errno));
1167 			throw status_t(errno);
1168 		}
1169 		if ((size_t)bytesWritten < toCopy) {
1170 			fListener->PrintError("Failed to write to package file.\n");
1171 			throw status_t(B_IO_ERROR);
1172 		}
1173 
1174 		fromOffset += toCopy;
1175 		toOffset += toCopy;
1176 		size -= toCopy;
1177 	}
1178 }
1179 
1180 
1181 void
1182 PackageWriterImpl::_AttributeRemoved(Attribute* attribute)
1183 {
1184 	AttributeValue& value = attribute->value;
1185 	if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW
1186 		&& value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
1187 		if (!fHeapRangesToRemove->AddRange(value.data.offset, value.data.size))
1188 			throw std::bad_alloc();
1189 	}
1190 
1191 	for (DoublyLinkedList<Attribute>::Iterator it
1192 				= attribute->children.GetIterator();
1193 			Attribute* child = it.Next();) {
1194 		_AttributeRemoved(child);
1195 	}
1196 }
1197 
1198 
1199 status_t
1200 PackageWriterImpl::_Finish()
1201 {
1202 	// write entries
1203 	for (EntryList::ConstIterator it = fRootEntry->ChildIterator();
1204 			Entry* entry = it.Next();) {
1205 		char pathBuffer[B_PATH_NAME_LENGTH];
1206 		pathBuffer[0] = '\0';
1207 		_AddEntry(AT_FDCWD, entry, entry->Name(), pathBuffer);
1208 	}
1209 
1210 	off_t heapSize = fHeapEnd - fHeapOffset;
1211 
1212 	hpkg_header header;
1213 
1214 	// write the TOC and package attributes
1215 	_WriteTOC(header);
1216 	_WritePackageAttributes(header);
1217 
1218 	off_t totalSize = fHeapEnd;
1219 
1220 	// Truncate the file to the size it is supposed to have. In update mode, it
1221 	// can be greater when one or more files are shrunk. In creation mode, when
1222 	// writing compressed TOC or package attributes yields a larger size than
1223 	// uncompressed, the file size may also be greater than it should be.
1224 	if (ftruncate(FD(), totalSize) != 0) {
1225 		fListener->PrintError("Failed to truncate package file to new "
1226 			"size: %s\n", strerror(errno));
1227 		return errno;
1228 	}
1229 
1230 	fListener->OnPackageSizeInfo(fHeapOffset, heapSize,
1231 		B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed),
1232 		B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed),
1233 		totalSize);
1234 
1235 	// prepare the header
1236 
1237 	// general
1238 	header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_MAGIC);
1239 	header.header_size = B_HOST_TO_BENDIAN_INT16((uint16)fHeapOffset);
1240 	header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_VERSION);
1241 	header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
1242 
1243 	// write the header
1244 	WriteBuffer(&header, sizeof(hpkg_header), 0);
1245 
1246 	SetFinished(true);
1247 	return B_OK;
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)
1327 {
1328 	// prepare the writer (zlib writer on top of a file writer)
1329 	off_t startOffset = fHeapEnd;
1330 
1331 	// write the sections
1332 	uint32 compression = B_HPKG_COMPRESSION_ZLIB;
1333 	uint64 uncompressedStringsSize;
1334 	uint64 uncompressedMainSize;
1335 	uint64 tocUncompressedSize;
1336 	int32 cachedStringsWritten = _WriteTOCCompressed(uncompressedStringsSize,
1337 		uncompressedMainSize, tocUncompressedSize);
1338 
1339 	off_t endOffset = fHeapEnd;
1340 
1341 	if (endOffset - startOffset >= (off_t)tocUncompressedSize) {
1342 		// the compressed section isn't shorter -- write uncompressed
1343 		fHeapEnd = startOffset;
1344 		compression = B_HPKG_COMPRESSION_NONE;
1345 		cachedStringsWritten = _WriteTOCUncompressed(uncompressedStringsSize,
1346 			uncompressedMainSize, tocUncompressedSize);
1347 
1348 		endOffset = fHeapEnd;
1349 	}
1350 
1351 	fListener->OnTOCSizeInfo(uncompressedStringsSize, uncompressedMainSize,
1352 		tocUncompressedSize);
1353 
1354 	// update the header
1355 
1356 	// TOC
1357 	header.toc_compression = B_HOST_TO_BENDIAN_INT32(compression);
1358 	header.toc_length_compressed = B_HOST_TO_BENDIAN_INT64(
1359 		endOffset - startOffset);
1360 	header.toc_length_uncompressed = B_HOST_TO_BENDIAN_INT64(
1361 		tocUncompressedSize);
1362 
1363 	// TOC subsections
1364 	header.toc_strings_length = B_HOST_TO_BENDIAN_INT64(
1365 		uncompressedStringsSize);
1366 	header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten);
1367 }
1368 
1369 
1370 int32
1371 PackageWriterImpl::_WriteTOCCompressed(uint64& _uncompressedStringsSize,
1372 	uint64& _uncompressedMainSize, uint64& _tocUncompressedSize)
1373 {
1374 	FDDataWriter realWriter(FD(), fHeapEnd, fListener);
1375 	ZlibDataWriter zlibWriter(&realWriter);
1376 	SetDataWriter(&zlibWriter);
1377 	zlibWriter.Init();
1378 
1379 	// write the sections
1380 	int32 cachedStringsWritten
1381 		= _WriteTOCSections(_uncompressedStringsSize, _uncompressedMainSize);
1382 
1383 	// finish the writer
1384 	zlibWriter.Finish();
1385 	fHeapEnd = realWriter.Offset();
1386 	SetDataWriter(NULL);
1387 
1388 	_tocUncompressedSize = zlibWriter.BytesWritten();
1389 	return cachedStringsWritten;
1390 }
1391 
1392 
1393 int32
1394 PackageWriterImpl::_WriteTOCUncompressed(uint64& _uncompressedStringsSize,
1395 	uint64& _uncompressedMainSize, uint64& _tocUncompressedSize)
1396 {
1397 	FDDataWriter realWriter(FD(), fHeapEnd, fListener);
1398 	SetDataWriter(&realWriter);
1399 
1400 	// write the sections
1401 	int32 cachedStringsWritten
1402 		= _WriteTOCSections(_uncompressedStringsSize, _uncompressedMainSize);
1403 
1404 	fHeapEnd = realWriter.Offset();
1405 	SetDataWriter(NULL);
1406 
1407 	_tocUncompressedSize = realWriter.BytesWritten();
1408 	return cachedStringsWritten;
1409 }
1410 
1411 
1412 int32
1413 PackageWriterImpl::_WriteTOCSections(uint64& _stringsSize, uint64& _mainSize)
1414 {
1415 	// write the cached strings
1416 	uint64 cachedStringsOffset = DataWriter()->BytesWritten();
1417 	int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2);
1418 
1419 	// write the main TOC section
1420 	uint64 mainOffset = DataWriter()->BytesWritten();
1421 	_WriteAttributeChildren(fRootAttribute);
1422 
1423 	_stringsSize = mainOffset - cachedStringsOffset;
1424 	_mainSize = DataWriter()->BytesWritten() - mainOffset;
1425 
1426 	return cachedStringsWritten;
1427 }
1428 
1429 
1430 void
1431 PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute)
1432 {
1433 	DoublyLinkedList<Attribute>::Iterator it
1434 		= attribute->children.GetIterator();
1435 	while (Attribute* child = it.Next()) {
1436 		// write tag
1437 		uint8 encoding = child->value.ApplicableEncoding();
1438 		WriteUnsignedLEB128(HPKG_ATTRIBUTE_TAG_COMPOSE(child->id,
1439 			child->value.type, encoding, !child->children.IsEmpty()));
1440 
1441 		// write value
1442 		WriteAttributeValue(child->value, encoding);
1443 
1444 		if (!child->children.IsEmpty())
1445 			_WriteAttributeChildren(child);
1446 	}
1447 
1448 	WriteUnsignedLEB128(0);
1449 }
1450 
1451 
1452 void
1453 PackageWriterImpl::_WritePackageAttributes(hpkg_header& header)
1454 {
1455 	// write the package attributes (zlib writer on top of a file writer)
1456 	off_t startOffset = fHeapEnd;
1457 
1458 	uint32 compression = B_HPKG_COMPRESSION_ZLIB;
1459 	uint32 stringsLengthUncompressed;
1460 	uint32 attributesLengthUncompressed;
1461 	uint32 stringsCount = _WritePackageAttributesCompressed(
1462 		stringsLengthUncompressed, attributesLengthUncompressed);
1463 
1464 	off_t endOffset = fHeapEnd;
1465 
1466 	if ((off_t)attributesLengthUncompressed <= endOffset - startOffset) {
1467 		// the compressed section isn't shorter -- write uncompressed
1468 		fHeapEnd = startOffset;
1469 		compression = B_HPKG_COMPRESSION_NONE;
1470 		stringsCount = _WritePackageAttributesUncompressed(
1471 			stringsLengthUncompressed, attributesLengthUncompressed);
1472 
1473 		endOffset = fHeapEnd;
1474 	}
1475 
1476 	fListener->OnPackageAttributesSizeInfo(stringsCount,
1477 		attributesLengthUncompressed);
1478 
1479 	// update the header
1480 	header.attributes_compression = B_HOST_TO_BENDIAN_INT32(compression);
1481 	header.attributes_length_compressed
1482 		= B_HOST_TO_BENDIAN_INT32(endOffset - startOffset);
1483 	header.attributes_length_uncompressed
1484 		= B_HOST_TO_BENDIAN_INT32(attributesLengthUncompressed);
1485 	header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount);
1486 	header.attributes_strings_length
1487 		= B_HOST_TO_BENDIAN_INT32(stringsLengthUncompressed);
1488 }
1489 
1490 
1491 uint32
1492 PackageWriterImpl::_WritePackageAttributesCompressed(
1493 	uint32& _stringsLengthUncompressed, uint32& _attributesLengthUncompressed)
1494 {
1495 	off_t startOffset = fHeapEnd;
1496 	FDDataWriter realWriter(FD(), startOffset, fListener);
1497 	ZlibDataWriter zlibWriter(&realWriter);
1498 	SetDataWriter(&zlibWriter);
1499 	zlibWriter.Init();
1500 
1501 	// write cached strings and package attributes tree
1502 	uint32 stringsCount = WritePackageAttributes(PackageAttributes(),
1503 		_stringsLengthUncompressed);
1504 
1505 	zlibWriter.Finish();
1506 	fHeapEnd = realWriter.Offset();
1507 	SetDataWriter(NULL);
1508 
1509 	_attributesLengthUncompressed = zlibWriter.BytesWritten();
1510 	return stringsCount;
1511 }
1512 
1513 
1514 uint32
1515 PackageWriterImpl::_WritePackageAttributesUncompressed(
1516 	uint32& _stringsLengthUncompressed, uint32& _attributesLengthUncompressed)
1517 {
1518 	off_t startOffset = fHeapEnd;
1519 	FDDataWriter realWriter(FD(), startOffset, fListener);
1520 
1521 	SetDataWriter(&realWriter);
1522 
1523 	// write cached strings and package attributes tree
1524 	uint32 stringsCount = WritePackageAttributes(PackageAttributes(),
1525 		_stringsLengthUncompressed);
1526 
1527 	fHeapEnd = realWriter.Offset();
1528 	SetDataWriter(NULL);
1529 
1530 	_attributesLengthUncompressed = realWriter.BytesWritten();
1531 	return stringsCount;
1532 }
1533 
1534 
1535 void
1536 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName,
1537 	char* pathBuffer)
1538 {
1539 	bool isImplicitEntry = entry != NULL && entry->IsImplicit();
1540 
1541 	SubPathAdder pathAdder(fListener, pathBuffer, fileName);
1542 	if (!isImplicitEntry)
1543 		fListener->OnEntryAdded(pathBuffer);
1544 
1545 	// open the node
1546 	int fd;
1547 	FileDescriptorCloser fdCloser;
1548 
1549 	if (entry != NULL && entry->FD() >= 0) {
1550 		// a file descriptor is already given -- use that
1551 		fd = entry->FD();
1552 	} else {
1553 		fd = openat(dirFD, fileName,
1554 			O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE));
1555 		if (fd < 0) {
1556 			fListener->PrintError("Failed to open entry \"%s\": %s\n",
1557 				pathBuffer, strerror(errno));
1558 			throw status_t(errno);
1559 		}
1560 		fdCloser.SetTo(fd);
1561 	}
1562 
1563 	// stat the node
1564 	struct stat st;
1565 	if (fstat(fd, &st) < 0) {
1566 		fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer,
1567 			strerror(errno));
1568 		throw status_t(errno);
1569 	}
1570 
1571 	// implicit entries must be directories
1572 	if (isImplicitEntry && !S_ISDIR(st.st_mode)) {
1573 		fListener->PrintError("Non-leaf path component \"%s\" is not a "
1574 			"directory\n", pathBuffer);
1575 		throw status_t(B_BAD_VALUE);
1576 	}
1577 
1578 	// In update mode we don't need to add an entry attribute for an implicit
1579 	// directory, if there already is one.
1580 	Attribute* entryAttribute = NULL;
1581 	if (S_ISDIR(st.st_mode) && (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
1582 		entryAttribute = fTopAttribute->FindEntryChild(fileName);
1583 		if (entryAttribute != NULL && isImplicitEntry) {
1584 			Stacker<Attribute> entryAttributeStacker(fTopAttribute,
1585 				entryAttribute);
1586 			_AddDirectoryChildren(entry, fd, pathBuffer);
1587 			return;
1588 		}
1589 	}
1590 
1591 	// check/translate the node type
1592 	uint8 fileType;
1593 	uint32 defaultPermissions;
1594 	if (S_ISREG(st.st_mode)) {
1595 		fileType = B_HPKG_FILE_TYPE_FILE;
1596 		defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS;
1597 	} else if (S_ISLNK(st.st_mode)) {
1598 		fileType = B_HPKG_FILE_TYPE_SYMLINK;
1599 		defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS;
1600 	} else if (S_ISDIR(st.st_mode)) {
1601 		fileType = B_HPKG_FILE_TYPE_DIRECTORY;
1602 		defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS;
1603 	} else {
1604 		// unsupported node type
1605 		fListener->PrintError("Unsupported node type, entry: \"%s\"\n",
1606 			pathBuffer);
1607 		throw status_t(B_UNSUPPORTED);
1608 	}
1609 
1610 	// add attribute entry, if it doesn't already exist (update mode, directory)
1611 	bool isNewEntry = entryAttribute == NULL;
1612 	if (entryAttribute == NULL) {
1613 		entryAttribute = _AddStringAttribute(
1614 			B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName);
1615 	}
1616 
1617 	Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute);
1618 
1619 	if (isNewEntry) {
1620 		// add stat data
1621 		if (fileType != B_HPKG_DEFAULT_FILE_TYPE)
1622 			_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType);
1623 		if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) {
1624 			_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS,
1625 				uint32(st.st_mode & ALLPERMS));
1626 		}
1627 		_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime));
1628 		_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime));
1629 #ifdef __HAIKU__
1630 		_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime));
1631 #else
1632 		_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime));
1633 #endif
1634 		// TODO: File user/group!
1635 
1636 		// add file data/symlink path
1637 		if (S_ISREG(st.st_mode)) {
1638 			// regular file -- add data
1639 			if (st.st_size > 0) {
1640 				BFDDataReader dataReader(fd);
1641 				status_t error = _AddData(dataReader, st.st_size);
1642 				if (error != B_OK)
1643 					throw status_t(error);
1644 			}
1645 		} else if (S_ISLNK(st.st_mode)) {
1646 			// symlink -- add link address
1647 			char path[B_PATH_NAME_LENGTH + 1];
1648 			ssize_t bytesRead = readlinkat(dirFD, fileName, path,
1649 				B_PATH_NAME_LENGTH);
1650 			if (bytesRead < 0) {
1651 				fListener->PrintError("Failed to read symlink \"%s\": %s\n",
1652 					pathBuffer, strerror(errno));
1653 				throw status_t(errno);
1654 			}
1655 
1656 			path[bytesRead] = '\0';
1657 			_AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path);
1658 		}
1659 	}
1660 
1661 	// add attributes
1662 	if (DIR* attrDir = fs_fopen_attr_dir(fd)) {
1663 		CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir);
1664 
1665 		while (dirent* entry = fs_read_attr_dir(attrDir)) {
1666 			attr_info attrInfo;
1667 			if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) {
1668 				fListener->PrintError(
1669 					"Failed to stat attribute \"%s\" of file \"%s\": %s\n",
1670 					entry->d_name, pathBuffer, strerror(errno));
1671 				throw status_t(errno);
1672 			}
1673 
1674 			// create attribute entry
1675 			Attribute* attributeAttribute = _AddStringAttribute(
1676 				B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name);
1677 			Stacker<Attribute> attributeAttributeStacker(fTopAttribute,
1678 				attributeAttribute);
1679 
1680 			// add type
1681 			_AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE,
1682 				(uint32)attrInfo.type);
1683 
1684 			// add data
1685 			BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type);
1686 			status_t error = _AddData(dataReader, attrInfo.size);
1687 			if (error != B_OK)
1688 				throw status_t(error);
1689 		}
1690 	}
1691 
1692 	if (S_ISDIR(st.st_mode))
1693 		_AddDirectoryChildren(entry, fd, pathBuffer);
1694 }
1695 
1696 
1697 void
1698 PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer)
1699 {
1700 	// directory -- recursively add children
1701 	if (entry != NULL && entry->IsImplicit()) {
1702 		// this is an implicit entry -- just add it's children
1703 		for (EntryList::ConstIterator it = entry->ChildIterator();
1704 				Entry* child = it.Next();) {
1705 			_AddEntry(fd, child, child->Name(), pathBuffer);
1706 		}
1707 	} else {
1708 		// we need to clone the directory FD for fdopendir()
1709 		int clonedFD = dup(fd);
1710 		if (clonedFD < 0) {
1711 			fListener->PrintError(
1712 				"Failed to dup() directory FD: %s\n", strerror(errno));
1713 			throw status_t(errno);
1714 		}
1715 
1716 		DIR* dir = fdopendir(clonedFD);
1717 		if (dir == NULL) {
1718 			fListener->PrintError(
1719 				"Failed to open directory \"%s\": %s\n", pathBuffer,
1720 				strerror(errno));
1721 			close(clonedFD);
1722 			throw status_t(errno);
1723 		}
1724 		CObjectDeleter<DIR, int> dirCloser(dir, closedir);
1725 
1726 		while (dirent* entry = readdir(dir)) {
1727 			// skip "." and ".."
1728 			if (strcmp(entry->d_name, ".") == 0
1729 				|| strcmp(entry->d_name, "..") == 0) {
1730 				continue;
1731 			}
1732 
1733 			_AddEntry(fd, NULL, entry->d_name, pathBuffer);
1734 		}
1735 	}
1736 }
1737 
1738 
1739 PackageWriterImpl::Attribute*
1740 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id,
1741 	const AttributeValue& value)
1742 {
1743 	Attribute* attribute = new Attribute(id);
1744 
1745 	attribute->value = value;
1746 	fTopAttribute->AddChild(attribute);
1747 
1748 	return attribute;
1749 }
1750 
1751 
1752 PackageWriterImpl::Attribute*
1753 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID,
1754 	const char* value)
1755 {
1756 	AttributeValue attributeValue;
1757 	attributeValue.SetTo(fStringCache.Get(value));
1758 	return _AddAttribute(attributeID, attributeValue);
1759 }
1760 
1761 
1762 PackageWriterImpl::Attribute*
1763 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID,
1764 	uint64 dataSize, uint64 dataOffset)
1765 {
1766 	AttributeValue attributeValue;
1767 	attributeValue.SetToData(dataSize, dataOffset);
1768 	return _AddAttribute(attributeID, attributeValue);
1769 }
1770 
1771 
1772 PackageWriterImpl::Attribute*
1773 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID,
1774 	uint64 dataSize, const uint8* data)
1775 {
1776 	AttributeValue attributeValue;
1777 	attributeValue.SetToData(dataSize, data);
1778 	return _AddAttribute(attributeID, attributeValue);
1779 }
1780 
1781 
1782 status_t
1783 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size)
1784 {
1785 	// add short data inline
1786 	if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) {
1787 		uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE];
1788 		status_t error = dataReader.ReadData(0, buffer, size);
1789 		if (error != B_OK) {
1790 			fListener->PrintError("Failed to read data: %s\n", strerror(error));
1791 			return error;
1792 		}
1793 
1794 		_AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer);
1795 		return B_OK;
1796 	}
1797 
1798 	// longer data -- try to compress
1799 	uint64 dataOffset = fHeapEnd;
1800 
1801 	uint64 compression = B_HPKG_COMPRESSION_NONE;
1802 	uint64 compressedSize;
1803 
1804 	status_t error = _WriteZlibCompressedData(dataReader, size, dataOffset,
1805 		compressedSize);
1806 	if (error == B_OK) {
1807 		compression = B_HPKG_COMPRESSION_ZLIB;
1808 	} else {
1809 		error = _WriteUncompressedData(dataReader, size, dataOffset);
1810 		compressedSize = size;
1811 	}
1812 	if (error != B_OK)
1813 		return error;
1814 
1815 	fHeapEnd = dataOffset + compressedSize;
1816 
1817 	// add data attribute
1818 	Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA,
1819 		compressedSize, dataOffset - fHeapOffset);
1820 	Stacker<Attribute> attributeAttributeStacker(fTopAttribute, dataAttribute);
1821 
1822 	// if compressed, add compression attributes
1823 	if (compression != B_HPKG_COMPRESSION_NONE) {
1824 		_AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION, compression);
1825 		_AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_SIZE, (uint64)size);
1826 			// uncompressed size
1827 	}
1828 
1829 	return B_OK;
1830 }
1831 
1832 
1833 status_t
1834 PackageWriterImpl::_WriteUncompressedData(BDataReader& dataReader, off_t size,
1835 	uint64 writeOffset)
1836 {
1837 	// copy the data to the heap
1838 	off_t readOffset = 0;
1839 	off_t remainingSize = size;
1840 	while (remainingSize > 0) {
1841 		// read data
1842 		size_t toCopy = std::min(remainingSize, (off_t)fDataBufferSize);
1843 		status_t error = dataReader.ReadData(readOffset, fDataBuffer, toCopy);
1844 		if (error != B_OK) {
1845 			fListener->PrintError("Failed to read data: %s\n", strerror(error));
1846 			return error;
1847 		}
1848 
1849 		// write to heap
1850 		ssize_t bytesWritten = pwrite(FD(), fDataBuffer, toCopy, writeOffset);
1851 		if (bytesWritten < 0) {
1852 			fListener->PrintError("Failed to write data: %s\n",
1853 				strerror(errno));
1854 			return errno;
1855 		}
1856 		if ((size_t)bytesWritten != toCopy) {
1857 			fListener->PrintError("Failed to write all data\n");
1858 			return B_ERROR;
1859 		}
1860 
1861 		remainingSize -= toCopy;
1862 		readOffset += toCopy;
1863 		writeOffset += toCopy;
1864 	}
1865 
1866 	return B_OK;
1867 }
1868 
1869 
1870 status_t
1871 PackageWriterImpl::_WriteZlibCompressedData(BDataReader& dataReader, off_t size,
1872 	uint64 writeOffset, uint64& _compressedSize)
1873 {
1874 	// Use zlib compression only for data large enough.
1875 	if (size < (off_t)kZlibCompressionSizeThreshold)
1876 		return B_BAD_VALUE;
1877 
1878 	// fDataBuffer is 2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, so split it into
1879 	// two halves we can use for reading and compressing
1880 	const size_t chunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB;
1881 	uint8* inputBuffer = (uint8*)fDataBuffer;
1882 	uint8* outputBuffer = (uint8*)fDataBuffer + chunkSize;
1883 
1884 	// account for the offset table
1885 	uint64 chunkCount = (size + (chunkSize - 1)) / chunkSize;
1886 	off_t offsetTableOffset = writeOffset;
1887 	uint64* offsetTable = NULL;
1888 	if (chunkCount > 1) {
1889 		offsetTable = new uint64[chunkCount - 1];
1890 		writeOffset = offsetTableOffset + (chunkCount - 1) * sizeof(uint64);
1891 	}
1892 	ArrayDeleter<uint64> offsetTableDeleter(offsetTable);
1893 
1894 	const uint64 dataOffset = writeOffset;
1895 	const uint64 dataEndLimit = offsetTableOffset + size;
1896 
1897 	// read the data, compress them and write them to the heap
1898 	off_t readOffset = 0;
1899 	off_t remainingSize = size;
1900 	uint64 chunkIndex = 0;
1901 	while (remainingSize > 0) {
1902 		// read data
1903 		size_t toCopy = std::min(remainingSize, (off_t)chunkSize);
1904 		status_t error = dataReader.ReadData(readOffset, inputBuffer, toCopy);
1905 		if (error != B_OK) {
1906 			fListener->PrintError("Failed to read data: %s\n", strerror(error));
1907 			return error;
1908 		}
1909 
1910 		// compress
1911 		size_t compressedSize;
1912 		error = ZlibCompressor::CompressSingleBuffer(inputBuffer, toCopy,
1913 			outputBuffer, toCopy, compressedSize);
1914 
1915 		const void* writeBuffer;
1916 		size_t bytesToWrite;
1917 		if (error == B_OK) {
1918 			writeBuffer = outputBuffer;
1919 			bytesToWrite = compressedSize;
1920 		} else {
1921 			if (error != B_BUFFER_OVERFLOW)
1922 				return error;
1923 			writeBuffer = inputBuffer;
1924 			bytesToWrite = toCopy;
1925 		}
1926 
1927 		// check the total compressed data size
1928 		if (writeOffset + bytesToWrite >= dataEndLimit)
1929 			return B_BUFFER_OVERFLOW;
1930 
1931 		if (chunkIndex > 0)
1932 			offsetTable[chunkIndex - 1] = writeOffset - dataOffset;
1933 
1934 		// write to heap
1935 		ssize_t bytesWritten = pwrite(FD(), writeBuffer, bytesToWrite,
1936 			writeOffset);
1937 		if (bytesWritten < 0) {
1938 			fListener->PrintError("Failed to write data: %s\n",
1939 				strerror(errno));
1940 			return errno;
1941 		}
1942 		if ((size_t)bytesWritten != bytesToWrite) {
1943 			fListener->PrintError("Failed to write all data\n");
1944 			return B_ERROR;
1945 		}
1946 
1947 		remainingSize -= toCopy;
1948 		readOffset += toCopy;
1949 		writeOffset += bytesToWrite;
1950 		chunkIndex++;
1951 	}
1952 
1953 	// write the offset table
1954 	if (chunkCount > 1) {
1955 		size_t bytesToWrite = (chunkCount - 1) * sizeof(uint64);
1956 		ssize_t bytesWritten = pwrite(FD(), offsetTable, bytesToWrite,
1957 			offsetTableOffset);
1958 		if (bytesWritten < 0) {
1959 			fListener->PrintError("Failed to write data: %s\n",
1960 				strerror(errno));
1961 			return errno;
1962 		}
1963 		if ((size_t)bytesWritten != bytesToWrite) {
1964 			fListener->PrintError("Failed to write all data\n");
1965 			return B_ERROR;
1966 		}
1967 	}
1968 
1969 	_compressedSize = writeOffset - offsetTableOffset;
1970 	return B_OK;
1971 }
1972 
1973 
1974 }	// namespace BPrivate
1975 
1976 }	// namespace BHPKG
1977 
1978 }	// namespace BPackageKit
1979