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