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