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