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