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, sizeof(hpkg_header), 610 parameters); 611 if (result != B_OK) 612 return result; 613 614 if (fStringCache.Init() != B_OK) 615 throw std::bad_alloc(); 616 617 // create entry list 618 fRootEntry = new Entry(NULL, 0, -1, true); 619 620 fRootAttribute = new Attribute(); 621 622 fHeapOffset = fHeaderSize = sizeof(hpkg_header); 623 fTopAttribute = fRootAttribute; 624 625 // in update mode, parse the TOC 626 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) { 627 PackageReaderImpl packageReader(fListener); 628 result = packageReader.Init(FD(), false, 0); 629 if (result != B_OK) 630 return result; 631 632 fHeapOffset = packageReader.HeapOffset(); 633 634 PackageContentHandler handler(fRootAttribute, fListener, fStringCache); 635 636 result = packageReader.ParseContent(&handler); 637 if (result != B_OK) 638 return result; 639 640 fHeapWriter->Reinit(packageReader.RawHeapReader()); 641 } 642 643 return B_OK; 644 } 645 646 647 status_t 648 PackageWriterImpl::_CheckLicenses() 649 { 650 BPath systemLicensePath; 651 status_t result 652 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 653 = find_directory(B_SYSTEM_DATA_DIRECTORY, &systemLicensePath); 654 #else 655 = systemLicensePath.SetTo(HAIKU_BUILD_SYSTEM_DATA_DIRECTORY); 656 #endif 657 if (result != B_OK) { 658 fListener->PrintError("unable to find system data path!\n"); 659 return result; 660 } 661 if ((result = systemLicensePath.Append("licenses")) != B_OK) { 662 fListener->PrintError("unable to append to system data path!\n"); 663 return result; 664 } 665 666 BDirectory systemLicenseDir(systemLicensePath.Path()); 667 668 const BStringList& licenseList = fPackageInfo.LicenseList(); 669 for (int i = 0; i < licenseList.CountStrings(); ++i) { 670 const BString& licenseName = licenseList.StringAt(i); 671 if (licenseName == kPublicDomainLicenseName) 672 continue; 673 674 BEntry license; 675 if (systemLicenseDir.FindEntry(licenseName.String(), &license) == B_OK) 676 continue; 677 678 // license is not a system license, so it must be contained in package 679 BString licensePath("data/licenses/"); 680 licensePath << licenseName; 681 682 if (!_IsEntryInPackage(licensePath)) { 683 fListener->PrintError("License '%s' isn't contained in package!\n", 684 licenseName.String()); 685 return B_BAD_DATA; 686 } 687 } 688 689 return B_OK; 690 } 691 692 693 bool 694 PackageWriterImpl::_IsEntryInPackage(const char* fileName) 695 { 696 const char* originalFileName = fileName; 697 698 // find the closest ancestor of the entry that is in the added entries 699 bool added = false; 700 Entry* entry = fRootEntry; 701 while (entry != NULL) { 702 if (!entry->IsImplicit()) { 703 added = true; 704 break; 705 } 706 707 if (*fileName == '\0') 708 break; 709 710 const char* nextSlash = strchr(fileName, '/'); 711 712 if (nextSlash == NULL) { 713 // no slash, just the file name 714 size_t length = strlen(fileName); 715 entry = entry->GetChild(fileName, length); 716 fileName += length; 717 continue; 718 } 719 720 // find the start of the next component, skipping slashes 721 const char* nextComponent = nextSlash + 1; 722 while (*nextComponent == '/') 723 nextComponent++; 724 725 entry = entry->GetChild(fileName, nextSlash - fileName); 726 727 fileName = nextComponent; 728 } 729 730 if (added) { 731 // the entry itself or one of its ancestors has been added to the 732 // package explicitly -- stat it, to see, if it exists 733 struct stat st; 734 if (entry->FD() >= 0) { 735 if (fstatat(entry->FD(), *fileName != '\0' ? fileName : NULL, &st, 736 AT_SYMLINK_NOFOLLOW) == 0) { 737 return true; 738 } 739 } else { 740 if (lstat(originalFileName, &st) == 0) 741 return true; 742 } 743 } 744 745 // In update mode the entry might already be in the package. 746 Attribute* attribute = fRootAttribute; 747 fileName = originalFileName; 748 749 while (attribute != NULL) { 750 if (*fileName == '\0') 751 return true; 752 753 const char* nextSlash = strchr(fileName, '/'); 754 755 if (nextSlash == NULL) { 756 // no slash, just the file name 757 return attribute->FindEntryChild(fileName) != NULL; 758 } 759 760 // find the start of the next component, skipping slashes 761 const char* nextComponent = nextSlash + 1; 762 while (*nextComponent == '/') 763 nextComponent++; 764 765 attribute = attribute->FindEntryChild(fileName, nextSlash - fileName); 766 767 fileName = nextComponent; 768 } 769 770 return false; 771 } 772 773 774 void 775 PackageWriterImpl::_UpdateReadPackageInfo() 776 { 777 // get the .PackageInfo entry attribute 778 Attribute* attribute = fRootAttribute->FindEntryChild( 779 B_HPKG_PACKAGE_INFO_FILE_NAME); 780 if (attribute == NULL) { 781 fListener->PrintError("No %s in package file.\n", 782 B_HPKG_PACKAGE_INFO_FILE_NAME); 783 throw status_t(B_BAD_DATA); 784 } 785 786 // get the data attribute 787 Attribute* dataAttribute = attribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_DATA); 788 if (dataAttribute == NULL) { 789 fListener->PrintError("%s entry in package file doesn't have data.\n", 790 B_HPKG_PACKAGE_INFO_FILE_NAME); 791 throw status_t(B_BAD_DATA); 792 } 793 794 AttributeValue& value = dataAttribute->value; 795 if (value.type != B_HPKG_ATTRIBUTE_TYPE_RAW) { 796 fListener->PrintError("%s entry in package file has an invalid data " 797 "attribute (not of type raw).\n", B_HPKG_PACKAGE_INFO_FILE_NAME); 798 throw status_t(B_BAD_DATA); 799 } 800 801 BPackageData data; 802 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) 803 data.SetData(value.data.size, value.data.raw); 804 else 805 data.SetData(value.data.size, value.data.offset); 806 807 // read the value into a string 808 BString valueString; 809 char* valueBuffer = valueString.LockBuffer(value.data.size); 810 if (valueBuffer == NULL) 811 throw std::bad_alloc(); 812 813 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) { 814 // data encoded inline -- just copy to buffer 815 memcpy(valueBuffer, value.data.raw, value.data.size); 816 } else { 817 // data on heap -- read from there 818 status_t error = fHeapWriter->ReadData(data.Offset(), valueBuffer, 819 data.Size()); 820 if (error != B_OK) 821 throw error; 822 } 823 824 valueString.UnlockBuffer(); 825 826 // parse the package info 827 status_t error = fPackageInfo.ReadFromConfigString(valueString); 828 if (error != B_OK) { 829 fListener->PrintError("Failed to parse package info data from package " 830 "file: %s\n", strerror(error)); 831 throw status_t(error); 832 } 833 } 834 835 836 void 837 PackageWriterImpl::_UpdateCheckEntryCollisions() 838 { 839 for (EntryList::ConstIterator it = fRootEntry->ChildIterator(); 840 Entry* entry = it.Next();) { 841 char pathBuffer[B_PATH_NAME_LENGTH]; 842 pathBuffer[0] = '\0'; 843 _UpdateCheckEntryCollisions(fRootAttribute, AT_FDCWD, entry, 844 entry->Name(), pathBuffer); 845 } 846 } 847 848 849 void 850 PackageWriterImpl::_UpdateCheckEntryCollisions(Attribute* parentAttribute, 851 int dirFD, Entry* entry, const char* fileName, char* pathBuffer) 852 { 853 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 854 855 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 856 857 // Check whether there's an entry attribute for this entry. If not, we can 858 // ignore this entry. 859 Attribute* entryAttribute = parentAttribute->FindEntryChild(fileName); 860 if (entryAttribute == NULL) 861 return; 862 863 // open the node 864 int fd; 865 FileDescriptorCloser fdCloser; 866 867 if (entry != NULL && entry->FD() >= 0) { 868 // a file descriptor is already given -- use that 869 fd = entry->FD(); 870 } else { 871 fd = openat(dirFD, fileName, 872 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); 873 if (fd < 0) { 874 fListener->PrintError("Failed to open entry \"%s\": %s\n", 875 pathBuffer, strerror(errno)); 876 throw status_t(errno); 877 } 878 fdCloser.SetTo(fd); 879 } 880 881 // stat the node 882 struct stat st; 883 if (fstat(fd, &st) < 0) { 884 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer, 885 strerror(errno)); 886 throw status_t(errno); 887 } 888 889 // implicit entries must be directories 890 if (isImplicitEntry && !S_ISDIR(st.st_mode)) { 891 fListener->PrintError("Non-leaf path component \"%s\" is not a " 892 "directory.\n", pathBuffer); 893 throw status_t(B_BAD_VALUE); 894 } 895 896 // get the pre-existing node's file type 897 uint32 preExistingFileType = B_HPKG_DEFAULT_FILE_TYPE; 898 if (Attribute* fileTypeAttribute 899 = entryAttribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_FILE_TYPE)) { 900 if (fileTypeAttribute->value.type == B_HPKG_ATTRIBUTE_TYPE_UINT) 901 preExistingFileType = fileTypeAttribute->value.unsignedInt; 902 } 903 904 // Compare the node type with that of the pre-existing one. 905 if (!S_ISDIR(st.st_mode)) { 906 // the pre-existing must not a directory either -- we'll remove it 907 if (preExistingFileType == B_HPKG_FILE_TYPE_DIRECTORY) { 908 fListener->PrintError("Specified file \"%s\" clashes with an " 909 "archived directory.\n", pathBuffer); 910 throw status_t(B_BAD_VALUE); 911 } 912 913 if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) { 914 fListener->PrintError("Specified file \"%s\" clashes with an " 915 "archived file.\n", pathBuffer); 916 throw status_t(B_FILE_EXISTS); 917 } 918 919 parentAttribute->RemoveChild(entryAttribute); 920 _AttributeRemoved(entryAttribute); 921 922 return; 923 } 924 925 // the pre-existing entry needs to be a directory too -- we will merge 926 if (preExistingFileType != B_HPKG_FILE_TYPE_DIRECTORY) { 927 fListener->PrintError("Specified directory \"%s\" clashes with an " 928 "archived non-directory.\n", pathBuffer); 929 throw status_t(B_BAD_VALUE); 930 } 931 932 // directory -- recursively add children 933 if (isImplicitEntry) { 934 // this is an implicit entry -- just check the child entries 935 for (EntryList::ConstIterator it = entry->ChildIterator(); 936 Entry* child = it.Next();) { 937 _UpdateCheckEntryCollisions(entryAttribute, fd, child, 938 child->Name(), pathBuffer); 939 } 940 } else { 941 // explicitly specified directory -- we need to read the directory 942 943 // first we check for colliding node attributes, though 944 if (DIR* attrDir = fs_fopen_attr_dir(fd)) { 945 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir); 946 947 while (dirent* entry = fs_read_attr_dir(attrDir)) { 948 attr_info attrInfo; 949 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { 950 fListener->PrintError( 951 "Failed to stat attribute \"%s\" of directory \"%s\": " 952 "%s\n", entry->d_name, pathBuffer, strerror(errno)); 953 throw status_t(errno); 954 } 955 956 // check whether the attribute exists 957 Attribute* attributeAttribute 958 = entryAttribute->FindNodeAttributeChild(entry->d_name); 959 if (attributeAttribute == NULL) 960 continue; 961 962 if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) { 963 fListener->PrintError("Attribute \"%s\" of specified " 964 "directory \"%s\" clashes with an archived " 965 "attribute.\n", pathBuffer); 966 throw status_t(B_FILE_EXISTS); 967 } 968 969 // remove it 970 entryAttribute->RemoveChild(attributeAttribute); 971 _AttributeRemoved(attributeAttribute); 972 } 973 } 974 975 // we need to clone the directory FD for fdopendir() 976 int clonedFD = dup(fd); 977 if (clonedFD < 0) { 978 fListener->PrintError( 979 "Failed to dup() directory FD: %s\n", strerror(errno)); 980 throw status_t(errno); 981 } 982 983 DIR* dir = fdopendir(clonedFD); 984 if (dir == NULL) { 985 fListener->PrintError( 986 "Failed to open directory \"%s\": %s\n", pathBuffer, 987 strerror(errno)); 988 close(clonedFD); 989 throw status_t(errno); 990 } 991 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 992 993 while (dirent* entry = readdir(dir)) { 994 // skip "." and ".." 995 if (strcmp(entry->d_name, ".") == 0 996 || strcmp(entry->d_name, "..") == 0) { 997 continue; 998 } 999 1000 _UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name, 1001 pathBuffer); 1002 } 1003 } 1004 } 1005 1006 1007 void 1008 PackageWriterImpl::_CompactHeap() 1009 { 1010 int32 count = fHeapRangesToRemove->CountRanges(); 1011 if (count == 0) 1012 return; 1013 1014 // compute the move deltas for the ranges 1015 Array<uint64> deltas; 1016 uint64 delta = 0; 1017 for (int32 i = 0; i < count; i++) { 1018 if (!deltas.Add(delta)) 1019 throw std::bad_alloc(); 1020 1021 delta += fHeapRangesToRemove->RangeAt(i).size; 1022 } 1023 1024 if (!deltas.Add(delta)) 1025 throw std::bad_alloc(); 1026 1027 // offset the attributes 1028 HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute( 1029 fRootAttribute); 1030 1031 // remove the ranges from the heap 1032 fHeapWriter->RemoveDataRanges(*fHeapRangesToRemove); 1033 } 1034 1035 1036 void 1037 PackageWriterImpl::_AttributeRemoved(Attribute* attribute) 1038 { 1039 AttributeValue& value = attribute->value; 1040 if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW 1041 && value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) { 1042 if (!fHeapRangesToRemove->AddRange(value.data.offset, value.data.size)) 1043 throw std::bad_alloc(); 1044 } 1045 1046 for (DoublyLinkedList<Attribute>::Iterator it 1047 = attribute->children.GetIterator(); 1048 Attribute* child = it.Next();) { 1049 _AttributeRemoved(child); 1050 } 1051 } 1052 1053 1054 status_t 1055 PackageWriterImpl::_Finish() 1056 { 1057 // write entries 1058 for (EntryList::ConstIterator it = fRootEntry->ChildIterator(); 1059 Entry* entry = it.Next();) { 1060 char pathBuffer[B_PATH_NAME_LENGTH]; 1061 pathBuffer[0] = '\0'; 1062 _AddEntry(AT_FDCWD, entry, entry->Name(), pathBuffer); 1063 } 1064 1065 hpkg_header header; 1066 1067 // write the TOC and package attributes 1068 uint64 tocLength; 1069 _WriteTOC(header, tocLength); 1070 1071 uint64 attributesLength; 1072 _WritePackageAttributes(header, attributesLength); 1073 1074 // flush the heap 1075 status_t error = fHeapWriter->Finish(); 1076 if (error != B_OK) 1077 return error; 1078 1079 uint64 compressedHeapSize = fHeapWriter->CompressedHeapSize(); 1080 1081 header.heap_compression = B_HOST_TO_BENDIAN_INT16(B_HPKG_COMPRESSION_ZLIB); 1082 header.heap_chunk_size = B_HOST_TO_BENDIAN_INT32(fHeapWriter->ChunkSize()); 1083 header.heap_size_compressed = B_HOST_TO_BENDIAN_INT64( 1084 fHeapWriter->CompressedHeapSize()); 1085 header.heap_size_uncompressed = B_HOST_TO_BENDIAN_INT64( 1086 fHeapWriter->UncompressedHeapSize()); 1087 1088 // Truncate the file to the size it is supposed to have. In update mode, it 1089 // can be greater when one or more files are shrunk. In creation mode it 1090 // should already have the correct size. 1091 off_t totalSize = fHeapWriter->HeapOffset() + (off_t)compressedHeapSize; 1092 if (ftruncate(FD(), totalSize) != 0) { 1093 fListener->PrintError("Failed to truncate package file to new " 1094 "size: %s\n", strerror(errno)); 1095 return errno; 1096 } 1097 1098 fListener->OnPackageSizeInfo(fHeaderSize, compressedHeapSize, tocLength, 1099 attributesLength, totalSize); 1100 1101 // prepare the header 1102 1103 // general 1104 header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_MAGIC); 1105 header.header_size = B_HOST_TO_BENDIAN_INT16(fHeaderSize); 1106 header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_VERSION); 1107 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize); 1108 header.minor_version = B_HOST_TO_BENDIAN_INT16(B_HPKG_MINOR_VERSION); 1109 1110 // write the header 1111 RawWriteBuffer(&header, sizeof(hpkg_header), 0); 1112 1113 SetFinished(true); 1114 return B_OK; 1115 } 1116 1117 1118 status_t 1119 PackageWriterImpl::_RegisterEntry(const char* fileName, int fd) 1120 { 1121 if (*fileName == '\0') { 1122 fListener->PrintError("Invalid empty file name\n"); 1123 return B_BAD_VALUE; 1124 } 1125 1126 // add all components of the path 1127 Entry* entry = fRootEntry; 1128 while (*fileName != 0) { 1129 const char* nextSlash = strchr(fileName, '/'); 1130 // no slash, just add the file name 1131 if (nextSlash == NULL) { 1132 entry = _RegisterEntry(entry, fileName, strlen(fileName), fd, 1133 false); 1134 break; 1135 } 1136 1137 // find the start of the next component, skipping slashes 1138 const char* nextComponent = nextSlash + 1; 1139 while (*nextComponent == '/') 1140 nextComponent++; 1141 1142 bool lastComponent = *nextComponent != '\0'; 1143 1144 if (nextSlash == fileName) { 1145 // the FS root 1146 entry = _RegisterEntry(entry, fileName, 1, lastComponent ? fd : -1, 1147 lastComponent); 1148 } else { 1149 entry = _RegisterEntry(entry, fileName, nextSlash - fileName, 1150 lastComponent ? fd : -1, lastComponent); 1151 } 1152 1153 fileName = nextComponent; 1154 } 1155 1156 return B_OK; 1157 } 1158 1159 1160 PackageWriterImpl::Entry* 1161 PackageWriterImpl::_RegisterEntry(Entry* parent, const char* name, 1162 size_t nameLength, int fd, bool isImplicit) 1163 { 1164 // check the component name -- don't allow "." or ".." 1165 if (name[0] == '.' 1166 && (nameLength == 1 || (nameLength == 2 && name[1] == '.'))) { 1167 fListener->PrintError("Invalid file name: \".\" and \"..\" " 1168 "are not allowed as path components\n"); 1169 throw status_t(B_BAD_VALUE); 1170 } 1171 1172 // the entry might already exist 1173 Entry* entry = parent->GetChild(name, nameLength); 1174 if (entry != NULL) { 1175 // If the entry was implicit and is no longer, we mark it non-implicit 1176 // and delete all of it's children. 1177 if (entry->IsImplicit() && !isImplicit) { 1178 entry->DeleteChildren(); 1179 entry->SetImplicit(false); 1180 entry->SetFD(fd); 1181 } 1182 } else { 1183 // nope -- create it 1184 entry = Entry::Create(name, nameLength, fd, isImplicit); 1185 parent->AddChild(entry); 1186 } 1187 1188 return entry; 1189 } 1190 1191 1192 void 1193 PackageWriterImpl::_WriteTOC(hpkg_header& header, uint64& _length) 1194 { 1195 // write the subsections 1196 uint64 startOffset = fHeapWriter->UncompressedHeapSize(); 1197 1198 // cached strings 1199 uint64 cachedStringsOffset = fHeapWriter->UncompressedHeapSize(); 1200 int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2); 1201 1202 // main TOC section 1203 uint64 mainOffset = fHeapWriter->UncompressedHeapSize(); 1204 _WriteAttributeChildren(fRootAttribute); 1205 1206 // notify the listener 1207 uint64 endOffset = fHeapWriter->UncompressedHeapSize(); 1208 uint64 stringsSize = mainOffset - cachedStringsOffset; 1209 uint64 mainSize = endOffset - mainOffset; 1210 uint64 tocSize = endOffset - startOffset; 1211 fListener->OnTOCSizeInfo(stringsSize, mainSize, tocSize); 1212 1213 // update the header 1214 header.toc_length = B_HOST_TO_BENDIAN_INT64(tocSize); 1215 header.toc_strings_length = B_HOST_TO_BENDIAN_INT64(stringsSize); 1216 header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten); 1217 1218 _length = tocSize; 1219 } 1220 1221 1222 void 1223 PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute) 1224 { 1225 DoublyLinkedList<Attribute>::Iterator it 1226 = attribute->children.GetIterator(); 1227 while (Attribute* child = it.Next()) { 1228 // write tag 1229 uint8 encoding = child->value.ApplicableEncoding(); 1230 WriteUnsignedLEB128(compose_attribute_tag(child->id, 1231 child->value.type, encoding, !child->children.IsEmpty())); 1232 1233 // write value 1234 WriteAttributeValue(child->value, encoding); 1235 1236 if (!child->children.IsEmpty()) 1237 _WriteAttributeChildren(child); 1238 } 1239 1240 WriteUnsignedLEB128(0); 1241 } 1242 1243 1244 void 1245 PackageWriterImpl::_WritePackageAttributes(hpkg_header& header, uint64& _length) 1246 { 1247 // write cached strings and package attributes tree 1248 off_t startOffset = fHeapWriter->UncompressedHeapSize(); 1249 1250 uint32 stringsLength; 1251 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 1252 stringsLength); 1253 1254 // notify listener 1255 uint32 attributesLength = fHeapWriter->UncompressedHeapSize() - startOffset; 1256 fListener->OnPackageAttributesSizeInfo(stringsCount, attributesLength); 1257 1258 // update the header 1259 header.attributes_length = B_HOST_TO_BENDIAN_INT32(attributesLength); 1260 header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount); 1261 header.attributes_strings_length = B_HOST_TO_BENDIAN_INT32(stringsLength); 1262 1263 _length = attributesLength; 1264 } 1265 1266 1267 void 1268 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName, 1269 char* pathBuffer) 1270 { 1271 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 1272 1273 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 1274 if (!isImplicitEntry) 1275 fListener->OnEntryAdded(pathBuffer); 1276 1277 // open the node 1278 int fd; 1279 FileDescriptorCloser fdCloser; 1280 1281 if (entry != NULL && entry->FD() >= 0) { 1282 // a file descriptor is already given -- use that 1283 fd = entry->FD(); 1284 } else { 1285 fd = openat(dirFD, fileName, 1286 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); 1287 if (fd < 0) { 1288 fListener->PrintError("Failed to open entry \"%s\": %s\n", 1289 pathBuffer, strerror(errno)); 1290 throw status_t(errno); 1291 } 1292 fdCloser.SetTo(fd); 1293 } 1294 1295 // stat the node 1296 struct stat st; 1297 if (fstat(fd, &st) < 0) { 1298 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer, 1299 strerror(errno)); 1300 throw status_t(errno); 1301 } 1302 1303 // implicit entries must be directories 1304 if (isImplicitEntry && !S_ISDIR(st.st_mode)) { 1305 fListener->PrintError("Non-leaf path component \"%s\" is not a " 1306 "directory\n", pathBuffer); 1307 throw status_t(B_BAD_VALUE); 1308 } 1309 1310 // In update mode we don't need to add an entry attribute for an implicit 1311 // directory, if there already is one. 1312 Attribute* entryAttribute = NULL; 1313 if (S_ISDIR(st.st_mode) && (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) { 1314 entryAttribute = fTopAttribute->FindEntryChild(fileName); 1315 if (entryAttribute != NULL && isImplicitEntry) { 1316 Stacker<Attribute> entryAttributeStacker(fTopAttribute, 1317 entryAttribute); 1318 _AddDirectoryChildren(entry, fd, pathBuffer); 1319 return; 1320 } 1321 } 1322 1323 // check/translate the node type 1324 uint8 fileType; 1325 uint32 defaultPermissions; 1326 if (S_ISREG(st.st_mode)) { 1327 fileType = B_HPKG_FILE_TYPE_FILE; 1328 defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS; 1329 } else if (S_ISLNK(st.st_mode)) { 1330 fileType = B_HPKG_FILE_TYPE_SYMLINK; 1331 defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS; 1332 } else if (S_ISDIR(st.st_mode)) { 1333 fileType = B_HPKG_FILE_TYPE_DIRECTORY; 1334 defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS; 1335 } else { 1336 // unsupported node type 1337 fListener->PrintError("Unsupported node type, entry: \"%s\"\n", 1338 pathBuffer); 1339 throw status_t(B_UNSUPPORTED); 1340 } 1341 1342 // add attribute entry, if it doesn't already exist (update mode, directory) 1343 bool isNewEntry = entryAttribute == NULL; 1344 if (entryAttribute == NULL) { 1345 entryAttribute = _AddStringAttribute( 1346 B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName); 1347 } 1348 1349 Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute); 1350 1351 if (isNewEntry) { 1352 // add stat data 1353 if (fileType != B_HPKG_DEFAULT_FILE_TYPE) 1354 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType); 1355 if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) { 1356 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS, 1357 uint32(st.st_mode & ALLPERMS)); 1358 } 1359 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime)); 1360 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime)); 1361 #ifdef __HAIKU__ 1362 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime)); 1363 #else 1364 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime)); 1365 #endif 1366 // TODO: File user/group! 1367 1368 // add file data/symlink path 1369 if (S_ISREG(st.st_mode)) { 1370 // regular file -- add data 1371 if (st.st_size > 0) { 1372 BFDDataReader dataReader(fd); 1373 status_t error = _AddData(dataReader, st.st_size); 1374 if (error != B_OK) 1375 throw status_t(error); 1376 } 1377 } else if (S_ISLNK(st.st_mode)) { 1378 // symlink -- add link address 1379 char path[B_PATH_NAME_LENGTH + 1]; 1380 ssize_t bytesRead = readlinkat(dirFD, fileName, path, 1381 B_PATH_NAME_LENGTH); 1382 if (bytesRead < 0) { 1383 fListener->PrintError("Failed to read symlink \"%s\": %s\n", 1384 pathBuffer, strerror(errno)); 1385 throw status_t(errno); 1386 } 1387 1388 path[bytesRead] = '\0'; 1389 _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path); 1390 } 1391 } 1392 1393 // add attributes 1394 if (DIR* attrDir = fs_fopen_attr_dir(fd)) { 1395 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir); 1396 1397 while (dirent* entry = fs_read_attr_dir(attrDir)) { 1398 attr_info attrInfo; 1399 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { 1400 fListener->PrintError( 1401 "Failed to stat attribute \"%s\" of file \"%s\": %s\n", 1402 entry->d_name, pathBuffer, strerror(errno)); 1403 throw status_t(errno); 1404 } 1405 1406 // create attribute entry 1407 Attribute* attributeAttribute = _AddStringAttribute( 1408 B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name); 1409 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, 1410 attributeAttribute); 1411 1412 // add type 1413 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE, 1414 (uint32)attrInfo.type); 1415 1416 // add data 1417 BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type); 1418 status_t error = _AddData(dataReader, attrInfo.size); 1419 if (error != B_OK) 1420 throw status_t(error); 1421 } 1422 } 1423 1424 if (S_ISDIR(st.st_mode)) 1425 _AddDirectoryChildren(entry, fd, pathBuffer); 1426 } 1427 1428 1429 void 1430 PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer) 1431 { 1432 // directory -- recursively add children 1433 if (entry != NULL && entry->IsImplicit()) { 1434 // this is an implicit entry -- just add it's children 1435 for (EntryList::ConstIterator it = entry->ChildIterator(); 1436 Entry* child = it.Next();) { 1437 _AddEntry(fd, child, child->Name(), pathBuffer); 1438 } 1439 } else { 1440 // we need to clone the directory FD for fdopendir() 1441 int clonedFD = dup(fd); 1442 if (clonedFD < 0) { 1443 fListener->PrintError( 1444 "Failed to dup() directory FD: %s\n", strerror(errno)); 1445 throw status_t(errno); 1446 } 1447 1448 DIR* dir = fdopendir(clonedFD); 1449 if (dir == NULL) { 1450 fListener->PrintError( 1451 "Failed to open directory \"%s\": %s\n", pathBuffer, 1452 strerror(errno)); 1453 close(clonedFD); 1454 throw status_t(errno); 1455 } 1456 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 1457 1458 while (dirent* entry = readdir(dir)) { 1459 // skip "." and ".." 1460 if (strcmp(entry->d_name, ".") == 0 1461 || strcmp(entry->d_name, "..") == 0) { 1462 continue; 1463 } 1464 1465 _AddEntry(fd, NULL, entry->d_name, pathBuffer); 1466 } 1467 } 1468 } 1469 1470 1471 PackageWriterImpl::Attribute* 1472 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id, 1473 const AttributeValue& value) 1474 { 1475 Attribute* attribute = new Attribute(id); 1476 1477 attribute->value = value; 1478 fTopAttribute->AddChild(attribute); 1479 1480 return attribute; 1481 } 1482 1483 1484 PackageWriterImpl::Attribute* 1485 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID, 1486 const char* value) 1487 { 1488 AttributeValue attributeValue; 1489 attributeValue.SetTo(fStringCache.Get(value)); 1490 return _AddAttribute(attributeID, attributeValue); 1491 } 1492 1493 1494 PackageWriterImpl::Attribute* 1495 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1496 uint64 dataSize, uint64 dataOffset) 1497 { 1498 AttributeValue attributeValue; 1499 attributeValue.SetToData(dataSize, dataOffset); 1500 return _AddAttribute(attributeID, attributeValue); 1501 } 1502 1503 1504 PackageWriterImpl::Attribute* 1505 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1506 uint64 dataSize, const uint8* data) 1507 { 1508 AttributeValue attributeValue; 1509 attributeValue.SetToData(dataSize, data); 1510 return _AddAttribute(attributeID, attributeValue); 1511 } 1512 1513 1514 status_t 1515 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size) 1516 { 1517 // add short data inline 1518 if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) { 1519 uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE]; 1520 status_t error = dataReader.ReadData(0, buffer, size); 1521 if (error != B_OK) { 1522 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1523 return error; 1524 } 1525 1526 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer); 1527 return B_OK; 1528 } 1529 1530 // add data to heap 1531 uint64 dataOffset; 1532 status_t error = fHeapWriter->AddData(dataReader, size, dataOffset); 1533 if (error != B_OK) 1534 return error; 1535 1536 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, dataOffset); 1537 return B_OK; 1538 } 1539 1540 1541 } // namespace BPrivate 1542 1543 } // namespace BHPKG 1544 1545 } // namespace BPackageKit 1546