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.
8 #include <package/hpkg/PackageWriterImpl.h>
22 #include <ByteOrder.h>
23 #include <Directory.h>
25 #include <FindDirectory.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";
55 namespace BPackageKit
{
62 // #pragma mark - Attributes
65 struct PackageWriterImpl::Attribute
66 : public DoublyLinkedListLinkImpl
<Attribute
> {
69 DoublyLinkedList
<Attribute
> children
;
71 Attribute(BHPKGAttributeID id_
= B_HPKG_ATTRIBUTE_ID_ENUM_COUNT
)
82 void AddChild(Attribute
* child
)
87 void RemoveChild(Attribute
* child
)
89 children
.Remove(child
);
94 while (Attribute
* child
= children
.RemoveHead())
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
)
104 if (child
->value
.type
!= B_HPKG_ATTRIBUTE_TYPE_STRING
)
106 const char* childName
= child
->value
.string
->string
;
107 if (strcmp(fileName
, childName
) == 0)
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
)
127 if (child
->value
.type
!= B_HPKG_ATTRIBUTE_TYPE_STRING
)
129 const char* childName
= child
->value
.string
->string
;
130 if (strcmp(attributeName
, childName
) == 0)
137 Attribute
* ChildWithID(BHPKGAttributeID id
) const
139 for (DoublyLinkedList
<Attribute
>::ConstIterator it
140 = children
.GetIterator(); Attribute
* child
= it
.Next();) {
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
;
173 virtual status_t
HandleSectionEnd(BHPKGPackageSectionID sectionID
)
178 virtual status_t
HandleAttribute(BHPKGAttributeID attributeID
,
179 const BPackageAttributeValue
& value
, void* parentToken
, void*& _token
)
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
);
195 case B_HPKG_ATTRIBUTE_TYPE_UINT
:
196 attribute
->value
.SetTo(value
.unsignedInt
);
199 case B_HPKG_ATTRIBUTE_TYPE_STRING
:
201 CachedString
* string
= fStringCache
.Get(value
.string
);
203 throw std::bad_alloc();
204 attribute
->value
.SetTo(string
);
208 case B_HPKG_ATTRIBUTE_TYPE_RAW
:
209 if (value
.encoding
== B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP
) {
210 attribute
->value
.SetToData(value
.data
.size
,
212 } else if (value
.encoding
213 == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE
) {
214 attribute
->value
.SetToData(value
.data
.size
, value
.data
.raw
);
216 fErrorOutput
->PrintError("Invalid attribute value encoding "
217 "%d (attribute %d)\n", value
.encoding
, attributeID
);
222 case B_HPKG_ATTRIBUTE_TYPE_INVALID
:
224 fErrorOutput
->PrintError("Invalid attribute value type %d "
225 "(attribute %d)\n", value
.type
, attributeID
);
233 virtual status_t
HandleAttributeDone(BHPKGAttributeID attributeID
,
234 const BPackageAttributeValue
& value
, void* parentToken
, void* token
)
239 virtual void HandleErrorOccurred()
241 fErrorOccurred
= true;
245 BErrorOutput
* fErrorOutput
;
246 StringCache
& fStringCache
;
247 Attribute
* fRootAttribute
;
252 // #pragma mark - Entry
255 struct PackageWriterImpl::Entry
: DoublyLinkedListLinkImpl
<Entry
> {
256 Entry(char* name
, size_t nameLength
, int fd
, bool isImplicit
)
259 fNameLength(nameLength
),
261 fIsImplicit(isImplicit
)
271 static Entry
* Create(const char* name
, size_t nameLength
, int fd
,
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
,
284 throw std::bad_alloc();
290 const char* Name() const
305 bool IsImplicit() const
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())
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
))
343 EntryList::ConstIterator
ChildIterator() const
345 return fChildren
.GetIterator();
357 // #pragma mark - SubPathAdder
360 struct PackageWriterImpl::SubPathAdder
{
361 SubPathAdder(BErrorOutput
* errorOutput
, char* pathBuffer
,
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
,
374 throw status_t(B_BUFFER_OVERFLOW
);
380 *fOriginalPathEnd
= '\0';
384 char* fOriginalPathEnd
;
388 // #pragma mark - HeapAttributeOffsetter
391 struct PackageWriterImpl::HeapAttributeOffsetter
{
392 HeapAttributeOffsetter(const RangeArray
<uint64
>& ranges
,
393 const Array
<uint64
>& 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
;
412 for (DoublyLinkedList
<Attribute
>::Iterator it
413 = attribute
->children
.GetIterator();
414 Attribute
* child
= it
.Next();) {
415 ProcessAttribute(child
);
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
),
445 fHeapRangesToRemove(NULL
),
447 fRootAttribute(NULL
),
454 PackageWriterImpl::~PackageWriterImpl()
456 delete fHeapRangesToRemove
;
457 delete fRootAttribute
;
463 PackageWriterImpl::Init(const char* fileName
,
464 const BPackageWriterParameters
& parameters
)
467 return _Init(NULL
, false, fileName
, parameters
);
468 } catch (status_t error
) {
470 } catch (std::bad_alloc
) {
471 fListener
->PrintError("Out of memory!\n");
478 PackageWriterImpl::Init(BPositionIO
* file
, bool keepFile
,
479 const BPackageWriterParameters
& parameters
)
482 return _Init(file
, keepFile
, NULL
, parameters
);
483 } catch (status_t error
) {
485 } catch (std::bad_alloc
) {
486 fListener
->PrintError("Out of memory!\n");
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
;
503 PackageWriterImpl::SetCheckLicenses(bool checkLicenses
)
505 fCheckLicenses
= checkLicenses
;
510 PackageWriterImpl::AddEntry(const char* fileName
, int fd
)
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
)
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());
529 BPackageWriterListener
* listener
;
531 } errorListener(fListener
);
534 // a file descriptor is given -- read the config from there
535 // stat the file to get the file size
537 if (fstat(fd
, &st
) != 0)
540 BString packageInfoString
;
541 char* buffer
= packageInfoString
.LockBuffer(st
.st_size
);
545 ssize_t result
= read_pos(fd
, 0, buffer
, st
.st_size
);
547 packageInfoString
.UnlockBuffer(0);
551 buffer
[st
.st_size
] = '\0';
552 packageInfoString
.UnlockBuffer(st
.st_size
);
554 result
= fPackageInfo
.ReadFromConfigString(packageInfoString
,
560 BEntry
packageInfoEntry(fileName
);
561 status_t result
= fPackageInfo
.ReadFromConfigFile(
562 packageInfoEntry
, &errorListener
);
564 || (result
= fPackageInfo
.InitCheck()) != B_OK
) {
565 if (!errorListener
.errorSeen
) {
566 fListener
->PrintError("Failed to read %s: %s\n",
567 fileName
, strerror(result
));
574 return _RegisterEntry(fileName
, fd
);
575 } catch (status_t error
) {
577 } catch (std::bad_alloc
) {
578 fListener
->PrintError("Out of memory!\n");
585 PackageWriterImpl::Finish()
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
);
601 fPackageInfo
.SetInstallPath(fInstallPath
);
603 RegisterPackageInfo(PackageAttributes(), fPackageInfo
);
605 if (fCheckLicenses
) {
606 status_t result
= _CheckLicenses();
611 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE
) != 0)
615 } catch (status_t error
) {
617 } catch (std::bad_alloc
) {
618 fListener
->PrintError("Out of memory!\n");
625 PackageWriterImpl::Recompress(BPositionIO
* inputFile
)
627 if (inputFile
== NULL
)
631 return _Recompress(inputFile
);
632 } catch (status_t error
) {
634 } catch (std::bad_alloc
) {
635 fListener
->PrintError("Out of memory!\n");
642 PackageWriterImpl::_Init(BPositionIO
* file
, bool keepFile
, const char* fileName
,
643 const BPackageWriterParameters
& parameters
)
645 status_t result
= inherited::Init(file
, keepFile
, fileName
, parameters
);
649 if (fStringCache
.Init() != B_OK
)
650 throw std::bad_alloc();
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
);
666 result
= packageReader
.Init(File(), false, 0, &header
);
670 fHeapOffset
= packageReader
.HeapOffset();
672 PackageContentHandler
handler(fRootAttribute
, fListener
, fStringCache
);
674 result
= packageReader
.ParseContent(&handler
);
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
);
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();
700 result
= InitHeapReader(fHeapOffset
);
710 PackageWriterImpl::_Finish()
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
);
722 // write the TOC and package attributes
724 _WriteTOC(header
, tocLength
);
726 uint64 attributesLength
;
727 _WritePackageAttributes(header
, attributesLength
);
730 status_t error
= fHeapWriter
->Finish();
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
);
749 fListener
->PrintError("Failed to truncate package file to new "
750 "size: %s\n", strerror(errno
));
754 fListener
->OnPackageSizeInfo(fHeaderSize
, compressedHeapSize
, tocLength
,
755 attributesLength
, totalSize
);
757 // prepare the header
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
);
767 RawWriteBuffer(&header
, sizeof(hpkg_header
), 0);
775 PackageWriterImpl::_Recompress(BPositionIO
* inputFile
)
777 if (inputFile
== NULL
)
780 // create a package reader for the input file
781 PackageReaderImpl
reader(fListener
);
783 status_t error
= reader
.Init(inputFile
, false, 0, &header
);
785 fListener
->PrintError("Failed to open hpkg file: %s\n",
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
);
811 RawWriteBuffer(&header
, sizeof(hpkg_header
), 0);
814 // copy the heap data
815 uint64 bytesCompressed
;
816 error
= fHeapWriter
->AddData(*reader
.RawHeapReader(), uncompressedHeapSize
,
822 error
= fHeapWriter
->Finish();
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
);
834 RawWriteBuffer(&header
, sizeof(hpkg_header
), 0);
843 PackageWriterImpl::_CheckLicenses()
845 BPath systemLicensePath
;
847 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
848 = find_directory(B_SYSTEM_DATA_DIRECTORY
, &systemLicensePath
);
850 = systemLicensePath
.SetTo(HAIKU_BUILD_SYSTEM_DATA_DIRECTORY
);
852 if (result
!= B_OK
) {
853 fListener
->PrintError("unable to find system data path: %s!\n",
857 if ((result
= systemLicensePath
.Append("licenses")) != B_OK
) {
858 fListener
->PrintError("unable to append to system data path!\n");
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
)
871 if (systemLicenseDir
.FindEntry(licenseName
.String(), &license
) == B_OK
)
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());
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
896 Entry
* entry
= fRootEntry
;
897 while (entry
!= NULL
) {
898 if (!entry
->IsImplicit()) {
903 if (*fileName
== '\0')
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
);
916 // find the start of the next component, skipping slashes
917 const char* nextComponent
= nextSlash
+ 1;
918 while (*nextComponent
== '/')
921 entry
= entry
->GetChild(fileName
, nextSlash
- fileName
);
923 fileName
= nextComponent
;
927 // the entry itself or one of its ancestors has been added to the
928 // package explicitly -- stat it, to see, if it exists
930 if (entry
->FD() >= 0) {
931 if (fstatat(entry
->FD(), *fileName
!= '\0' ? fileName
: NULL
, &st
,
932 AT_SYMLINK_NOFOLLOW
) == 0) {
936 if (lstat(originalFileName
, &st
) == 0)
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')
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
== '/')
961 attribute
= attribute
->FindEntryChild(fileName
, nextSlash
- fileName
);
963 fileName
= nextComponent
;
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
);
998 if (value
.encoding
== B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE
)
999 data
.SetData(value
.data
.size
, value
.data
.raw
);
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
);
1013 // data on heap -- read from there
1014 status_t error
= fHeapWriter
->ReadData(data
.Offset(), valueBuffer
,
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
);
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
);
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
)
1061 FileDescriptorCloser fdCloser
;
1063 if (entry
!= NULL
&& entry
->FD() >= 0) {
1064 // a file descriptor is already given -- use that
1067 fd
= openat(dirFD
, fileName
,
1068 O_RDONLY
| (isImplicitEntry
? 0 : O_NOTRAVERSE
));
1070 fListener
->PrintError("Failed to open entry \"%s\": %s\n",
1071 pathBuffer
, strerror(errno
));
1072 throw status_t(errno
);
1079 if (fstat(fd
, &st
) < 0) {
1080 fListener
->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer
,
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
);
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
);
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
)) {
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
)
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
);
1166 entryAttribute
->RemoveChild(attributeAttribute
);
1167 _AttributeRemoved(attributeAttribute
);
1171 // we need to clone the directory FD for fdopendir()
1172 int clonedFD
= dup(fd
);
1174 fListener
->PrintError(
1175 "Failed to dup() directory FD: %s\n", strerror(errno
));
1176 throw status_t(errno
);
1179 DIR* dir
= fdopendir(clonedFD
);
1181 fListener
->PrintError(
1182 "Failed to open directory \"%s\": %s\n", pathBuffer
,
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) {
1196 _UpdateCheckEntryCollisions(entryAttribute
, fd
, NULL
, entry
->d_name
,
1204 PackageWriterImpl::_CompactHeap()
1206 int32 count
= fHeapRangesToRemove
->CountRanges();
1210 // compute the move deltas for the ranges
1211 Array
<uint64
> deltas
;
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(
1227 // remove the ranges from the heap
1228 fHeapWriter
->RemoveDataRanges(*fHeapRangesToRemove
);
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
);
1252 PackageWriterImpl::_RegisterEntry(const char* fileName
, int fd
)
1254 if (*fileName
== '\0') {
1255 fListener
->PrintError("Invalid empty file name\n");
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
,
1270 // find the start of the next component, skipping slashes
1271 const char* nextComponent
= nextSlash
+ 1;
1272 while (*nextComponent
== '/')
1275 bool lastComponent
= *nextComponent
!= '\0';
1277 if (nextSlash
== fileName
) {
1279 entry
= _RegisterEntry(entry
, fileName
, 1, lastComponent
? fd
: -1,
1282 entry
= _RegisterEntry(entry
, fileName
, nextSlash
- fileName
,
1283 lastComponent
? fd
: -1, lastComponent
);
1286 fileName
= nextComponent
;
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 ".."
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);
1316 // nope -- create it
1317 entry
= Entry::Create(name
, nameLength
, fd
, isImplicit
);
1318 parent
->AddChild(entry
);
1326 PackageWriterImpl::_WriteTOC(hpkg_header
& header
, uint64
& _length
)
1328 // write the subsections
1329 uint64 startOffset
= fHeapWriter
->UncompressedHeapSize();
1332 uint64 cachedStringsOffset
= fHeapWriter
->UncompressedHeapSize();
1333 int32 cachedStringsWritten
= WriteCachedStrings(fStringCache
, 2);
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
);
1356 PackageWriterImpl::_WriteAttributeChildren(Attribute
* attribute
)
1358 DoublyLinkedList
<Attribute
>::Iterator it
1359 = attribute
->children
.GetIterator();
1360 while (Attribute
* child
= it
.Next()) {
1362 uint8 encoding
= child
->value
.ApplicableEncoding();
1363 WriteUnsignedLEB128(compose_attribute_tag(child
->id
,
1364 child
->value
.type
, encoding
, !child
->children
.IsEmpty()));
1367 WriteAttributeValue(child
->value
, encoding
);
1369 if (!child
->children
.IsEmpty())
1370 _WriteAttributeChildren(child
);
1373 WriteUnsignedLEB128(0);
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(),
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
;
1401 PackageWriterImpl::_AddEntry(int dirFD
, Entry
* entry
, const char* fileName
,
1404 bool isImplicitEntry
= entry
!= NULL
&& entry
->IsImplicit();
1406 SubPathAdder
pathAdder(fListener
, pathBuffer
, fileName
);
1407 if (!isImplicitEntry
)
1408 fListener
->OnEntryAdded(pathBuffer
);
1412 FileDescriptorCloser fdCloser
;
1414 if (entry
!= NULL
&& entry
->FD() >= 0) {
1415 // a file descriptor is already given -- use that
1418 fd
= openat(dirFD
, fileName
,
1419 O_RDONLY
| (isImplicitEntry
? 0 : O_NOTRAVERSE
));
1421 fListener
->PrintError("Failed to open entry \"%s\": %s\n",
1422 pathBuffer
, strerror(errno
));
1423 throw status_t(errno
);
1430 if (fstat(fd
, &st
) < 0) {
1431 fListener
->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer
,
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
,
1451 _AddDirectoryChildren(entry
, fd
, pathBuffer
);
1456 // check/translate the node type
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
;
1469 // unsupported node type
1470 fListener
->PrintError("Unsupported node type, entry: \"%s\"\n",
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
);
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
));
1495 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME
, uint32(st
.st_crtime
));
1497 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME
, uint32(st
.st_mtime
));
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
);
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
);
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
)) {
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
);
1546 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE
,
1547 (uint32
)attrInfo
.type
);
1550 BAttributeDataReader
dataReader(fd
, entry
->d_name
, attrInfo
.type
);
1551 status_t error
= _AddData(dataReader
, attrInfo
.size
);
1553 throw status_t(error
);
1557 if (S_ISDIR(st
.st_mode
))
1558 _AddDirectoryChildren(entry
, fd
, pathBuffer
);
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
);
1573 // we need to clone the directory FD for fdopendir()
1574 int clonedFD
= dup(fd
);
1576 fListener
->PrintError(
1577 "Failed to dup() directory FD: %s\n", strerror(errno
));
1578 throw status_t(errno
);
1581 DIR* dir
= fdopendir(clonedFD
);
1583 fListener
->PrintError(
1584 "Failed to open directory \"%s\": %s\n", pathBuffer
,
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) {
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
);
1617 PackageWriterImpl::Attribute
*
1618 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID
,
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
);
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
));
1659 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA
, size
, buffer
);
1665 status_t error
= fHeapWriter
->AddData(dataReader
, size
, dataOffset
);
1669 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA
, size
, dataOffset
);
1674 } // namespace BPrivate
1676 } // namespace BHPKG
1678 } // namespace BPackageKit