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