BPicture: Fix archive constructor.
[haiku.git] / src / kits / package / hpkg / PackageWriterImpl.cpp
blob9ba1d4252a07970716347af8560b0886e67fd1ed
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 */
8 #include <package/hpkg/PackageWriterImpl.h>
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>
19 #include <algorithm>
20 #include <new>
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>
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>
35 #include <AutoDeleter.h>
36 #include <RangeArray.h>
38 #include <package/hpkg/HPKGDefsPrivate.h>
40 #include <package/hpkg/DataReader.h>
41 #include <package/hpkg/PackageFileHeapReader.h>
42 #include <package/hpkg/PackageFileHeapWriter.h>
43 #include <package/hpkg/PackageReaderImpl.h>
44 #include <package/hpkg/Stacker.h>
47 using BPrivate::FileDescriptorCloser;
50 static const char* const kPublicDomainLicenseName = "Public Domain";
53 #include <typeinfo>
55 namespace BPackageKit {
57 namespace BHPKG {
59 namespace BPrivate {
62 // #pragma mark - Attributes
65 struct PackageWriterImpl::Attribute
66 : public DoublyLinkedListLinkImpl<Attribute> {
67 BHPKGAttributeID id;
68 AttributeValue value;
69 DoublyLinkedList<Attribute> children;
71 Attribute(BHPKGAttributeID id_ = B_HPKG_ATTRIBUTE_ID_ENUM_COUNT)
73 id(id_)
77 ~Attribute()
79 DeleteChildren();
82 void AddChild(Attribute* child)
84 children.Add(child);
87 void RemoveChild(Attribute* child)
89 children.Remove(child);
92 void DeleteChildren()
94 while (Attribute* child = children.RemoveHead())
95 delete child;
98 Attribute* FindEntryChild(const char* fileName) const
100 for (DoublyLinkedList<Attribute>::ConstIterator it
101 = children.GetIterator(); Attribute* child = it.Next();) {
102 if (child->id != B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY)
103 continue;
104 if (child->value.type != B_HPKG_ATTRIBUTE_TYPE_STRING)
105 continue;
106 const char* childName = child->value.string->string;
107 if (strcmp(fileName, childName) == 0)
108 return child;
111 return NULL;
114 Attribute* FindEntryChild(const char* fileName, size_t nameLength) const
116 BString name(fileName, nameLength);
117 return (size_t)name.Length() == nameLength
118 ? FindEntryChild(name) : NULL;
121 Attribute* FindNodeAttributeChild(const char* attributeName) const
123 for (DoublyLinkedList<Attribute>::ConstIterator it
124 = children.GetIterator(); Attribute* child = it.Next();) {
125 if (child->id != B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE)
126 continue;
127 if (child->value.type != B_HPKG_ATTRIBUTE_TYPE_STRING)
128 continue;
129 const char* childName = child->value.string->string;
130 if (strcmp(attributeName, childName) == 0)
131 return child;
134 return NULL;
137 Attribute* ChildWithID(BHPKGAttributeID id) const
139 for (DoublyLinkedList<Attribute>::ConstIterator it
140 = children.GetIterator(); Attribute* child = it.Next();) {
141 if (child->id == id)
142 return child;
145 return NULL;
150 // #pragma mark - PackageContentHandler
153 struct PackageWriterImpl::PackageContentHandler
154 : BLowLevelPackageContentHandler {
155 PackageContentHandler(Attribute* rootAttribute, BErrorOutput* errorOutput,
156 StringCache& stringCache)
158 fErrorOutput(errorOutput),
159 fStringCache(stringCache),
160 fRootAttribute(rootAttribute),
161 fErrorOccurred(false)
165 virtual status_t HandleSectionStart(BHPKGPackageSectionID sectionID,
166 bool& _handleSection)
168 // we're only interested in the TOC
169 _handleSection = sectionID == B_HPKG_SECTION_PACKAGE_TOC;
170 return B_OK;
173 virtual status_t HandleSectionEnd(BHPKGPackageSectionID sectionID)
175 return B_OK;
178 virtual status_t HandleAttribute(BHPKGAttributeID attributeID,
179 const BPackageAttributeValue& value, void* parentToken, void*& _token)
181 if (fErrorOccurred)
182 return B_OK;
184 Attribute* parentAttribute = parentToken != NULL
185 ? (Attribute*)parentToken : fRootAttribute;
187 Attribute* attribute = new Attribute(attributeID);
188 parentAttribute->AddChild(attribute);
190 switch (value.type) {
191 case B_HPKG_ATTRIBUTE_TYPE_INT:
192 attribute->value.SetTo(value.signedInt);
193 break;
195 case B_HPKG_ATTRIBUTE_TYPE_UINT:
196 attribute->value.SetTo(value.unsignedInt);
197 break;
199 case B_HPKG_ATTRIBUTE_TYPE_STRING:
201 CachedString* string = fStringCache.Get(value.string);
202 if (string == NULL)
203 throw std::bad_alloc();
204 attribute->value.SetTo(string);
205 break;
208 case B_HPKG_ATTRIBUTE_TYPE_RAW:
209 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
210 attribute->value.SetToData(value.data.size,
211 value.data.offset);
212 } else if (value.encoding
213 == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
214 attribute->value.SetToData(value.data.size, value.data.raw);
215 } else {
216 fErrorOutput->PrintError("Invalid attribute value encoding "
217 "%d (attribute %d)\n", value.encoding, attributeID);
218 return B_BAD_DATA;
220 break;
222 case B_HPKG_ATTRIBUTE_TYPE_INVALID:
223 default:
224 fErrorOutput->PrintError("Invalid attribute value type %d "
225 "(attribute %d)\n", value.type, attributeID);
226 return B_BAD_DATA;
229 _token = attribute;
230 return B_OK;
233 virtual status_t HandleAttributeDone(BHPKGAttributeID attributeID,
234 const BPackageAttributeValue& value, void* parentToken, void* token)
236 return B_OK;
239 virtual void HandleErrorOccurred()
241 fErrorOccurred = true;
244 private:
245 BErrorOutput* fErrorOutput;
246 StringCache& fStringCache;
247 Attribute* fRootAttribute;
248 bool fErrorOccurred;
252 // #pragma mark - Entry
255 struct PackageWriterImpl::Entry : DoublyLinkedListLinkImpl<Entry> {
256 Entry(char* name, size_t nameLength, int fd, bool isImplicit)
258 fName(name),
259 fNameLength(nameLength),
260 fFD(fd),
261 fIsImplicit(isImplicit)
265 ~Entry()
267 DeleteChildren();
268 free(fName);
271 static Entry* Create(const char* name, size_t nameLength, int fd,
272 bool isImplicit)
274 char* clonedName = (char*)malloc(nameLength + 1);
275 if (clonedName == NULL)
276 throw std::bad_alloc();
277 memcpy(clonedName, name, nameLength);
278 clonedName[nameLength] = '\0';
280 Entry* entry = new(std::nothrow) Entry(clonedName, nameLength, fd,
281 isImplicit);
282 if (entry == NULL) {
283 free(clonedName);
284 throw std::bad_alloc();
287 return entry;
290 const char* Name() const
292 return fName;
295 int FD() const
297 return fFD;
300 void SetFD(int fd)
302 fFD = fd;
305 bool IsImplicit() const
307 return fIsImplicit;
310 void SetImplicit(bool isImplicit)
312 fIsImplicit = isImplicit;
315 bool HasName(const char* name, size_t nameLength)
317 return nameLength == fNameLength
318 && strncmp(name, fName, nameLength) == 0;
321 void AddChild(Entry* child)
323 fChildren.Add(child);
326 void DeleteChildren()
328 while (Entry* child = fChildren.RemoveHead())
329 delete child;
332 Entry* GetChild(const char* name, size_t nameLength) const
334 EntryList::ConstIterator it = fChildren.GetIterator();
335 while (Entry* child = it.Next()) {
336 if (child->HasName(name, nameLength))
337 return child;
340 return NULL;
343 EntryList::ConstIterator ChildIterator() const
345 return fChildren.GetIterator();
348 private:
349 char* fName;
350 size_t fNameLength;
351 int fFD;
352 bool fIsImplicit;
353 EntryList fChildren;
357 // #pragma mark - SubPathAdder
360 struct PackageWriterImpl::SubPathAdder {
361 SubPathAdder(BErrorOutput* errorOutput, char* pathBuffer,
362 const char* subPath)
364 fOriginalPathEnd(pathBuffer + strlen(pathBuffer))
366 if (fOriginalPathEnd != pathBuffer)
367 strlcat(pathBuffer, "/", B_PATH_NAME_LENGTH);
369 if (strlcat(pathBuffer, subPath, B_PATH_NAME_LENGTH)
370 >= B_PATH_NAME_LENGTH) {
371 *fOriginalPathEnd = '\0';
372 errorOutput->PrintError("Path too long: \"%s/%s\"\n", pathBuffer,
373 subPath);
374 throw status_t(B_BUFFER_OVERFLOW);
378 ~SubPathAdder()
380 *fOriginalPathEnd = '\0';
383 private:
384 char* fOriginalPathEnd;
388 // #pragma mark - HeapAttributeOffsetter
391 struct PackageWriterImpl::HeapAttributeOffsetter {
392 HeapAttributeOffsetter(const RangeArray<uint64>& ranges,
393 const Array<uint64>& deltas)
395 fRanges(ranges),
396 fDeltas(deltas)
400 void ProcessAttribute(Attribute* attribute)
402 // If the attribute refers to a heap value, adjust it
403 AttributeValue& value = attribute->value;
405 if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW
406 && value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
407 uint64 delta = fDeltas[fRanges.InsertionIndex(value.data.offset)];
408 value.data.offset -= delta;
411 // recurse
412 for (DoublyLinkedList<Attribute>::Iterator it
413 = attribute->children.GetIterator();
414 Attribute* child = it.Next();) {
415 ProcessAttribute(child);
419 private:
420 const RangeArray<uint64>& fRanges;
421 const Array<uint64>& fDeltas;
425 // #pragma mark - PackageWriterImpl (Inline Methods)
428 template<typename Type>
429 inline PackageWriterImpl::Attribute*
430 PackageWriterImpl::_AddAttribute(BHPKGAttributeID attributeID, Type value)
432 AttributeValue attributeValue;
433 attributeValue.SetTo(value);
434 return _AddAttribute(attributeID, attributeValue);
438 // #pragma mark - PackageWriterImpl
441 PackageWriterImpl::PackageWriterImpl(BPackageWriterListener* listener)
443 inherited("package", listener),
444 fListener(listener),
445 fHeapRangesToRemove(NULL),
446 fRootEntry(NULL),
447 fRootAttribute(NULL),
448 fTopAttribute(NULL),
449 fCheckLicenses(true)
454 PackageWriterImpl::~PackageWriterImpl()
456 delete fHeapRangesToRemove;
457 delete fRootAttribute;
458 delete fRootEntry;
462 status_t
463 PackageWriterImpl::Init(const char* fileName,
464 const BPackageWriterParameters& parameters)
466 try {
467 return _Init(NULL, false, fileName, parameters);
468 } catch (status_t error) {
469 return error;
470 } catch (std::bad_alloc) {
471 fListener->PrintError("Out of memory!\n");
472 return B_NO_MEMORY;
477 status_t
478 PackageWriterImpl::Init(BPositionIO* file, bool keepFile,
479 const BPackageWriterParameters& parameters)
481 try {
482 return _Init(file, keepFile, NULL, parameters);
483 } catch (status_t error) {
484 return error;
485 } catch (std::bad_alloc) {
486 fListener->PrintError("Out of memory!\n");
487 return B_NO_MEMORY;
492 status_t
493 PackageWriterImpl::SetInstallPath(const char* installPath)
495 fInstallPath = installPath;
496 return installPath == NULL
497 || (size_t)fInstallPath.Length() == strlen(installPath)
498 ? B_OK : B_NO_MEMORY;
502 void
503 PackageWriterImpl::SetCheckLicenses(bool checkLicenses)
505 fCheckLicenses = checkLicenses;
509 status_t
510 PackageWriterImpl::AddEntry(const char* fileName, int fd)
512 try {
513 // if it's ".PackageInfo", parse it
514 if (strcmp(fileName, B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
515 struct ErrorListener : public BPackageInfo::ParseErrorListener {
516 ErrorListener(BPackageWriterListener* _listener)
518 listener(_listener),
519 errorSeen(false)
523 virtual void OnError(const BString& msg, int line, int col) {
524 listener->PrintError("Parse error in %s(%d:%d) -> %s\n",
525 B_HPKG_PACKAGE_INFO_FILE_NAME, line, col, msg.String());
526 errorSeen = true;
529 BPackageWriterListener* listener;
530 bool errorSeen;
531 } errorListener(fListener);
533 if (fd >= 0) {
534 // a file descriptor is given -- read the config from there
535 // stat the file to get the file size
536 struct stat st;
537 if (fstat(fd, &st) != 0)
538 return errno;
540 BString packageInfoString;
541 char* buffer = packageInfoString.LockBuffer(st.st_size);
542 if (buffer == NULL)
543 return B_NO_MEMORY;
545 ssize_t result = read_pos(fd, 0, buffer, st.st_size);
546 if (result < 0) {
547 packageInfoString.UnlockBuffer(0);
548 return errno;
551 buffer[st.st_size] = '\0';
552 packageInfoString.UnlockBuffer(st.st_size);
554 result = fPackageInfo.ReadFromConfigString(packageInfoString,
555 &errorListener);
556 if (result != B_OK)
557 return result;
558 } else {
559 // use the file name
560 BEntry packageInfoEntry(fileName);
561 status_t result = fPackageInfo.ReadFromConfigFile(
562 packageInfoEntry, &errorListener);
563 if (result != B_OK
564 || (result = fPackageInfo.InitCheck()) != B_OK) {
565 if (!errorListener.errorSeen) {
566 fListener->PrintError("Failed to read %s: %s\n",
567 fileName, strerror(result));
569 return result;
574 return _RegisterEntry(fileName, fd);
575 } catch (status_t error) {
576 return error;
577 } catch (std::bad_alloc) {
578 fListener->PrintError("Out of memory!\n");
579 return B_NO_MEMORY;
584 status_t
585 PackageWriterImpl::Finish()
587 try {
588 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
589 _UpdateCheckEntryCollisions();
591 if (fPackageInfo.InitCheck() != B_OK)
592 _UpdateReadPackageInfo();
595 if (fPackageInfo.InitCheck() != B_OK) {
596 fListener->PrintError("No package-info file found (%s)!\n",
597 B_HPKG_PACKAGE_INFO_FILE_NAME);
598 return B_BAD_DATA;
601 fPackageInfo.SetInstallPath(fInstallPath);
603 RegisterPackageInfo(PackageAttributes(), fPackageInfo);
605 if (fCheckLicenses) {
606 status_t result = _CheckLicenses();
607 if (result != B_OK)
608 return result;
611 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0)
612 _CompactHeap();
614 return _Finish();
615 } catch (status_t error) {
616 return error;
617 } catch (std::bad_alloc) {
618 fListener->PrintError("Out of memory!\n");
619 return B_NO_MEMORY;
624 status_t
625 PackageWriterImpl::Recompress(BPositionIO* inputFile)
627 if (inputFile == NULL)
628 return B_BAD_VALUE;
630 try {
631 return _Recompress(inputFile);
632 } catch (status_t error) {
633 return error;
634 } catch (std::bad_alloc) {
635 fListener->PrintError("Out of memory!\n");
636 return B_NO_MEMORY;
641 status_t
642 PackageWriterImpl::_Init(BPositionIO* file, bool keepFile, const char* fileName,
643 const BPackageWriterParameters& parameters)
645 status_t result = inherited::Init(file, keepFile, fileName, parameters);
646 if (result != B_OK)
647 return result;
649 if (fStringCache.Init() != B_OK)
650 throw std::bad_alloc();
652 // create entry list
653 fRootEntry = new Entry(NULL, 0, -1, true);
655 fRootAttribute = new Attribute();
657 fHeapOffset = fHeaderSize = sizeof(hpkg_header);
658 fTopAttribute = fRootAttribute;
660 fHeapRangesToRemove = new RangeArray<uint64>;
662 // in update mode, parse the TOC
663 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
664 PackageReaderImpl packageReader(fListener);
665 hpkg_header header;
666 result = packageReader.Init(File(), false, 0, &header);
667 if (result != B_OK)
668 return result;
670 fHeapOffset = packageReader.HeapOffset();
672 PackageContentHandler handler(fRootAttribute, fListener, fStringCache);
674 result = packageReader.ParseContent(&handler);
675 if (result != B_OK)
676 return result;
678 // While the compression level can change, we have to reuse the
679 // compression algorithm at least.
680 SetCompression(B_BENDIAN_TO_HOST_INT16(header.heap_compression));
682 result = InitHeapReader(fHeapOffset);
683 if (result != B_OK)
684 return result;
686 fHeapWriter->Reinit(packageReader.RawHeapReader());
688 // Remove the old packages attributes and TOC section from the heap.
689 // We'll write new ones later.
690 const PackageFileSection& attributesSection
691 = packageReader.PackageAttributesSection();
692 const PackageFileSection& tocSection = packageReader.TOCSection();
693 if (!fHeapRangesToRemove->AddRange(attributesSection.offset,
694 attributesSection.uncompressedLength)
695 || !fHeapRangesToRemove->AddRange(tocSection.offset,
696 tocSection.uncompressedLength)) {
697 throw std::bad_alloc();
699 } else {
700 result = InitHeapReader(fHeapOffset);
701 if (result != B_OK)
702 return result;
705 return B_OK;
709 status_t
710 PackageWriterImpl::_Finish()
712 // write entries
713 for (EntryList::ConstIterator it = fRootEntry->ChildIterator();
714 Entry* entry = it.Next();) {
715 char pathBuffer[B_PATH_NAME_LENGTH];
716 pathBuffer[0] = '\0';
717 _AddEntry(AT_FDCWD, entry, entry->Name(), pathBuffer);
720 hpkg_header header;
722 // write the TOC and package attributes
723 uint64 tocLength;
724 _WriteTOC(header, tocLength);
726 uint64 attributesLength;
727 _WritePackageAttributes(header, attributesLength);
729 // flush the heap
730 status_t error = fHeapWriter->Finish();
731 if (error != B_OK)
732 return error;
734 uint64 compressedHeapSize = fHeapWriter->CompressedHeapSize();
736 header.heap_compression = B_HOST_TO_BENDIAN_INT16(
737 Parameters().Compression());
738 header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize());
739 header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64(compressedHeapSize);
740 header.heap_size_uncompressed = B_HOST_TO_BENDIAN_INT64(
741 fHeapWriter->UncompressedHeapSize());
743 // Truncate the file to the size it is supposed to have. In update mode, it
744 // can be greater when one or more files are shrunk. In creation mode it
745 // should already have the correct size.
746 off_t totalSize = fHeapWriter->HeapOffset() + (off_t)compressedHeapSize;
747 error = File()->SetSize(totalSize);
748 if (error != B_OK) {
749 fListener->PrintError("Failed to truncate package file to new "
750 "size: %s\n", strerror(errno));
751 return errno;
754 fListener->OnPackageSizeInfo(fHeaderSize, compressedHeapSize, tocLength,
755 attributesLength, totalSize);
757 // prepare the header
759 // general
760 header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_MAGIC);
761 header.header_size = B_HOST_TO_BENDIAN_INT16(fHeaderSize);
762 header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_VERSION);
763 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
764 header.minor_version = B_HOST_TO_BENDIAN_INT16(B_HPKG_MINOR_VERSION);
766 // write the header
767 RawWriteBuffer(&header, sizeof(hpkg_header), 0);
769 SetFinished(true);
770 return B_OK;
774 status_t
775 PackageWriterImpl::_Recompress(BPositionIO* inputFile)
777 if (inputFile == NULL)
778 return B_BAD_VALUE;
780 // create a package reader for the input file
781 PackageReaderImpl reader(fListener);
782 hpkg_header header;
783 status_t error = reader.Init(inputFile, false, 0, &header);
784 if (error != B_OK) {
785 fListener->PrintError("Failed to open hpkg file: %s\n",
786 strerror(error));
787 return error;
790 // Update some header fields, assuming no compression. We'll rewrite the
791 // header later, should compression have been used. Doing it this way allows
792 // for streaming an uncompressed package.
793 uint64 uncompressedHeapSize
794 = reader.RawHeapReader()->UncompressedHeapSize();
795 uint64 compressedHeapSize = uncompressedHeapSize;
797 off_t totalSize = fHeapWriter->HeapOffset() + (off_t)compressedHeapSize;
799 header.heap_compression = B_HOST_TO_BENDIAN_INT16(
800 Parameters().Compression());
801 header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize());
802 header.heap_size_uncompressed
803 = B_HOST_TO_BENDIAN_INT64(uncompressedHeapSize);
805 if (Parameters().Compression() == B_HPKG_COMPRESSION_NONE) {
806 header.heap_size_compressed
807 = B_HOST_TO_BENDIAN_INT64(compressedHeapSize);
808 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
810 // write the header
811 RawWriteBuffer(&header, sizeof(hpkg_header), 0);
814 // copy the heap data
815 uint64 bytesCompressed;
816 error = fHeapWriter->AddData(*reader.RawHeapReader(), uncompressedHeapSize,
817 bytesCompressed);
818 if (error != B_OK)
819 return error;
821 // flush the heap
822 error = fHeapWriter->Finish();
823 if (error != B_OK)
824 return error;
826 // If compression is enabled, update and write the header.
827 if (Parameters().Compression() != B_HPKG_COMPRESSION_NONE) {
828 compressedHeapSize = fHeapWriter->CompressedHeapSize();
829 totalSize = fHeapWriter->HeapOffset() + (off_t)compressedHeapSize;
830 header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64(compressedHeapSize);
831 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize);
833 // write the header
834 RawWriteBuffer(&header, sizeof(hpkg_header), 0);
837 SetFinished(true);
838 return B_OK;
842 status_t
843 PackageWriterImpl::_CheckLicenses()
845 BPath systemLicensePath;
846 status_t result
847 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
848 = find_directory(B_SYSTEM_DATA_DIRECTORY, &systemLicensePath);
849 #else
850 = systemLicensePath.SetTo(HAIKU_BUILD_SYSTEM_DATA_DIRECTORY);
851 #endif
852 if (result != B_OK) {
853 fListener->PrintError("unable to find system data path: %s!\n",
854 strerror(result));
855 return result;
857 if ((result = systemLicensePath.Append("licenses")) != B_OK) {
858 fListener->PrintError("unable to append to system data path!\n");
859 return result;
862 BDirectory systemLicenseDir(systemLicensePath.Path());
864 const BStringList& licenseList = fPackageInfo.LicenseList();
865 for (int i = 0; i < licenseList.CountStrings(); ++i) {
866 const BString& licenseName = licenseList.StringAt(i);
867 if (licenseName == kPublicDomainLicenseName)
868 continue;
870 BEntry license;
871 if (systemLicenseDir.FindEntry(licenseName.String(), &license) == B_OK)
872 continue;
874 // license is not a system license, so it must be contained in package
875 BString licensePath("data/licenses/");
876 licensePath << licenseName;
878 if (!_IsEntryInPackage(licensePath)) {
879 fListener->PrintError("License '%s' isn't contained in package!\n",
880 licenseName.String());
881 return B_BAD_DATA;
885 return B_OK;
889 bool
890 PackageWriterImpl::_IsEntryInPackage(const char* fileName)
892 const char* originalFileName = fileName;
894 // find the closest ancestor of the entry that is in the added entries
895 bool added = false;
896 Entry* entry = fRootEntry;
897 while (entry != NULL) {
898 if (!entry->IsImplicit()) {
899 added = true;
900 break;
903 if (*fileName == '\0')
904 break;
906 const char* nextSlash = strchr(fileName, '/');
908 if (nextSlash == NULL) {
909 // no slash, just the file name
910 size_t length = strlen(fileName);
911 entry = entry->GetChild(fileName, length);
912 fileName += length;
913 continue;
916 // find the start of the next component, skipping slashes
917 const char* nextComponent = nextSlash + 1;
918 while (*nextComponent == '/')
919 nextComponent++;
921 entry = entry->GetChild(fileName, nextSlash - fileName);
923 fileName = nextComponent;
926 if (added) {
927 // the entry itself or one of its ancestors has been added to the
928 // package explicitly -- stat it, to see, if it exists
929 struct stat st;
930 if (entry->FD() >= 0) {
931 if (fstatat(entry->FD(), *fileName != '\0' ? fileName : NULL, &st,
932 AT_SYMLINK_NOFOLLOW) == 0) {
933 return true;
935 } else {
936 if (lstat(originalFileName, &st) == 0)
937 return true;
941 // In update mode the entry might already be in the package.
942 Attribute* attribute = fRootAttribute;
943 fileName = originalFileName;
945 while (attribute != NULL) {
946 if (*fileName == '\0')
947 return true;
949 const char* nextSlash = strchr(fileName, '/');
951 if (nextSlash == NULL) {
952 // no slash, just the file name
953 return attribute->FindEntryChild(fileName) != NULL;
956 // find the start of the next component, skipping slashes
957 const char* nextComponent = nextSlash + 1;
958 while (*nextComponent == '/')
959 nextComponent++;
961 attribute = attribute->FindEntryChild(fileName, nextSlash - fileName);
963 fileName = nextComponent;
966 return false;
970 void
971 PackageWriterImpl::_UpdateReadPackageInfo()
973 // get the .PackageInfo entry attribute
974 Attribute* attribute = fRootAttribute->FindEntryChild(
975 B_HPKG_PACKAGE_INFO_FILE_NAME);
976 if (attribute == NULL) {
977 fListener->PrintError("No %s in package file.\n",
978 B_HPKG_PACKAGE_INFO_FILE_NAME);
979 throw status_t(B_BAD_DATA);
982 // get the data attribute
983 Attribute* dataAttribute = attribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_DATA);
984 if (dataAttribute == NULL) {
985 fListener->PrintError("%s entry in package file doesn't have data.\n",
986 B_HPKG_PACKAGE_INFO_FILE_NAME);
987 throw status_t(B_BAD_DATA);
990 AttributeValue& value = dataAttribute->value;
991 if (value.type != B_HPKG_ATTRIBUTE_TYPE_RAW) {
992 fListener->PrintError("%s entry in package file has an invalid data "
993 "attribute (not of type raw).\n", B_HPKG_PACKAGE_INFO_FILE_NAME);
994 throw status_t(B_BAD_DATA);
997 BPackageData data;
998 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE)
999 data.SetData(value.data.size, value.data.raw);
1000 else
1001 data.SetData(value.data.size, value.data.offset);
1003 // read the value into a string
1004 BString valueString;
1005 char* valueBuffer = valueString.LockBuffer(value.data.size);
1006 if (valueBuffer == NULL)
1007 throw std::bad_alloc();
1009 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) {
1010 // data encoded inline -- just copy to buffer
1011 memcpy(valueBuffer, value.data.raw, value.data.size);
1012 } else {
1013 // data on heap -- read from there
1014 status_t error = fHeapWriter->ReadData(data.Offset(), valueBuffer,
1015 data.Size());
1016 if (error != B_OK)
1017 throw error;
1020 valueString.UnlockBuffer();
1022 // parse the package info
1023 status_t error = fPackageInfo.ReadFromConfigString(valueString);
1024 if (error != B_OK) {
1025 fListener->PrintError("Failed to parse package info data from package "
1026 "file: %s\n", strerror(error));
1027 throw status_t(error);
1032 void
1033 PackageWriterImpl::_UpdateCheckEntryCollisions()
1035 for (EntryList::ConstIterator it = fRootEntry->ChildIterator();
1036 Entry* entry = it.Next();) {
1037 char pathBuffer[B_PATH_NAME_LENGTH];
1038 pathBuffer[0] = '\0';
1039 _UpdateCheckEntryCollisions(fRootAttribute, AT_FDCWD, entry,
1040 entry->Name(), pathBuffer);
1045 void
1046 PackageWriterImpl::_UpdateCheckEntryCollisions(Attribute* parentAttribute,
1047 int dirFD, Entry* entry, const char* fileName, char* pathBuffer)
1049 bool isImplicitEntry = entry != NULL && entry->IsImplicit();
1051 SubPathAdder pathAdder(fListener, pathBuffer, fileName);
1053 // Check whether there's an entry attribute for this entry. If not, we can
1054 // ignore this entry.
1055 Attribute* entryAttribute = parentAttribute->FindEntryChild(fileName);
1056 if (entryAttribute == NULL)
1057 return;
1059 // open the node
1060 int fd;
1061 FileDescriptorCloser fdCloser;
1063 if (entry != NULL && entry->FD() >= 0) {
1064 // a file descriptor is already given -- use that
1065 fd = entry->FD();
1066 } else {
1067 fd = openat(dirFD, fileName,
1068 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE));
1069 if (fd < 0) {
1070 fListener->PrintError("Failed to open entry \"%s\": %s\n",
1071 pathBuffer, strerror(errno));
1072 throw status_t(errno);
1074 fdCloser.SetTo(fd);
1077 // stat the node
1078 struct stat st;
1079 if (fstat(fd, &st) < 0) {
1080 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer,
1081 strerror(errno));
1082 throw status_t(errno);
1085 // implicit entries must be directories
1086 if (isImplicitEntry && !S_ISDIR(st.st_mode)) {
1087 fListener->PrintError("Non-leaf path component \"%s\" is not a "
1088 "directory.\n", pathBuffer);
1089 throw status_t(B_BAD_VALUE);
1092 // get the pre-existing node's file type
1093 uint32 preExistingFileType = B_HPKG_DEFAULT_FILE_TYPE;
1094 if (Attribute* fileTypeAttribute
1095 = entryAttribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_FILE_TYPE)) {
1096 if (fileTypeAttribute->value.type == B_HPKG_ATTRIBUTE_TYPE_UINT)
1097 preExistingFileType = fileTypeAttribute->value.unsignedInt;
1100 // Compare the node type with that of the pre-existing one.
1101 if (!S_ISDIR(st.st_mode)) {
1102 // the pre-existing must not a directory either -- we'll remove it
1103 if (preExistingFileType == B_HPKG_FILE_TYPE_DIRECTORY) {
1104 fListener->PrintError("Specified file \"%s\" clashes with an "
1105 "archived directory.\n", pathBuffer);
1106 throw status_t(B_BAD_VALUE);
1109 if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) {
1110 fListener->PrintError("Specified file \"%s\" clashes with an "
1111 "archived file.\n", pathBuffer);
1112 throw status_t(B_FILE_EXISTS);
1115 parentAttribute->RemoveChild(entryAttribute);
1116 _AttributeRemoved(entryAttribute);
1118 return;
1121 // the pre-existing entry needs to be a directory too -- we will merge
1122 if (preExistingFileType != B_HPKG_FILE_TYPE_DIRECTORY) {
1123 fListener->PrintError("Specified directory \"%s\" clashes with an "
1124 "archived non-directory.\n", pathBuffer);
1125 throw status_t(B_BAD_VALUE);
1128 // directory -- recursively add children
1129 if (isImplicitEntry) {
1130 // this is an implicit entry -- just check the child entries
1131 for (EntryList::ConstIterator it = entry->ChildIterator();
1132 Entry* child = it.Next();) {
1133 _UpdateCheckEntryCollisions(entryAttribute, fd, child,
1134 child->Name(), pathBuffer);
1136 } else {
1137 // explicitly specified directory -- we need to read the directory
1139 // first we check for colliding node attributes, though
1140 if (DIR* attrDir = fs_fopen_attr_dir(fd)) {
1141 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir);
1143 while (dirent* entry = fs_read_attr_dir(attrDir)) {
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);
1152 // check whether the attribute exists
1153 Attribute* attributeAttribute
1154 = entryAttribute->FindNodeAttributeChild(entry->d_name);
1155 if (attributeAttribute == NULL)
1156 continue;
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);
1165 // remove it
1166 entryAttribute->RemoveChild(attributeAttribute);
1167 _AttributeRemoved(attributeAttribute);
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);
1179 DIR* dir = fdopendir(clonedFD);
1180 if (dir == NULL) {
1181 fListener->PrintError(
1182 "Failed to open directory \"%s\": %s\n", pathBuffer,
1183 strerror(errno));
1184 close(clonedFD);
1185 throw status_t(errno);
1187 CObjectDeleter<DIR, int> dirCloser(dir, closedir);
1189 while (dirent* entry = readdir(dir)) {
1190 // skip "." and ".."
1191 if (strcmp(entry->d_name, ".") == 0
1192 || strcmp(entry->d_name, "..") == 0) {
1193 continue;
1196 _UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name,
1197 pathBuffer);
1203 void
1204 PackageWriterImpl::_CompactHeap()
1206 int32 count = fHeapRangesToRemove->CountRanges();
1207 if (count == 0)
1208 return;
1210 // compute the move deltas for the ranges
1211 Array<uint64> deltas;
1212 uint64 delta = 0;
1213 for (int32 i = 0; i < count; i++) {
1214 if (!deltas.Add(delta))
1215 throw std::bad_alloc();
1217 delta += fHeapRangesToRemove->RangeAt(i).size;
1220 if (!deltas.Add(delta))
1221 throw std::bad_alloc();
1223 // offset the attributes
1224 HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute(
1225 fRootAttribute);
1227 // remove the ranges from the heap
1228 fHeapWriter->RemoveDataRanges(*fHeapRangesToRemove);
1232 void
1233 PackageWriterImpl::_AttributeRemoved(Attribute* attribute)
1235 AttributeValue& value = attribute->value;
1236 if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW
1237 && value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) {
1238 if (!fHeapRangesToRemove->AddRange(value.data.offset, value.data.size))
1239 throw std::bad_alloc();
1240 } else if (value.type == B_HPKG_ATTRIBUTE_TYPE_STRING)
1241 fStringCache.Put(value.string);
1243 for (DoublyLinkedList<Attribute>::Iterator it
1244 = attribute->children.GetIterator();
1245 Attribute* child = it.Next();) {
1246 _AttributeRemoved(child);
1251 status_t
1252 PackageWriterImpl::_RegisterEntry(const char* fileName, int fd)
1254 if (*fileName == '\0') {
1255 fListener->PrintError("Invalid empty file name\n");
1256 return B_BAD_VALUE;
1259 // add all components of the path
1260 Entry* entry = fRootEntry;
1261 while (*fileName != 0) {
1262 const char* nextSlash = strchr(fileName, '/');
1263 // no slash, just add the file name
1264 if (nextSlash == NULL) {
1265 entry = _RegisterEntry(entry, fileName, strlen(fileName), fd,
1266 false);
1267 break;
1270 // find the start of the next component, skipping slashes
1271 const char* nextComponent = nextSlash + 1;
1272 while (*nextComponent == '/')
1273 nextComponent++;
1275 bool lastComponent = *nextComponent != '\0';
1277 if (nextSlash == fileName) {
1278 // the FS root
1279 entry = _RegisterEntry(entry, fileName, 1, lastComponent ? fd : -1,
1280 lastComponent);
1281 } else {
1282 entry = _RegisterEntry(entry, fileName, nextSlash - fileName,
1283 lastComponent ? fd : -1, lastComponent);
1286 fileName = nextComponent;
1289 return B_OK;
1293 PackageWriterImpl::Entry*
1294 PackageWriterImpl::_RegisterEntry(Entry* parent, const char* name,
1295 size_t nameLength, int fd, bool isImplicit)
1297 // check the component name -- don't allow "." or ".."
1298 if (name[0] == '.'
1299 && (nameLength == 1 || (nameLength == 2 && name[1] == '.'))) {
1300 fListener->PrintError("Invalid file name: \".\" and \"..\" "
1301 "are not allowed as path components\n");
1302 throw status_t(B_BAD_VALUE);
1305 // the entry might already exist
1306 Entry* entry = parent->GetChild(name, nameLength);
1307 if (entry != NULL) {
1308 // If the entry was implicit and is no longer, we mark it non-implicit
1309 // and delete all of it's children.
1310 if (entry->IsImplicit() && !isImplicit) {
1311 entry->DeleteChildren();
1312 entry->SetImplicit(false);
1313 entry->SetFD(fd);
1315 } else {
1316 // nope -- create it
1317 entry = Entry::Create(name, nameLength, fd, isImplicit);
1318 parent->AddChild(entry);
1321 return entry;
1325 void
1326 PackageWriterImpl::_WriteTOC(hpkg_header& header, uint64& _length)
1328 // write the subsections
1329 uint64 startOffset = fHeapWriter->UncompressedHeapSize();
1331 // cached strings
1332 uint64 cachedStringsOffset = fHeapWriter->UncompressedHeapSize();
1333 int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2);
1335 // main TOC section
1336 uint64 mainOffset = fHeapWriter->UncompressedHeapSize();
1337 _WriteAttributeChildren(fRootAttribute);
1339 // notify the listener
1340 uint64 endOffset = fHeapWriter->UncompressedHeapSize();
1341 uint64 stringsSize = mainOffset - cachedStringsOffset;
1342 uint64 mainSize = endOffset - mainOffset;
1343 uint64 tocSize = endOffset - startOffset;
1344 fListener->OnTOCSizeInfo(stringsSize, mainSize, tocSize);
1346 // update the header
1347 header.toc_length = B_HOST_TO_BENDIAN_INT64(tocSize);
1348 header.toc_strings_length = B_HOST_TO_BENDIAN_INT64(stringsSize);
1349 header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten);
1351 _length = tocSize;
1355 void
1356 PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute)
1358 DoublyLinkedList<Attribute>::Iterator it
1359 = attribute->children.GetIterator();
1360 while (Attribute* child = it.Next()) {
1361 // write tag
1362 uint8 encoding = child->value.ApplicableEncoding();
1363 WriteUnsignedLEB128(compose_attribute_tag(child->id,
1364 child->value.type, encoding, !child->children.IsEmpty()));
1366 // write value
1367 WriteAttributeValue(child->value, encoding);
1369 if (!child->children.IsEmpty())
1370 _WriteAttributeChildren(child);
1373 WriteUnsignedLEB128(0);
1377 void
1378 PackageWriterImpl::_WritePackageAttributes(hpkg_header& header, uint64& _length)
1380 // write cached strings and package attributes tree
1381 off_t startOffset = fHeapWriter->UncompressedHeapSize();
1383 uint32 stringsLength;
1384 uint32 stringsCount = WritePackageAttributes(PackageAttributes(),
1385 stringsLength);
1387 // notify listener
1388 uint32 attributesLength = fHeapWriter->UncompressedHeapSize() - startOffset;
1389 fListener->OnPackageAttributesSizeInfo(stringsCount, attributesLength);
1391 // update the header
1392 header.attributes_length = B_HOST_TO_BENDIAN_INT32(attributesLength);
1393 header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount);
1394 header.attributes_strings_length = B_HOST_TO_BENDIAN_INT32(stringsLength);
1396 _length = attributesLength;
1400 void
1401 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName,
1402 char* pathBuffer)
1404 bool isImplicitEntry = entry != NULL && entry->IsImplicit();
1406 SubPathAdder pathAdder(fListener, pathBuffer, fileName);
1407 if (!isImplicitEntry)
1408 fListener->OnEntryAdded(pathBuffer);
1410 // open the node
1411 int fd;
1412 FileDescriptorCloser fdCloser;
1414 if (entry != NULL && entry->FD() >= 0) {
1415 // a file descriptor is already given -- use that
1416 fd = entry->FD();
1417 } else {
1418 fd = openat(dirFD, fileName,
1419 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE));
1420 if (fd < 0) {
1421 fListener->PrintError("Failed to open entry \"%s\": %s\n",
1422 pathBuffer, strerror(errno));
1423 throw status_t(errno);
1425 fdCloser.SetTo(fd);
1428 // stat the node
1429 struct stat st;
1430 if (fstat(fd, &st) < 0) {
1431 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer,
1432 strerror(errno));
1433 throw status_t(errno);
1436 // implicit entries must be directories
1437 if (isImplicitEntry && !S_ISDIR(st.st_mode)) {
1438 fListener->PrintError("Non-leaf path component \"%s\" is not a "
1439 "directory\n", pathBuffer);
1440 throw status_t(B_BAD_VALUE);
1443 // In update mode we don't need to add an entry attribute for an implicit
1444 // directory, if there already is one.
1445 Attribute* entryAttribute = NULL;
1446 if (S_ISDIR(st.st_mode) && (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) {
1447 entryAttribute = fTopAttribute->FindEntryChild(fileName);
1448 if (entryAttribute != NULL && isImplicitEntry) {
1449 Stacker<Attribute> entryAttributeStacker(fTopAttribute,
1450 entryAttribute);
1451 _AddDirectoryChildren(entry, fd, pathBuffer);
1452 return;
1456 // check/translate the node type
1457 uint8 fileType;
1458 uint32 defaultPermissions;
1459 if (S_ISREG(st.st_mode)) {
1460 fileType = B_HPKG_FILE_TYPE_FILE;
1461 defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS;
1462 } else if (S_ISLNK(st.st_mode)) {
1463 fileType = B_HPKG_FILE_TYPE_SYMLINK;
1464 defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS;
1465 } else if (S_ISDIR(st.st_mode)) {
1466 fileType = B_HPKG_FILE_TYPE_DIRECTORY;
1467 defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS;
1468 } else {
1469 // unsupported node type
1470 fListener->PrintError("Unsupported node type, entry: \"%s\"\n",
1471 pathBuffer);
1472 throw status_t(B_UNSUPPORTED);
1475 // add attribute entry, if it doesn't already exist (update mode, directory)
1476 bool isNewEntry = entryAttribute == NULL;
1477 if (entryAttribute == NULL) {
1478 entryAttribute = _AddStringAttribute(
1479 B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName);
1482 Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute);
1484 if (isNewEntry) {
1485 // add stat data
1486 if (fileType != B_HPKG_DEFAULT_FILE_TYPE)
1487 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType);
1488 if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) {
1489 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS,
1490 uint32(st.st_mode & ALLPERMS));
1492 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime));
1493 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime));
1494 #ifdef __HAIKU__
1495 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime));
1496 #else
1497 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime));
1498 #endif
1499 // TODO: File user/group!
1501 // add file data/symlink path
1502 if (S_ISREG(st.st_mode)) {
1503 // regular file -- add data
1504 if (st.st_size > 0) {
1505 BFDDataReader dataReader(fd);
1506 status_t error = _AddData(dataReader, st.st_size);
1507 if (error != B_OK)
1508 throw status_t(error);
1510 } else if (S_ISLNK(st.st_mode)) {
1511 // symlink -- add link address
1512 char path[B_PATH_NAME_LENGTH + 1];
1513 ssize_t bytesRead = readlinkat(dirFD, fileName, path,
1514 B_PATH_NAME_LENGTH);
1515 if (bytesRead < 0) {
1516 fListener->PrintError("Failed to read symlink \"%s\": %s\n",
1517 pathBuffer, strerror(errno));
1518 throw status_t(errno);
1521 path[bytesRead] = '\0';
1522 _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path);
1526 // add attributes
1527 if (DIR* attrDir = fs_fopen_attr_dir(fd)) {
1528 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir);
1530 while (dirent* entry = fs_read_attr_dir(attrDir)) {
1531 attr_info attrInfo;
1532 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) {
1533 fListener->PrintError(
1534 "Failed to stat attribute \"%s\" of file \"%s\": %s\n",
1535 entry->d_name, pathBuffer, strerror(errno));
1536 throw status_t(errno);
1539 // create attribute entry
1540 Attribute* attributeAttribute = _AddStringAttribute(
1541 B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name);
1542 Stacker<Attribute> attributeAttributeStacker(fTopAttribute,
1543 attributeAttribute);
1545 // add type
1546 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE,
1547 (uint32)attrInfo.type);
1549 // add data
1550 BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type);
1551 status_t error = _AddData(dataReader, attrInfo.size);
1552 if (error != B_OK)
1553 throw status_t(error);
1557 if (S_ISDIR(st.st_mode))
1558 _AddDirectoryChildren(entry, fd, pathBuffer);
1562 void
1563 PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer)
1565 // directory -- recursively add children
1566 if (entry != NULL && entry->IsImplicit()) {
1567 // this is an implicit entry -- just add it's children
1568 for (EntryList::ConstIterator it = entry->ChildIterator();
1569 Entry* child = it.Next();) {
1570 _AddEntry(fd, child, child->Name(), pathBuffer);
1572 } else {
1573 // we need to clone the directory FD for fdopendir()
1574 int clonedFD = dup(fd);
1575 if (clonedFD < 0) {
1576 fListener->PrintError(
1577 "Failed to dup() directory FD: %s\n", strerror(errno));
1578 throw status_t(errno);
1581 DIR* dir = fdopendir(clonedFD);
1582 if (dir == NULL) {
1583 fListener->PrintError(
1584 "Failed to open directory \"%s\": %s\n", pathBuffer,
1585 strerror(errno));
1586 close(clonedFD);
1587 throw status_t(errno);
1589 CObjectDeleter<DIR, int> dirCloser(dir, closedir);
1591 while (dirent* entry = readdir(dir)) {
1592 // skip "." and ".."
1593 if (strcmp(entry->d_name, ".") == 0
1594 || strcmp(entry->d_name, "..") == 0) {
1595 continue;
1598 _AddEntry(fd, NULL, entry->d_name, pathBuffer);
1604 PackageWriterImpl::Attribute*
1605 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id,
1606 const AttributeValue& value)
1608 Attribute* attribute = new Attribute(id);
1610 attribute->value = value;
1611 fTopAttribute->AddChild(attribute);
1613 return attribute;
1617 PackageWriterImpl::Attribute*
1618 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID,
1619 const char* value)
1621 AttributeValue attributeValue;
1622 attributeValue.SetTo(fStringCache.Get(value));
1623 return _AddAttribute(attributeID, attributeValue);
1627 PackageWriterImpl::Attribute*
1628 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID,
1629 uint64 dataSize, uint64 dataOffset)
1631 AttributeValue attributeValue;
1632 attributeValue.SetToData(dataSize, dataOffset);
1633 return _AddAttribute(attributeID, attributeValue);
1637 PackageWriterImpl::Attribute*
1638 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID,
1639 uint64 dataSize, const uint8* data)
1641 AttributeValue attributeValue;
1642 attributeValue.SetToData(dataSize, data);
1643 return _AddAttribute(attributeID, attributeValue);
1647 status_t
1648 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size)
1650 // add short data inline
1651 if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) {
1652 uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE];
1653 status_t error = dataReader.ReadData(0, buffer, size);
1654 if (error != B_OK) {
1655 fListener->PrintError("Failed to read data: %s\n", strerror(error));
1656 return error;
1659 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer);
1660 return B_OK;
1663 // add data to heap
1664 uint64 dataOffset;
1665 status_t error = fHeapWriter->AddData(dataReader, size, dataOffset);
1666 if (error != B_OK)
1667 return error;
1669 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, dataOffset);
1670 return B_OK;
1674 } // namespace BPrivate
1676 } // namespace BHPKG
1678 } // namespace BPackageKit