xref: /haiku/src/kits/package/hpkg/PackageWriterImpl.cpp (revision 7a74a5df454197933bc6e80a542102362ee98703)
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