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