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 AttrDirCloser attrDir(fs_fopen_attr_dir(fd)); 1142 if (attrDir.IsSet()) { 1143 while (dirent* entry = fs_read_attr_dir(attrDir.Get())) { 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 DirCloser dir(fdopendir(clonedFD)); 1180 if (!dir.IsSet()) { 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 1188 while (dirent* entry = readdir(dir.Get())) { 1189 // skip "." and ".." 1190 if (strcmp(entry->d_name, ".") == 0 1191 || strcmp(entry->d_name, "..") == 0) { 1192 continue; 1193 } 1194 1195 _UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name, 1196 pathBuffer); 1197 } 1198 } 1199 } 1200 1201 1202 void 1203 PackageWriterImpl::_CompactHeap() 1204 { 1205 int32 count = fHeapRangesToRemove->CountRanges(); 1206 if (count == 0) 1207 return; 1208 1209 // compute the move deltas for the ranges 1210 Array<uint64> deltas; 1211 uint64 delta = 0; 1212 for (int32 i = 0; i < count; i++) { 1213 if (!deltas.Add(delta)) 1214 throw std::bad_alloc(); 1215 1216 delta += fHeapRangesToRemove->RangeAt(i).size; 1217 } 1218 1219 if (!deltas.Add(delta)) 1220 throw std::bad_alloc(); 1221 1222 // offset the attributes 1223 HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute( 1224 fRootAttribute); 1225 1226 // remove the ranges from the heap 1227 fHeapWriter->RemoveDataRanges(*fHeapRangesToRemove); 1228 } 1229 1230 1231 void 1232 PackageWriterImpl::_AttributeRemoved(Attribute* attribute) 1233 { 1234 AttributeValue& value = attribute->value; 1235 if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW 1236 && value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) { 1237 if (!fHeapRangesToRemove->AddRange(value.data.offset, value.data.size)) 1238 throw std::bad_alloc(); 1239 } else if (value.type == B_HPKG_ATTRIBUTE_TYPE_STRING) 1240 fStringCache.Put(value.string); 1241 1242 for (DoublyLinkedList<Attribute>::Iterator it 1243 = attribute->children.GetIterator(); 1244 Attribute* child = it.Next();) { 1245 _AttributeRemoved(child); 1246 } 1247 } 1248 1249 1250 status_t 1251 PackageWriterImpl::_RegisterEntry(const char* fileName, int fd) 1252 { 1253 if (*fileName == '\0') { 1254 fListener->PrintError("Invalid empty file name\n"); 1255 return B_BAD_VALUE; 1256 } 1257 1258 // add all components of the path 1259 Entry* entry = fRootEntry; 1260 while (*fileName != 0) { 1261 const char* nextSlash = strchr(fileName, '/'); 1262 // no slash, just add the file name 1263 if (nextSlash == NULL) { 1264 entry = _RegisterEntry(entry, fileName, strlen(fileName), fd, 1265 false); 1266 break; 1267 } 1268 1269 // find the start of the next component, skipping slashes 1270 const char* nextComponent = nextSlash + 1; 1271 while (*nextComponent == '/') 1272 nextComponent++; 1273 1274 bool lastComponent = *nextComponent != '\0'; 1275 1276 if (nextSlash == fileName) { 1277 // the FS root 1278 entry = _RegisterEntry(entry, fileName, 1, lastComponent ? fd : -1, 1279 lastComponent); 1280 } else { 1281 entry = _RegisterEntry(entry, fileName, nextSlash - fileName, 1282 lastComponent ? fd : -1, lastComponent); 1283 } 1284 1285 fileName = nextComponent; 1286 } 1287 1288 return B_OK; 1289 } 1290 1291 1292 PackageWriterImpl::Entry* 1293 PackageWriterImpl::_RegisterEntry(Entry* parent, const char* name, 1294 size_t nameLength, int fd, bool isImplicit) 1295 { 1296 // check the component name -- don't allow "." or ".." 1297 if (name[0] == '.' 1298 && (nameLength == 1 || (nameLength == 2 && name[1] == '.'))) { 1299 fListener->PrintError("Invalid file name: \".\" and \"..\" " 1300 "are not allowed as path components\n"); 1301 throw status_t(B_BAD_VALUE); 1302 } 1303 1304 // the entry might already exist 1305 Entry* entry = parent->GetChild(name, nameLength); 1306 if (entry != NULL) { 1307 // If the entry was implicit and is no longer, we mark it non-implicit 1308 // and delete all of it's children. 1309 if (entry->IsImplicit() && !isImplicit) { 1310 entry->DeleteChildren(); 1311 entry->SetImplicit(false); 1312 entry->SetFD(fd); 1313 } 1314 } else { 1315 // nope -- create it 1316 entry = Entry::Create(name, nameLength, fd, isImplicit); 1317 parent->AddChild(entry); 1318 } 1319 1320 return entry; 1321 } 1322 1323 1324 void 1325 PackageWriterImpl::_WriteTOC(hpkg_header& header, uint64& _length) 1326 { 1327 // write the subsections 1328 uint64 startOffset = fHeapWriter->UncompressedHeapSize(); 1329 1330 // cached strings 1331 uint64 cachedStringsOffset = fHeapWriter->UncompressedHeapSize(); 1332 int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2); 1333 1334 // main TOC section 1335 uint64 mainOffset = fHeapWriter->UncompressedHeapSize(); 1336 _WriteAttributeChildren(fRootAttribute); 1337 1338 // notify the listener 1339 uint64 endOffset = fHeapWriter->UncompressedHeapSize(); 1340 uint64 stringsSize = mainOffset - cachedStringsOffset; 1341 uint64 mainSize = endOffset - mainOffset; 1342 uint64 tocSize = endOffset - startOffset; 1343 fListener->OnTOCSizeInfo(stringsSize, mainSize, tocSize); 1344 1345 // update the header 1346 header.toc_length = B_HOST_TO_BENDIAN_INT64(tocSize); 1347 header.toc_strings_length = B_HOST_TO_BENDIAN_INT64(stringsSize); 1348 header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten); 1349 1350 _length = tocSize; 1351 } 1352 1353 1354 void 1355 PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute) 1356 { 1357 DoublyLinkedList<Attribute>::Iterator it 1358 = attribute->children.GetIterator(); 1359 while (Attribute* child = it.Next()) { 1360 // write tag 1361 uint8 encoding = child->value.ApplicableEncoding(); 1362 WriteUnsignedLEB128(compose_attribute_tag(child->id, 1363 child->value.type, encoding, !child->children.IsEmpty())); 1364 1365 // write value 1366 WriteAttributeValue(child->value, encoding); 1367 1368 if (!child->children.IsEmpty()) 1369 _WriteAttributeChildren(child); 1370 } 1371 1372 WriteUnsignedLEB128(0); 1373 } 1374 1375 1376 void 1377 PackageWriterImpl::_WritePackageAttributes(hpkg_header& header, uint64& _length) 1378 { 1379 // write cached strings and package attributes tree 1380 off_t startOffset = fHeapWriter->UncompressedHeapSize(); 1381 1382 uint32 stringsLength; 1383 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 1384 stringsLength); 1385 1386 // notify listener 1387 uint32 attributesLength = fHeapWriter->UncompressedHeapSize() - startOffset; 1388 fListener->OnPackageAttributesSizeInfo(stringsCount, attributesLength); 1389 1390 // update the header 1391 header.attributes_length = B_HOST_TO_BENDIAN_INT32(attributesLength); 1392 header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount); 1393 header.attributes_strings_length = B_HOST_TO_BENDIAN_INT32(stringsLength); 1394 1395 _length = attributesLength; 1396 } 1397 1398 1399 void 1400 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName, 1401 char* pathBuffer) 1402 { 1403 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 1404 1405 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 1406 if (!isImplicitEntry) 1407 fListener->OnEntryAdded(pathBuffer); 1408 1409 // open the node 1410 int fd; 1411 FileDescriptorCloser fdCloser; 1412 1413 if (entry != NULL && entry->FD() >= 0) { 1414 // a file descriptor is already given -- use that 1415 fd = entry->FD(); 1416 } else { 1417 fd = openat(dirFD, fileName, 1418 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); 1419 if (fd < 0) { 1420 fListener->PrintError("Failed to open entry \"%s\": %s\n", 1421 pathBuffer, strerror(errno)); 1422 throw status_t(errno); 1423 } 1424 fdCloser.SetTo(fd); 1425 } 1426 1427 // stat the node 1428 struct stat st; 1429 if (fstat(fd, &st) < 0) { 1430 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer, 1431 strerror(errno)); 1432 throw status_t(errno); 1433 } 1434 1435 // implicit entries must be directories 1436 if (isImplicitEntry && !S_ISDIR(st.st_mode)) { 1437 fListener->PrintError("Non-leaf path component \"%s\" is not a " 1438 "directory\n", pathBuffer); 1439 throw status_t(B_BAD_VALUE); 1440 } 1441 1442 // In update mode we don't need to add an entry attribute for an implicit 1443 // directory, if there already is one. 1444 Attribute* entryAttribute = NULL; 1445 if (S_ISDIR(st.st_mode) && (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) { 1446 entryAttribute = fTopAttribute->FindEntryChild(fileName); 1447 if (entryAttribute != NULL && isImplicitEntry) { 1448 Stacker<Attribute> entryAttributeStacker(fTopAttribute, 1449 entryAttribute); 1450 _AddDirectoryChildren(entry, fd, pathBuffer); 1451 return; 1452 } 1453 } 1454 1455 // check/translate the node type 1456 uint8 fileType; 1457 uint32 defaultPermissions; 1458 if (S_ISREG(st.st_mode)) { 1459 fileType = B_HPKG_FILE_TYPE_FILE; 1460 defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS; 1461 } else if (S_ISLNK(st.st_mode)) { 1462 fileType = B_HPKG_FILE_TYPE_SYMLINK; 1463 defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS; 1464 } else if (S_ISDIR(st.st_mode)) { 1465 fileType = B_HPKG_FILE_TYPE_DIRECTORY; 1466 defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS; 1467 } else { 1468 // unsupported node type 1469 fListener->PrintError("Unsupported node type, entry: \"%s\"\n", 1470 pathBuffer); 1471 throw status_t(B_UNSUPPORTED); 1472 } 1473 1474 // add attribute entry, if it doesn't already exist (update mode, directory) 1475 bool isNewEntry = entryAttribute == NULL; 1476 if (entryAttribute == NULL) { 1477 entryAttribute = _AddStringAttribute( 1478 B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName); 1479 } 1480 1481 Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute); 1482 1483 if (isNewEntry) { 1484 // add stat data 1485 if (fileType != B_HPKG_DEFAULT_FILE_TYPE) 1486 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType); 1487 if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) { 1488 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS, 1489 uint32(st.st_mode & ALLPERMS)); 1490 } 1491 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime)); 1492 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime)); 1493 #ifdef __HAIKU__ 1494 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime)); 1495 #else 1496 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime)); 1497 #endif 1498 // TODO: File user/group! 1499 1500 // add file data/symlink path 1501 if (S_ISREG(st.st_mode)) { 1502 // regular file -- add data 1503 if (st.st_size > 0) { 1504 BFDDataReader dataReader(fd); 1505 status_t error = _AddData(dataReader, st.st_size); 1506 if (error != B_OK) 1507 throw status_t(error); 1508 } 1509 } else if (S_ISLNK(st.st_mode)) { 1510 // symlink -- add link address 1511 char path[B_PATH_NAME_LENGTH + 1]; 1512 ssize_t bytesRead = readlinkat(dirFD, fileName, path, 1513 B_PATH_NAME_LENGTH); 1514 if (bytesRead < 0) { 1515 fListener->PrintError("Failed to read symlink \"%s\": %s\n", 1516 pathBuffer, strerror(errno)); 1517 throw status_t(errno); 1518 } 1519 1520 path[bytesRead] = '\0'; 1521 _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path); 1522 } 1523 } 1524 1525 // add attributes 1526 AttrDirCloser attrDir(fs_fopen_attr_dir(fd)); 1527 if (attrDir.IsSet()) { 1528 while (dirent* entry = fs_read_attr_dir(attrDir.Get())) { 1529 attr_info attrInfo; 1530 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { 1531 fListener->PrintError( 1532 "Failed to stat attribute \"%s\" of file \"%s\": %s\n", 1533 entry->d_name, pathBuffer, strerror(errno)); 1534 throw status_t(errno); 1535 } 1536 1537 // create attribute entry 1538 Attribute* attributeAttribute = _AddStringAttribute( 1539 B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name); 1540 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, 1541 attributeAttribute); 1542 1543 // add type 1544 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE, 1545 (uint32)attrInfo.type); 1546 1547 // add data 1548 BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type); 1549 status_t error = _AddData(dataReader, attrInfo.size); 1550 if (error != B_OK) 1551 throw status_t(error); 1552 } 1553 } 1554 1555 if (S_ISDIR(st.st_mode)) 1556 _AddDirectoryChildren(entry, fd, pathBuffer); 1557 } 1558 1559 1560 void 1561 PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer) 1562 { 1563 // directory -- recursively add children 1564 if (entry != NULL && entry->IsImplicit()) { 1565 // this is an implicit entry -- just add it's children 1566 for (EntryList::ConstIterator it = entry->ChildIterator(); 1567 Entry* child = it.Next();) { 1568 _AddEntry(fd, child, child->Name(), pathBuffer); 1569 } 1570 } else { 1571 // we need to clone the directory FD for fdopendir() 1572 int clonedFD = dup(fd); 1573 if (clonedFD < 0) { 1574 fListener->PrintError( 1575 "Failed to dup() directory FD: %s\n", strerror(errno)); 1576 throw status_t(errno); 1577 } 1578 1579 DirCloser dir(fdopendir(clonedFD)); 1580 if (!dir.IsSet()) { 1581 fListener->PrintError( 1582 "Failed to open directory \"%s\": %s\n", pathBuffer, 1583 strerror(errno)); 1584 close(clonedFD); 1585 throw status_t(errno); 1586 } 1587 1588 while (dirent* entry = readdir(dir.Get())) { 1589 // skip "." and ".." 1590 if (strcmp(entry->d_name, ".") == 0 1591 || strcmp(entry->d_name, "..") == 0) { 1592 continue; 1593 } 1594 1595 _AddEntry(fd, NULL, entry->d_name, pathBuffer); 1596 } 1597 } 1598 } 1599 1600 1601 PackageWriterImpl::Attribute* 1602 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id, 1603 const AttributeValue& value) 1604 { 1605 Attribute* attribute = new Attribute(id); 1606 1607 attribute->value = value; 1608 fTopAttribute->AddChild(attribute); 1609 1610 return attribute; 1611 } 1612 1613 1614 PackageWriterImpl::Attribute* 1615 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID, 1616 const char* value) 1617 { 1618 AttributeValue attributeValue; 1619 attributeValue.SetTo(fStringCache.Get(value)); 1620 return _AddAttribute(attributeID, attributeValue); 1621 } 1622 1623 1624 PackageWriterImpl::Attribute* 1625 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1626 uint64 dataSize, uint64 dataOffset) 1627 { 1628 AttributeValue attributeValue; 1629 attributeValue.SetToData(dataSize, dataOffset); 1630 return _AddAttribute(attributeID, attributeValue); 1631 } 1632 1633 1634 PackageWriterImpl::Attribute* 1635 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1636 uint64 dataSize, const uint8* data) 1637 { 1638 AttributeValue attributeValue; 1639 attributeValue.SetToData(dataSize, data); 1640 return _AddAttribute(attributeID, attributeValue); 1641 } 1642 1643 1644 status_t 1645 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size) 1646 { 1647 // add short data inline 1648 if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) { 1649 uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE]; 1650 status_t error = dataReader.ReadData(0, buffer, size); 1651 if (error != B_OK) { 1652 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1653 return error; 1654 } 1655 1656 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer); 1657 return B_OK; 1658 } 1659 1660 // add data to heap 1661 uint64 dataOffset; 1662 status_t error = fHeapWriter->AddData(dataReader, size, dataOffset); 1663 if (error != B_OK) 1664 return error; 1665 1666 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, dataOffset); 1667 return B_OK; 1668 } 1669 1670 1671 } // namespace BPrivate 1672 1673 } // namespace BHPKG 1674 1675 } // namespace BPackageKit 1676