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(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; 473 } 474 } 475 476 477 status_t 478 PackageWriterImpl::Init(BPositionIO* file, bool keepFile, 479 const BPackageWriterParameters& parameters) 480 { 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; 488 } 489 } 490 491 492 status_t 493 PackageWriterImpl::SetInstallPath(const char* installPath) 494 { 495 fInstallPath = installPath; 496 return installPath == NULL 497 || (size_t)fInstallPath.Length() == strlen(installPath) 498 ? B_OK : B_NO_MEMORY; 499 } 500 501 502 void 503 PackageWriterImpl::SetCheckLicenses(bool checkLicenses) 504 { 505 fCheckLicenses = checkLicenses; 506 } 507 508 509 status_t 510 PackageWriterImpl::AddEntry(const char* fileName, int fd) 511 { 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) 517 : 518 listener(_listener), 519 errorSeen(false) 520 { 521 } 522 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; 527 } 528 529 BPackageWriterListener* listener; 530 bool errorSeen; 531 } errorListener(fListener); 532 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; 539 540 BString packageInfoString; 541 char* buffer = packageInfoString.LockBuffer(st.st_size); 542 if (buffer == NULL) 543 return B_NO_MEMORY; 544 545 ssize_t result = read_pos(fd, 0, buffer, st.st_size); 546 if (result < 0) { 547 packageInfoString.UnlockBuffer(0); 548 return errno; 549 } 550 551 buffer[st.st_size] = '\0'; 552 packageInfoString.UnlockBuffer(st.st_size); 553 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)); 568 } 569 return result; 570 } 571 } 572 } 573 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; 580 } 581 } 582 583 584 status_t 585 PackageWriterImpl::Finish() 586 { 587 try { 588 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) { 589 _UpdateCheckEntryCollisions(); 590 591 if (fPackageInfo.InitCheck() != B_OK) 592 _UpdateReadPackageInfo(); 593 } 594 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; 599 } 600 601 fPackageInfo.SetInstallPath(fInstallPath); 602 603 RegisterPackageInfo(PackageAttributes(), fPackageInfo); 604 605 if (fCheckLicenses) { 606 status_t result = _CheckLicenses(); 607 if (result != B_OK) 608 return result; 609 } 610 611 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) 612 _CompactHeap(); 613 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; 620 } 621 } 622 623 624 status_t 625 PackageWriterImpl::Recompress(BPositionIO* inputFile) 626 { 627 if (inputFile == NULL) 628 return B_BAD_VALUE; 629 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; 637 } 638 } 639 640 641 status_t 642 PackageWriterImpl::_Init(BPositionIO* file, bool keepFile, const char* fileName, 643 const BPackageWriterParameters& parameters) 644 { 645 status_t result = inherited::Init(file, keepFile, fileName, parameters); 646 if (result != B_OK) 647 return result; 648 649 if (fStringCache.Init() != B_OK) 650 throw std::bad_alloc(); 651 652 // create entry list 653 fRootEntry = new Entry(NULL, 0, -1, true); 654 655 fRootAttribute = new Attribute(); 656 657 fHeapOffset = fHeaderSize = sizeof(hpkg_header); 658 fTopAttribute = fRootAttribute; 659 660 fHeapRangesToRemove = new RangeArray<uint64>; 661 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; 669 670 fHeapOffset = packageReader.HeapOffset(); 671 672 PackageContentHandler handler(fRootAttribute, fListener, fStringCache); 673 674 result = packageReader.ParseContent(&handler); 675 if (result != B_OK) 676 return result; 677 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)); 681 682 result = InitHeapReader(fHeapOffset); 683 if (result != B_OK) 684 return result; 685 686 fHeapWriter->Reinit(packageReader.RawHeapReader()); 687 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(); 698 } 699 } else { 700 result = InitHeapReader(fHeapOffset); 701 if (result != B_OK) 702 return result; 703 } 704 705 return B_OK; 706 } 707 708 709 status_t 710 PackageWriterImpl::_Finish() 711 { 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); 718 } 719 720 hpkg_header header; 721 722 // write the TOC and package attributes 723 uint64 tocLength; 724 _WriteTOC(header, tocLength); 725 726 uint64 attributesLength; 727 _WritePackageAttributes(header, attributesLength); 728 729 // flush the heap 730 status_t error = fHeapWriter->Finish(); 731 if (error != B_OK) 732 return error; 733 734 uint64 compressedHeapSize = fHeapWriter->CompressedHeapSize(); 735 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()); 742 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; 752 } 753 754 fListener->OnPackageSizeInfo(fHeaderSize, compressedHeapSize, tocLength, 755 attributesLength, totalSize); 756 757 // prepare the header 758 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); 765 766 // write the header 767 RawWriteBuffer(&header, sizeof(hpkg_header), 0); 768 769 SetFinished(true); 770 return B_OK; 771 } 772 773 774 status_t 775 PackageWriterImpl::_Recompress(BPositionIO* inputFile) 776 { 777 if (inputFile == NULL) 778 return B_BAD_VALUE; 779 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; 788 } 789 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; 796 797 off_t totalSize = fHeapWriter->HeapOffset() + (off_t)compressedHeapSize; 798 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); 804 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); 809 810 // write the header 811 RawWriteBuffer(&header, sizeof(hpkg_header), 0); 812 } 813 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; 820 821 // flush the heap 822 error = fHeapWriter->Finish(); 823 if (error != B_OK) 824 return error; 825 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); 832 833 // write the header 834 RawWriteBuffer(&header, sizeof(hpkg_header), 0); 835 } 836 837 SetFinished(true); 838 return B_OK; 839 } 840 841 842 status_t 843 PackageWriterImpl::_CheckLicenses() 844 { 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; 856 } 857 if ((result = systemLicensePath.Append("licenses")) != B_OK) { 858 fListener->PrintError("unable to append to system data path!\n"); 859 return result; 860 } 861 862 BDirectory systemLicenseDir(systemLicensePath.Path()); 863 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; 869 870 BEntry license; 871 if (systemLicenseDir.FindEntry(licenseName.String(), &license) == B_OK) 872 continue; 873 874 // license is not a system license, so it must be contained in package 875 BString licensePath("data/licenses/"); 876 licensePath << licenseName; 877 878 if (!_IsEntryInPackage(licensePath)) { 879 fListener->PrintError("License '%s' isn't contained in package!\n", 880 licenseName.String()); 881 return B_BAD_DATA; 882 } 883 } 884 885 return B_OK; 886 } 887 888 889 bool 890 PackageWriterImpl::_IsEntryInPackage(const char* fileName) 891 { 892 const char* originalFileName = fileName; 893 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; 901 } 902 903 if (*fileName == '\0') 904 break; 905 906 const char* nextSlash = strchr(fileName, '/'); 907 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; 914 } 915 916 // find the start of the next component, skipping slashes 917 const char* nextComponent = nextSlash + 1; 918 while (*nextComponent == '/') 919 nextComponent++; 920 921 entry = entry->GetChild(fileName, nextSlash - fileName); 922 923 fileName = nextComponent; 924 } 925 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; 934 } 935 } else { 936 if (lstat(originalFileName, &st) == 0) 937 return true; 938 } 939 } 940 941 // In update mode the entry might already be in the package. 942 Attribute* attribute = fRootAttribute; 943 fileName = originalFileName; 944 945 while (attribute != NULL) { 946 if (*fileName == '\0') 947 return true; 948 949 const char* nextSlash = strchr(fileName, '/'); 950 951 if (nextSlash == NULL) { 952 // no slash, just the file name 953 return attribute->FindEntryChild(fileName) != NULL; 954 } 955 956 // find the start of the next component, skipping slashes 957 const char* nextComponent = nextSlash + 1; 958 while (*nextComponent == '/') 959 nextComponent++; 960 961 attribute = attribute->FindEntryChild(fileName, nextSlash - fileName); 962 963 fileName = nextComponent; 964 } 965 966 return false; 967 } 968 969 970 void 971 PackageWriterImpl::_UpdateReadPackageInfo() 972 { 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); 980 } 981 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); 988 } 989 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); 995 } 996 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); 1002 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(); 1008 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; 1018 } 1019 1020 valueString.UnlockBuffer(); 1021 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); 1028 } 1029 } 1030 1031 1032 void 1033 PackageWriterImpl::_UpdateCheckEntryCollisions() 1034 { 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); 1041 } 1042 } 1043 1044 1045 void 1046 PackageWriterImpl::_UpdateCheckEntryCollisions(Attribute* parentAttribute, 1047 int dirFD, Entry* entry, const char* fileName, char* pathBuffer) 1048 { 1049 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 1050 1051 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 1052 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; 1058 1059 // open the node 1060 int fd; 1061 FileDescriptorCloser fdCloser; 1062 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); 1073 } 1074 fdCloser.SetTo(fd); 1075 } 1076 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); 1083 } 1084 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); 1090 } 1091 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; 1098 } 1099 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); 1107 } 1108 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); 1113 } 1114 1115 parentAttribute->RemoveChild(entryAttribute); 1116 _AttributeRemoved(entryAttribute); 1117 1118 return; 1119 } 1120 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); 1126 } 1127 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); 1135 } 1136 } else { 1137 // explicitly specified directory -- we need to read the directory 1138 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); 1142 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); 1150 } 1151 1152 // check whether the attribute exists 1153 Attribute* attributeAttribute 1154 = entryAttribute->FindNodeAttributeChild(entry->d_name); 1155 if (attributeAttribute == NULL) 1156 continue; 1157 1158 if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) { 1159 fListener->PrintError("Attribute \"%s\" of specified " 1160 "directory \"%s\" clashes with an archived " 1161 "attribute.\n", pathBuffer); 1162 throw status_t(B_FILE_EXISTS); 1163 } 1164 1165 // remove it 1166 entryAttribute->RemoveChild(attributeAttribute); 1167 _AttributeRemoved(attributeAttribute); 1168 } 1169 } 1170 1171 // we need to clone the directory FD for fdopendir() 1172 int clonedFD = dup(fd); 1173 if (clonedFD < 0) { 1174 fListener->PrintError( 1175 "Failed to dup() directory FD: %s\n", strerror(errno)); 1176 throw status_t(errno); 1177 } 1178 1179 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); 1186 } 1187 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 1188 1189 while (dirent* entry = readdir(dir)) { 1190 // skip "." and ".." 1191 if (strcmp(entry->d_name, ".") == 0 1192 || strcmp(entry->d_name, "..") == 0) { 1193 continue; 1194 } 1195 1196 _UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name, 1197 pathBuffer); 1198 } 1199 } 1200 } 1201 1202 1203 void 1204 PackageWriterImpl::_CompactHeap() 1205 { 1206 int32 count = fHeapRangesToRemove->CountRanges(); 1207 if (count == 0) 1208 return; 1209 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(); 1216 1217 delta += fHeapRangesToRemove->RangeAt(i).size; 1218 } 1219 1220 if (!deltas.Add(delta)) 1221 throw std::bad_alloc(); 1222 1223 // offset the attributes 1224 HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute( 1225 fRootAttribute); 1226 1227 // remove the ranges from the heap 1228 fHeapWriter->RemoveDataRanges(*fHeapRangesToRemove); 1229 } 1230 1231 1232 void 1233 PackageWriterImpl::_AttributeRemoved(Attribute* attribute) 1234 { 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); 1242 1243 for (DoublyLinkedList<Attribute>::Iterator it 1244 = attribute->children.GetIterator(); 1245 Attribute* child = it.Next();) { 1246 _AttributeRemoved(child); 1247 } 1248 } 1249 1250 1251 status_t 1252 PackageWriterImpl::_RegisterEntry(const char* fileName, int fd) 1253 { 1254 if (*fileName == '\0') { 1255 fListener->PrintError("Invalid empty file name\n"); 1256 return B_BAD_VALUE; 1257 } 1258 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; 1268 } 1269 1270 // find the start of the next component, skipping slashes 1271 const char* nextComponent = nextSlash + 1; 1272 while (*nextComponent == '/') 1273 nextComponent++; 1274 1275 bool lastComponent = *nextComponent != '\0'; 1276 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); 1284 } 1285 1286 fileName = nextComponent; 1287 } 1288 1289 return B_OK; 1290 } 1291 1292 1293 PackageWriterImpl::Entry* 1294 PackageWriterImpl::_RegisterEntry(Entry* parent, const char* name, 1295 size_t nameLength, int fd, bool isImplicit) 1296 { 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); 1303 } 1304 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); 1314 } 1315 } else { 1316 // nope -- create it 1317 entry = Entry::Create(name, nameLength, fd, isImplicit); 1318 parent->AddChild(entry); 1319 } 1320 1321 return entry; 1322 } 1323 1324 1325 void 1326 PackageWriterImpl::_WriteTOC(hpkg_header& header, uint64& _length) 1327 { 1328 // write the subsections 1329 uint64 startOffset = fHeapWriter->UncompressedHeapSize(); 1330 1331 // cached strings 1332 uint64 cachedStringsOffset = fHeapWriter->UncompressedHeapSize(); 1333 int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2); 1334 1335 // main TOC section 1336 uint64 mainOffset = fHeapWriter->UncompressedHeapSize(); 1337 _WriteAttributeChildren(fRootAttribute); 1338 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); 1345 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); 1350 1351 _length = tocSize; 1352 } 1353 1354 1355 void 1356 PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute) 1357 { 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())); 1365 1366 // write value 1367 WriteAttributeValue(child->value, encoding); 1368 1369 if (!child->children.IsEmpty()) 1370 _WriteAttributeChildren(child); 1371 } 1372 1373 WriteUnsignedLEB128(0); 1374 } 1375 1376 1377 void 1378 PackageWriterImpl::_WritePackageAttributes(hpkg_header& header, uint64& _length) 1379 { 1380 // write cached strings and package attributes tree 1381 off_t startOffset = fHeapWriter->UncompressedHeapSize(); 1382 1383 uint32 stringsLength; 1384 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 1385 stringsLength); 1386 1387 // notify listener 1388 uint32 attributesLength = fHeapWriter->UncompressedHeapSize() - startOffset; 1389 fListener->OnPackageAttributesSizeInfo(stringsCount, attributesLength); 1390 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); 1395 1396 _length = attributesLength; 1397 } 1398 1399 1400 void 1401 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName, 1402 char* pathBuffer) 1403 { 1404 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 1405 1406 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 1407 if (!isImplicitEntry) 1408 fListener->OnEntryAdded(pathBuffer); 1409 1410 // open the node 1411 int fd; 1412 FileDescriptorCloser fdCloser; 1413 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); 1424 } 1425 fdCloser.SetTo(fd); 1426 } 1427 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); 1434 } 1435 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); 1441 } 1442 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; 1453 } 1454 } 1455 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); 1473 } 1474 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); 1480 } 1481 1482 Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute); 1483 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)); 1491 } 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! 1500 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); 1509 } 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); 1519 } 1520 1521 path[bytesRead] = '\0'; 1522 _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path); 1523 } 1524 } 1525 1526 // add attributes 1527 if (DIR* attrDir = fs_fopen_attr_dir(fd)) { 1528 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir); 1529 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); 1537 } 1538 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); 1544 1545 // add type 1546 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE, 1547 (uint32)attrInfo.type); 1548 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); 1554 } 1555 } 1556 1557 if (S_ISDIR(st.st_mode)) 1558 _AddDirectoryChildren(entry, fd, pathBuffer); 1559 } 1560 1561 1562 void 1563 PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer) 1564 { 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); 1571 } 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); 1579 } 1580 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); 1588 } 1589 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 1590 1591 while (dirent* entry = readdir(dir)) { 1592 // skip "." and ".." 1593 if (strcmp(entry->d_name, ".") == 0 1594 || strcmp(entry->d_name, "..") == 0) { 1595 continue; 1596 } 1597 1598 _AddEntry(fd, NULL, entry->d_name, pathBuffer); 1599 } 1600 } 1601 } 1602 1603 1604 PackageWriterImpl::Attribute* 1605 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id, 1606 const AttributeValue& value) 1607 { 1608 Attribute* attribute = new Attribute(id); 1609 1610 attribute->value = value; 1611 fTopAttribute->AddChild(attribute); 1612 1613 return attribute; 1614 } 1615 1616 1617 PackageWriterImpl::Attribute* 1618 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID, 1619 const char* value) 1620 { 1621 AttributeValue attributeValue; 1622 attributeValue.SetTo(fStringCache.Get(value)); 1623 return _AddAttribute(attributeID, attributeValue); 1624 } 1625 1626 1627 PackageWriterImpl::Attribute* 1628 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1629 uint64 dataSize, uint64 dataOffset) 1630 { 1631 AttributeValue attributeValue; 1632 attributeValue.SetToData(dataSize, dataOffset); 1633 return _AddAttribute(attributeID, attributeValue); 1634 } 1635 1636 1637 PackageWriterImpl::Attribute* 1638 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1639 uint64 dataSize, const uint8* data) 1640 { 1641 AttributeValue attributeValue; 1642 attributeValue.SetToData(dataSize, data); 1643 return _AddAttribute(attributeID, attributeValue); 1644 } 1645 1646 1647 status_t 1648 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size) 1649 { 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; 1657 } 1658 1659 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer); 1660 return B_OK; 1661 } 1662 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; 1668 1669 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, dataOffset); 1670 return B_OK; 1671 } 1672 1673 1674 } // namespace BPrivate 1675 1676 } // namespace BHPKG 1677 1678 } // namespace BPackageKit 1679