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