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