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/BlockBufferCacheNoLock.h> 30 31 #include <package/hpkg/PackageAttributeValue.h> 32 #include <package/hpkg/PackageContentHandler.h> 33 #include <package/hpkg/PackageData.h> 34 #include <package/hpkg/PackageDataReader.h> 35 36 #include <AutoDeleter.h> 37 #include <RangeArray.h> 38 39 #include <package/hpkg/HPKGDefsPrivate.h> 40 41 #include <package/hpkg/DataOutput.h> 42 #include <package/hpkg/DataReader.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, uint64 heapOffset) 161 : 162 fErrorOutput(errorOutput), 163 fStringCache(stringCache), 164 fRootAttribute(rootAttribute), 165 fHeapOffset(heapOffset), 166 fErrorOccurred(false) 167 { 168 } 169 170 static const char* AttributeNameForID(uint8 id) 171 { 172 return BLowLevelPackageContentHandler::AttributeNameForID(id); 173 } 174 175 virtual status_t HandleSectionStart(BHPKGPackageSectionID sectionID, 176 bool& _handleSection) 177 { 178 // we're only interested in the TOC 179 _handleSection = sectionID == B_HPKG_SECTION_PACKAGE_TOC; 180 return B_OK; 181 } 182 183 virtual status_t HandleSectionEnd(BHPKGPackageSectionID sectionID) 184 { 185 return B_OK; 186 } 187 188 virtual status_t HandleAttribute(BHPKGAttributeID attributeID, 189 const BPackageAttributeValue& value, void* parentToken, void*& _token) 190 { 191 if (fErrorOccurred) 192 return B_OK; 193 194 Attribute* parentAttribute = parentToken != NULL 195 ? (Attribute*)parentToken : fRootAttribute; 196 197 Attribute* attribute = new Attribute(attributeID); 198 parentAttribute->AddChild(attribute); 199 200 switch (value.type) { 201 case B_HPKG_ATTRIBUTE_TYPE_INT: 202 attribute->value.SetTo(value.signedInt); 203 break; 204 205 case B_HPKG_ATTRIBUTE_TYPE_UINT: 206 attribute->value.SetTo(value.unsignedInt); 207 break; 208 209 case B_HPKG_ATTRIBUTE_TYPE_STRING: 210 { 211 CachedString* string = fStringCache.Get(value.string); 212 if (string == NULL) 213 throw std::bad_alloc(); 214 attribute->value.SetTo(string); 215 break; 216 } 217 218 case B_HPKG_ATTRIBUTE_TYPE_RAW: 219 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) { 220 attribute->value.SetToData(value.data.size, 221 value.data.offset - fHeapOffset); 222 } else if (value.encoding 223 == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) { 224 attribute->value.SetToData(value.data.size, value.data.raw); 225 } else { 226 fErrorOutput->PrintError("Invalid attribute value encoding " 227 "%d (attribute %d)\n", value.encoding, attributeID); 228 return B_BAD_DATA; 229 } 230 break; 231 232 case B_HPKG_ATTRIBUTE_TYPE_INVALID: 233 default: 234 fErrorOutput->PrintError("Invalid attribute value type %d " 235 "(attribute %d)\n", value.type, attributeID); 236 return B_BAD_DATA; 237 } 238 239 _token = attribute; 240 return B_OK; 241 } 242 243 virtual status_t HandleAttributeDone(BHPKGAttributeID attributeID, 244 const BPackageAttributeValue& value, void* parentToken, void* token) 245 { 246 return B_OK; 247 } 248 249 virtual void HandleErrorOccurred() 250 { 251 fErrorOccurred = true; 252 } 253 254 private: 255 BErrorOutput* fErrorOutput; 256 StringCache& fStringCache; 257 Attribute* fRootAttribute; 258 uint64 fHeapOffset; 259 bool fErrorOccurred; 260 }; 261 262 263 // #pragma mark - Entry 264 265 266 struct PackageWriterImpl::Entry : DoublyLinkedListLinkImpl<Entry> { 267 Entry(char* name, size_t nameLength, int fd, bool isImplicit) 268 : 269 fName(name), 270 fNameLength(nameLength), 271 fFD(fd), 272 fIsImplicit(isImplicit) 273 { 274 } 275 276 ~Entry() 277 { 278 DeleteChildren(); 279 free(fName); 280 } 281 282 static Entry* Create(const char* name, size_t nameLength, int fd, 283 bool isImplicit) 284 { 285 char* clonedName = (char*)malloc(nameLength + 1); 286 if (clonedName == NULL) 287 throw std::bad_alloc(); 288 memcpy(clonedName, name, nameLength); 289 clonedName[nameLength] = '\0'; 290 291 Entry* entry = new(std::nothrow) Entry(clonedName, nameLength, fd, 292 isImplicit); 293 if (entry == NULL) { 294 free(clonedName); 295 throw std::bad_alloc(); 296 } 297 298 return entry; 299 } 300 301 const char* Name() const 302 { 303 return fName; 304 } 305 306 int FD() const 307 { 308 return fFD; 309 } 310 311 void SetFD(int fd) 312 { 313 fFD = fd; 314 } 315 316 bool IsImplicit() const 317 { 318 return fIsImplicit; 319 } 320 321 void SetImplicit(bool isImplicit) 322 { 323 fIsImplicit = isImplicit; 324 } 325 326 bool HasName(const char* name, size_t nameLength) 327 { 328 return nameLength == fNameLength 329 && strncmp(name, fName, nameLength) == 0; 330 } 331 332 void AddChild(Entry* child) 333 { 334 fChildren.Add(child); 335 } 336 337 void DeleteChildren() 338 { 339 while (Entry* child = fChildren.RemoveHead()) 340 delete child; 341 } 342 343 Entry* GetChild(const char* name, size_t nameLength) const 344 { 345 EntryList::ConstIterator it = fChildren.GetIterator(); 346 while (Entry* child = it.Next()) { 347 if (child->HasName(name, nameLength)) 348 return child; 349 } 350 351 return NULL; 352 } 353 354 EntryList::ConstIterator ChildIterator() const 355 { 356 return fChildren.GetIterator(); 357 } 358 359 private: 360 char* fName; 361 size_t fNameLength; 362 int fFD; 363 bool fIsImplicit; 364 EntryList fChildren; 365 }; 366 367 368 // #pragma mark - SubPathAdder 369 370 371 struct PackageWriterImpl::SubPathAdder { 372 SubPathAdder(BErrorOutput* errorOutput, char* pathBuffer, 373 const char* subPath) 374 : 375 fOriginalPathEnd(pathBuffer + strlen(pathBuffer)) 376 { 377 if (fOriginalPathEnd != pathBuffer) 378 strlcat(pathBuffer, "/", B_PATH_NAME_LENGTH); 379 380 if (strlcat(pathBuffer, subPath, B_PATH_NAME_LENGTH) 381 >= B_PATH_NAME_LENGTH) { 382 *fOriginalPathEnd = '\0'; 383 errorOutput->PrintError("Path too long: \"%s/%s\"\n", pathBuffer, 384 subPath); 385 throw status_t(B_BUFFER_OVERFLOW); 386 } 387 } 388 389 ~SubPathAdder() 390 { 391 *fOriginalPathEnd = '\0'; 392 } 393 394 private: 395 char* fOriginalPathEnd; 396 }; 397 398 399 struct PackageWriterImpl::HeapAttributeOffsetter { 400 HeapAttributeOffsetter(const RangeArray<off_t>& ranges, 401 const Array<off_t>& deltas) 402 : 403 fRanges(ranges), 404 fDeltas(deltas) 405 { 406 } 407 408 void ProcessAttribute(Attribute* attribute) 409 { 410 // If the attribute refers to a heap value, adjust it 411 AttributeValue& value = attribute->value; 412 413 if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW 414 && value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) { 415 off_t delta = fDeltas[fRanges.InsertionIndex(value.data.offset)]; 416 value.data.offset -= delta; 417 } 418 419 // recurse 420 for (DoublyLinkedList<Attribute>::Iterator it 421 = attribute->children.GetIterator(); 422 Attribute* child = it.Next();) { 423 ProcessAttribute(child); 424 } 425 } 426 427 private: 428 const RangeArray<off_t>& fRanges; 429 const Array<off_t>& fDeltas; 430 }; 431 432 433 // #pragma mark - PackageWriterImpl (Inline Methods) 434 435 436 template<typename Type> 437 inline PackageWriterImpl::Attribute* 438 PackageWriterImpl::_AddAttribute(BHPKGAttributeID attributeID, Type value) 439 { 440 AttributeValue attributeValue; 441 attributeValue.SetTo(value); 442 return _AddAttribute(attributeID, attributeValue); 443 } 444 445 446 // #pragma mark - PackageWriterImpl 447 448 449 PackageWriterImpl::PackageWriterImpl(BPackageWriterListener* listener) 450 : 451 inherited(listener), 452 fListener(listener), 453 fHeapRangesToRemove(NULL), 454 fDataBuffer(NULL), 455 fDataBufferSize(2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB), 456 fRootEntry(NULL), 457 fRootAttribute(NULL), 458 fTopAttribute(NULL), 459 fCheckLicenses(true) 460 { 461 } 462 463 464 PackageWriterImpl::~PackageWriterImpl() 465 { 466 delete fRootAttribute; 467 468 delete fRootEntry; 469 470 free(fDataBuffer); 471 } 472 473 474 status_t 475 PackageWriterImpl::Init(const char* fileName, uint32 flags) 476 { 477 try { 478 return _Init(fileName, flags); 479 } catch (status_t error) { 480 return error; 481 } catch (std::bad_alloc) { 482 fListener->PrintError("Out of memory!\n"); 483 return B_NO_MEMORY; 484 } 485 } 486 487 488 void 489 PackageWriterImpl::SetCheckLicenses(bool checkLicenses) 490 { 491 fCheckLicenses = checkLicenses; 492 } 493 494 495 status_t 496 PackageWriterImpl::AddEntry(const char* fileName, int fd) 497 { 498 try { 499 // if it's ".PackageInfo", parse it 500 if (strcmp(fileName, B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) { 501 struct ErrorListener : public BPackageInfo::ParseErrorListener { 502 ErrorListener(BPackageWriterListener* _listener) 503 : listener(_listener) {} 504 virtual void OnError(const BString& msg, int line, int col) { 505 listener->PrintError("Parse error in %s(%d:%d) -> %s\n", 506 B_HPKG_PACKAGE_INFO_FILE_NAME, line, col, msg.String()); 507 } 508 BPackageWriterListener* listener; 509 } errorListener(fListener); 510 511 if (fd >= 0) { 512 // a file descriptor is given -- read the config from there 513 // stat the file to get the file size 514 struct stat st; 515 if (fstat(fd, &st) != 0) 516 return errno; 517 518 BString packageInfoString; 519 char* buffer = packageInfoString.LockBuffer(st.st_size); 520 if (buffer == NULL) 521 return B_NO_MEMORY; 522 523 ssize_t result = read_pos(fd, 0, buffer, st.st_size); 524 if (result < 0) { 525 packageInfoString.UnlockBuffer(0); 526 return errno; 527 } 528 529 buffer[st.st_size] = '\0'; 530 packageInfoString.UnlockBuffer(st.st_size); 531 532 result = fPackageInfo.ReadFromConfigString(packageInfoString, 533 &errorListener); 534 if (result != B_OK) 535 return result; 536 } else { 537 // use the file name 538 BEntry packageInfoEntry(fileName); 539 status_t result = fPackageInfo.ReadFromConfigFile( 540 packageInfoEntry, &errorListener); 541 if (result != B_OK 542 || (result = fPackageInfo.InitCheck()) != B_OK) { 543 return result; 544 } 545 } 546 } 547 548 return _RegisterEntry(fileName, fd); 549 } catch (status_t error) { 550 return error; 551 } catch (std::bad_alloc) { 552 fListener->PrintError("Out of memory!\n"); 553 return B_NO_MEMORY; 554 } 555 } 556 557 558 status_t 559 PackageWriterImpl::Finish() 560 { 561 try { 562 RangeArray<off_t> heapRangesToRemove; 563 fHeapRangesToRemove = &heapRangesToRemove; 564 565 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) { 566 _UpdateCheckEntryCollisions(); 567 568 if (fPackageInfo.InitCheck() != B_OK) 569 _UpdateReadPackageInfo(); 570 } 571 572 if (fPackageInfo.InitCheck() != B_OK) { 573 fListener->PrintError("No package-info file found (%s)!\n", 574 B_HPKG_PACKAGE_INFO_FILE_NAME); 575 return B_BAD_DATA; 576 } 577 578 RegisterPackageInfo(PackageAttributes(), fPackageInfo); 579 580 if (fCheckLicenses) { 581 status_t result = _CheckLicenses(); 582 if (result != B_OK) 583 return result; 584 } 585 586 if ((Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) 587 _CompactHeap(); 588 589 fHeapRangesToRemove = NULL; 590 591 return _Finish(); 592 } catch (status_t error) { 593 return error; 594 } catch (std::bad_alloc) { 595 fListener->PrintError("Out of memory!\n"); 596 return B_NO_MEMORY; 597 } 598 } 599 600 601 status_t 602 PackageWriterImpl::_Init(const char* fileName, uint32 flags) 603 { 604 status_t result = inherited::Init(fileName, "package", flags); 605 if (result != B_OK) 606 return result; 607 608 // allocate data buffer 609 fDataBuffer = malloc(fDataBufferSize); 610 if (fDataBuffer == NULL) 611 throw std::bad_alloc(); 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 = fHeapEnd = 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 fHeapEnd = fHeapOffset + packageReader.HeapSize(); 633 634 PackageContentHandler handler(fRootAttribute, fListener, fStringCache, 635 fHeapOffset); 636 637 result = packageReader.ParseContent(&handler); 638 if (result != B_OK) 639 return result; 640 641 if ((uint64)fHeapOffset > packageReader.HeapOffset()) { 642 fListener->PrintError("Unexpected heap offset in package file.\n"); 643 return B_BAD_DATA; 644 } 645 } 646 647 return B_OK; 648 } 649 650 651 status_t 652 PackageWriterImpl::_CheckLicenses() 653 { 654 BPath systemLicensePath; 655 status_t result 656 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 657 = find_directory(B_SYSTEM_DATA_DIRECTORY, &systemLicensePath); 658 #else 659 = systemLicensePath.SetTo(HAIKU_BUILD_SYSTEM_DATA_DIRECTORY); 660 #endif 661 if (result != B_OK) { 662 fListener->PrintError("unable to find system data path!\n"); 663 return result; 664 } 665 if ((result = systemLicensePath.Append("licenses")) != B_OK) { 666 fListener->PrintError("unable to append to system data path!\n"); 667 return result; 668 } 669 670 BDirectory systemLicenseDir(systemLicensePath.Path()); 671 672 const BObjectList<BString>& licenseList = fPackageInfo.LicenseList(); 673 for (int i = 0; i < licenseList.CountItems(); ++i) { 674 const BString& licenseName = *licenseList.ItemAt(i); 675 if (licenseName == kPublicDomainLicenseName) 676 continue; 677 678 BEntry license; 679 if (systemLicenseDir.FindEntry(licenseName.String(), &license) == B_OK) 680 continue; 681 682 // license is not a system license, so it must be contained in package 683 BString licensePath("data/licenses/"); 684 licensePath << licenseName; 685 686 if (!_IsEntryInPackage(licensePath)) { 687 fListener->PrintError("License '%s' isn't contained in package!\n", 688 licenseName.String()); 689 return B_BAD_DATA; 690 } 691 } 692 693 return B_OK; 694 } 695 696 697 bool 698 PackageWriterImpl::_IsEntryInPackage(const char* fileName) 699 { 700 const char* originalFileName = fileName; 701 702 // find the closest ancestor of the entry that is in the added entries 703 bool added = false; 704 Entry* entry = fRootEntry; 705 while (entry != NULL) { 706 if (!entry->IsImplicit()) { 707 added = true; 708 break; 709 } 710 711 if (*fileName == '\0') 712 break; 713 714 const char* nextSlash = strchr(fileName, '/'); 715 716 if (nextSlash == NULL) { 717 // no slash, just the file name 718 size_t length = strlen(fileName); 719 entry = entry->GetChild(fileName, length); 720 fileName += length; 721 continue; 722 } 723 724 // find the start of the next component, skipping slashes 725 const char* nextComponent = nextSlash + 1; 726 while (*nextComponent == '/') 727 nextComponent++; 728 729 entry = entry->GetChild(fileName, nextSlash - fileName); 730 731 fileName = nextComponent; 732 } 733 734 if (added) { 735 // the entry itself or one of its ancestors has been added to the 736 // package explicitly -- stat it, to see, if it exists 737 struct stat st; 738 if (entry->FD() >= 0) { 739 if (fstatat(entry->FD(), *fileName != '\0' ? fileName : NULL, &st, 740 AT_SYMLINK_NOFOLLOW) == 0) { 741 return true; 742 } 743 } else { 744 if (lstat(originalFileName, &st) == 0) 745 return true; 746 } 747 } 748 749 // In update mode the entry might already be in the package. 750 Attribute* attribute = fRootAttribute; 751 fileName = originalFileName; 752 753 while (attribute != NULL) { 754 if (*fileName == '\0') 755 return true; 756 757 const char* nextSlash = strchr(fileName, '/'); 758 759 if (nextSlash == NULL) { 760 // no slash, just the file name 761 return attribute->FindEntryChild(fileName) != NULL; 762 } 763 764 // find the start of the next component, skipping slashes 765 const char* nextComponent = nextSlash + 1; 766 while (*nextComponent == '/') 767 nextComponent++; 768 769 attribute = attribute->FindEntryChild(fileName, nextSlash - fileName); 770 771 fileName = nextComponent; 772 } 773 774 return false; 775 } 776 777 778 void 779 PackageWriterImpl::_UpdateReadPackageInfo() 780 { 781 // get the .PackageInfo entry attribute 782 Attribute* attribute = fRootAttribute->FindEntryChild( 783 B_HPKG_PACKAGE_INFO_FILE_NAME); 784 if (attribute == NULL) { 785 fListener->PrintError("No %s in package file.\n", 786 B_HPKG_PACKAGE_INFO_FILE_NAME); 787 throw status_t(B_BAD_DATA); 788 } 789 790 // get the data attribute 791 Attribute* dataAttribute = attribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_DATA); 792 if (dataAttribute == NULL) { 793 fListener->PrintError("%s entry in package file doesn't have data.\n", 794 B_HPKG_PACKAGE_INFO_FILE_NAME); 795 throw status_t(B_BAD_DATA); 796 } 797 798 AttributeValue& value = dataAttribute->value; 799 if (value.type != B_HPKG_ATTRIBUTE_TYPE_RAW) { 800 fListener->PrintError("%s entry in package file has an invalid data " 801 "attribute (not of type raw).\n", B_HPKG_PACKAGE_INFO_FILE_NAME); 802 throw status_t(B_BAD_DATA); 803 } 804 805 BPackageData data; 806 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) 807 data.SetData(value.data.size, value.data.raw); 808 else 809 data.SetData(value.data.size, value.data.offset + fHeapOffset); 810 811 // get the compression 812 uint8 compression = B_HPKG_DEFAULT_DATA_COMPRESSION; 813 if (Attribute* compressionAttribute = dataAttribute->ChildWithID( 814 B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION)) { 815 if (compressionAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) { 816 fListener->PrintError("%s entry in package file has an invalid " 817 "data compression attribute (not of type uint).\n", 818 B_HPKG_PACKAGE_INFO_FILE_NAME); 819 throw status_t(B_BAD_DATA); 820 } 821 compression = compressionAttribute->value.unsignedInt; 822 } 823 824 data.SetCompression(compression); 825 826 // get the size 827 uint64 size; 828 Attribute* sizeAttribute = dataAttribute->ChildWithID( 829 B_HPKG_ATTRIBUTE_ID_DATA_SIZE); 830 if (sizeAttribute == NULL) { 831 size = value.data.size; 832 } else if (sizeAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) { 833 fListener->PrintError("%s entry in package file has an invalid data " 834 "size attribute (not of type uint).\n", 835 B_HPKG_PACKAGE_INFO_FILE_NAME); 836 throw status_t(B_BAD_DATA); 837 } else 838 size = sizeAttribute->value.unsignedInt; 839 840 data.SetUncompressedSize(size); 841 842 // get the chunk size 843 uint64 chunkSize = compression == B_HPKG_COMPRESSION_ZLIB 844 ? B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB : 0; 845 if (Attribute* chunkSizeAttribute = dataAttribute->ChildWithID( 846 B_HPKG_ATTRIBUTE_ID_DATA_CHUNK_SIZE)) { 847 if (chunkSizeAttribute->value.type != B_HPKG_ATTRIBUTE_TYPE_UINT) { 848 fListener->PrintError("%s entry in package file has an invalid " 849 "data chunk size attribute (not of type uint).\n", 850 B_HPKG_PACKAGE_INFO_FILE_NAME); 851 throw status_t(B_BAD_DATA); 852 } 853 chunkSize = chunkSizeAttribute->value.unsignedInt; 854 } 855 856 data.SetChunkSize(chunkSize); 857 858 // read the value into a string 859 BString valueString; 860 char* valueBuffer = valueString.LockBuffer(size); 861 if (valueBuffer == NULL) 862 throw std::bad_alloc(); 863 864 if (value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_INLINE) { 865 // data encoded inline -- just copy to buffer 866 if (size != value.data.size) { 867 fListener->PrintError("%s entry in package file has an invalid " 868 "data attribute (mismatching size).\n", 869 B_HPKG_PACKAGE_INFO_FILE_NAME); 870 throw status_t(B_BAD_DATA); 871 } 872 memcpy(valueBuffer, value.data.raw, value.data.size); 873 } else { 874 // data on heap -- read from there 875 BBlockBufferCacheNoLock bufferCache(16 * 1024, 1); 876 status_t error = bufferCache.Init(); 877 if (error != B_OK) { 878 fListener->PrintError("Failed to initialize buffer cache: %s\n", 879 strerror(error)); 880 throw status_t(error); 881 } 882 883 // create a BPackageDataReader 884 BFDDataReader packageFileReader(FD()); 885 BPackageDataReader* reader; 886 error = BPackageDataReaderFactory(&bufferCache) 887 .CreatePackageDataReader(&packageFileReader, data, reader); 888 if (error != B_OK) { 889 fListener->PrintError("Failed to create package data reader: %s\n", 890 strerror(error)); 891 throw status_t(error); 892 } 893 ObjectDeleter<BPackageDataReader> readerDeleter(reader); 894 895 // read the data 896 error = reader->ReadData(0, valueBuffer, size); 897 if (error != B_OK) { 898 fListener->PrintError("Failed to read data of %s entry in package " 899 "file: %s\n", B_HPKG_PACKAGE_INFO_FILE_NAME, strerror(error)); 900 throw status_t(error); 901 } 902 } 903 904 valueString.UnlockBuffer(); 905 906 // parse the package info 907 status_t error = fPackageInfo.ReadFromConfigString(valueString); 908 if (error != B_OK) { 909 fListener->PrintError("Failed to parse package info data from package " 910 "file: %s\n", strerror(error)); 911 throw status_t(error); 912 } 913 } 914 915 916 void 917 PackageWriterImpl::_UpdateCheckEntryCollisions() 918 { 919 for (EntryList::ConstIterator it = fRootEntry->ChildIterator(); 920 Entry* entry = it.Next();) { 921 char pathBuffer[B_PATH_NAME_LENGTH]; 922 pathBuffer[0] = '\0'; 923 _UpdateCheckEntryCollisions(fRootAttribute, AT_FDCWD, entry, 924 entry->Name(), pathBuffer); 925 } 926 } 927 928 929 void 930 PackageWriterImpl::_UpdateCheckEntryCollisions(Attribute* parentAttribute, 931 int dirFD, Entry* entry, const char* fileName, char* pathBuffer) 932 { 933 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 934 935 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 936 937 // Check wether there's an entry attribute for this entry. If not, we can 938 // ignore this entry. 939 Attribute* entryAttribute = parentAttribute->FindEntryChild(fileName); 940 if (entryAttribute == NULL) 941 return; 942 943 // open the node 944 int fd; 945 FileDescriptorCloser fdCloser; 946 947 if (entry != NULL && entry->FD() >= 0) { 948 // a file descriptor is already given -- use that 949 fd = entry->FD(); 950 } else { 951 fd = openat(dirFD, fileName, 952 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); 953 if (fd < 0) { 954 fListener->PrintError("Failed to open entry \"%s\": %s\n", 955 pathBuffer, strerror(errno)); 956 throw status_t(errno); 957 } 958 fdCloser.SetTo(fd); 959 } 960 961 // stat the node 962 struct stat st; 963 if (fstat(fd, &st) < 0) { 964 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer, 965 strerror(errno)); 966 throw status_t(errno); 967 } 968 969 // implicit entries must be directories 970 if (isImplicitEntry && !S_ISDIR(st.st_mode)) { 971 fListener->PrintError("Non-leaf path component \"%s\" is not a " 972 "directory.\n", pathBuffer); 973 throw status_t(B_BAD_VALUE); 974 } 975 976 // get the pre-existing node's file type 977 uint32 preExistingFileType = B_HPKG_DEFAULT_FILE_TYPE; 978 if (Attribute* fileTypeAttribute 979 = entryAttribute->ChildWithID(B_HPKG_ATTRIBUTE_ID_FILE_TYPE)) { 980 if (fileTypeAttribute->value.type == B_HPKG_ATTRIBUTE_TYPE_UINT) 981 preExistingFileType = fileTypeAttribute->value.unsignedInt; 982 } 983 984 // Compare the node type with that of the pre-existing one. 985 if (!S_ISDIR(st.st_mode)) { 986 // the pre-existing must not a directory either -- we'll remove it 987 if (preExistingFileType == B_HPKG_FILE_TYPE_DIRECTORY) { 988 fListener->PrintError("Specified file \"%s\" clashes with an " 989 "archived directory.\n", pathBuffer); 990 throw status_t(B_BAD_VALUE); 991 } 992 993 if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) { 994 fListener->PrintError("Specified file \"%s\" clashes with an " 995 "archived file.\n", pathBuffer); 996 throw status_t(B_FILE_EXISTS); 997 } 998 999 parentAttribute->RemoveChild(entryAttribute); 1000 _AttributeRemoved(entryAttribute); 1001 1002 return; 1003 } 1004 1005 // the pre-existing entry needs to be a directory too -- we will merge 1006 if (preExistingFileType != B_HPKG_FILE_TYPE_DIRECTORY) { 1007 fListener->PrintError("Specified directory \"%s\" clashes with an " 1008 "archived non-directory.\n", pathBuffer); 1009 throw status_t(B_BAD_VALUE); 1010 } 1011 1012 // directory -- recursively add children 1013 if (isImplicitEntry) { 1014 // this is an implicit entry -- just check the child entries 1015 for (EntryList::ConstIterator it = entry->ChildIterator(); 1016 Entry* child = it.Next();) { 1017 _UpdateCheckEntryCollisions(entryAttribute, fd, child, 1018 child->Name(), pathBuffer); 1019 } 1020 } else { 1021 // explicitly specified directory -- we need to read the directory 1022 1023 // first we check for colliding node attributes, though 1024 if (DIR* attrDir = fs_fopen_attr_dir(fd)) { 1025 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir); 1026 1027 while (dirent* entry = fs_read_attr_dir(attrDir)) { 1028 attr_info attrInfo; 1029 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { 1030 fListener->PrintError( 1031 "Failed to stat attribute \"%s\" of directory \"%s\": " 1032 "%s\n", entry->d_name, pathBuffer, strerror(errno)); 1033 throw status_t(errno); 1034 } 1035 1036 // check whether the attribute exists 1037 Attribute* attributeAttribute 1038 = entryAttribute->FindNodeAttributeChild(entry->d_name); 1039 if (attributeAttribute == NULL) 1040 continue; 1041 1042 if ((Flags() & B_HPKG_WRITER_FORCE_ADD) == 0) { 1043 fListener->PrintError("Attribute \"%s\" of specified " 1044 "directory \"%s\" clashes with an archived " 1045 "attribute.\n", pathBuffer); 1046 throw status_t(B_FILE_EXISTS); 1047 } 1048 1049 // remove it 1050 entryAttribute->RemoveChild(attributeAttribute); 1051 _AttributeRemoved(attributeAttribute); 1052 } 1053 } 1054 1055 // we need to clone the directory FD for fdopendir() 1056 int clonedFD = dup(fd); 1057 if (clonedFD < 0) { 1058 fListener->PrintError( 1059 "Failed to dup() directory FD: %s\n", strerror(errno)); 1060 throw status_t(errno); 1061 } 1062 1063 DIR* dir = fdopendir(clonedFD); 1064 if (dir == NULL) { 1065 fListener->PrintError( 1066 "Failed to open directory \"%s\": %s\n", pathBuffer, 1067 strerror(errno)); 1068 close(clonedFD); 1069 throw status_t(errno); 1070 } 1071 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 1072 1073 while (dirent* entry = readdir(dir)) { 1074 // skip "." and ".." 1075 if (strcmp(entry->d_name, ".") == 0 1076 || strcmp(entry->d_name, "..") == 0) { 1077 continue; 1078 } 1079 1080 _UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name, 1081 pathBuffer); 1082 } 1083 } 1084 } 1085 1086 1087 void 1088 PackageWriterImpl::_CompactHeap() 1089 { 1090 int32 count = fHeapRangesToRemove->CountRanges(); 1091 if (count == 0) 1092 return; 1093 1094 // compute the move deltas for the ranges 1095 Array<off_t> deltas; 1096 off_t delta = 0; 1097 for (int32 i = 0; i < count; i++) { 1098 if (!deltas.Add(delta)) 1099 throw std::bad_alloc(); 1100 1101 delta += fHeapRangesToRemove->RangeAt(i).size; 1102 } 1103 1104 if (!deltas.Add(delta)) 1105 throw std::bad_alloc(); 1106 1107 // offset the attributes 1108 HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute( 1109 fRootAttribute); 1110 1111 // move the heap chunks in the file around 1112 off_t chunkOffset = fHeapOffset; 1113 delta = 0; 1114 1115 for (int32 i = 0; i < count; i++) { 1116 const Range<off_t>& range = fHeapRangesToRemove->RangeAt(i); 1117 1118 if (delta > 0 && chunkOffset < range.offset) { 1119 // move chunk 1120 _MoveHeapChunk(chunkOffset, chunkOffset - delta, 1121 range.offset - chunkOffset); 1122 } 1123 1124 chunkOffset = range.EndOffset(); 1125 delta += range.size; 1126 } 1127 1128 // move the final chunk 1129 off_t heapSize = fHeapEnd - fHeapOffset; 1130 if (delta > 0 && chunkOffset < heapSize) { 1131 _MoveHeapChunk(chunkOffset, chunkOffset - delta, 1132 heapSize - chunkOffset); 1133 } 1134 1135 fHeapEnd -= delta; 1136 } 1137 1138 1139 void 1140 PackageWriterImpl::_MoveHeapChunk(off_t fromOffset, off_t toOffset, off_t size) 1141 { 1142 // convert heap offsets to file offsets 1143 fromOffset += fHeapOffset; 1144 toOffset += fHeapOffset; 1145 1146 while (size > 0) { 1147 size_t toCopy = std::min(size, (off_t)fDataBufferSize); 1148 1149 // read data into buffer 1150 ssize_t bytesRead = read_pos(FD(), fromOffset, fDataBuffer, toCopy); 1151 if (bytesRead < 0) { 1152 fListener->PrintError("Failed to read from package file: %s\n", 1153 strerror(errno)); 1154 throw status_t(errno); 1155 } 1156 if ((size_t)bytesRead < toCopy) { 1157 fListener->PrintError("Failed to read from package file (wanted " 1158 "%zu bytes, got %zd).\n", toCopy, bytesRead); 1159 throw status_t(B_IO_ERROR); 1160 } 1161 1162 // write data to target offset 1163 ssize_t bytesWritten = write_pos(FD(), toOffset, fDataBuffer, toCopy); 1164 if (bytesWritten < 0) { 1165 fListener->PrintError("Failed to write to package file: %s\n", 1166 strerror(errno)); 1167 throw status_t(errno); 1168 } 1169 if ((size_t)bytesWritten < toCopy) { 1170 fListener->PrintError("Failed to write to package file.\n"); 1171 throw status_t(B_IO_ERROR); 1172 } 1173 1174 fromOffset += toCopy; 1175 toOffset += toCopy; 1176 size -= toCopy; 1177 } 1178 } 1179 1180 1181 void 1182 PackageWriterImpl::_AttributeRemoved(Attribute* attribute) 1183 { 1184 AttributeValue& value = attribute->value; 1185 if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW 1186 && value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) { 1187 if (!fHeapRangesToRemove->AddRange(value.data.offset, value.data.size)) 1188 throw std::bad_alloc(); 1189 } 1190 1191 for (DoublyLinkedList<Attribute>::Iterator it 1192 = attribute->children.GetIterator(); 1193 Attribute* child = it.Next();) { 1194 _AttributeRemoved(child); 1195 } 1196 } 1197 1198 1199 status_t 1200 PackageWriterImpl::_Finish() 1201 { 1202 // write entries 1203 for (EntryList::ConstIterator it = fRootEntry->ChildIterator(); 1204 Entry* entry = it.Next();) { 1205 char pathBuffer[B_PATH_NAME_LENGTH]; 1206 pathBuffer[0] = '\0'; 1207 _AddEntry(AT_FDCWD, entry, entry->Name(), pathBuffer); 1208 } 1209 1210 off_t heapSize = fHeapEnd - fHeapOffset; 1211 1212 hpkg_header header; 1213 1214 // write the TOC and package attributes 1215 _WriteTOC(header); 1216 _WritePackageAttributes(header); 1217 1218 off_t totalSize = fHeapEnd; 1219 1220 // Truncate the file to the size it is supposed to have. In update mode, it 1221 // can be greater when one or more files are shrunk. In creation mode, when 1222 // writing compressed TOC or package attributes yields a larger size than 1223 // uncompressed, the file size may also be greater than it should be. 1224 if (ftruncate(FD(), totalSize) != 0) { 1225 fListener->PrintError("Failed to truncate package file to new " 1226 "size: %s\n", strerror(errno)); 1227 return errno; 1228 } 1229 1230 fListener->OnPackageSizeInfo(fHeapOffset, heapSize, 1231 B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed), 1232 B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed), 1233 totalSize); 1234 1235 // prepare the header 1236 1237 // general 1238 header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_MAGIC); 1239 header.header_size = B_HOST_TO_BENDIAN_INT16((uint16)fHeapOffset); 1240 header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_VERSION); 1241 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize); 1242 1243 // write the header 1244 WriteBuffer(&header, sizeof(hpkg_header), 0); 1245 1246 SetFinished(true); 1247 return B_OK; 1248 } 1249 1250 1251 status_t 1252 PackageWriterImpl::_RegisterEntry(const char* fileName, int fd) 1253 { 1254 if (*fileName == '\0') { 1255 fListener->PrintError("Invalid empty file name\n"); 1256 return B_BAD_VALUE; 1257 } 1258 1259 // add all components of the path 1260 Entry* entry = fRootEntry; 1261 while (*fileName != 0) { 1262 const char* nextSlash = strchr(fileName, '/'); 1263 // no slash, just add the file name 1264 if (nextSlash == NULL) { 1265 entry = _RegisterEntry(entry, fileName, strlen(fileName), fd, 1266 false); 1267 break; 1268 } 1269 1270 // find the start of the next component, skipping slashes 1271 const char* nextComponent = nextSlash + 1; 1272 while (*nextComponent == '/') 1273 nextComponent++; 1274 1275 bool lastComponent = *nextComponent != '\0'; 1276 1277 if (nextSlash == fileName) { 1278 // the FS root 1279 entry = _RegisterEntry(entry, fileName, 1, lastComponent ? fd : -1, 1280 lastComponent); 1281 } else { 1282 entry = _RegisterEntry(entry, fileName, nextSlash - fileName, 1283 lastComponent ? fd : -1, lastComponent); 1284 } 1285 1286 fileName = nextComponent; 1287 } 1288 1289 return B_OK; 1290 } 1291 1292 1293 PackageWriterImpl::Entry* 1294 PackageWriterImpl::_RegisterEntry(Entry* parent, const char* name, 1295 size_t nameLength, int fd, bool isImplicit) 1296 { 1297 // check the component name -- don't allow "." or ".." 1298 if (name[0] == '.' 1299 && (nameLength == 1 || (nameLength == 2 && name[1] == '.'))) { 1300 fListener->PrintError("Invalid file name: \".\" and \"..\" " 1301 "are not allowed as path components\n"); 1302 throw status_t(B_BAD_VALUE); 1303 } 1304 1305 // the entry might already exist 1306 Entry* entry = parent->GetChild(name, nameLength); 1307 if (entry != NULL) { 1308 // If the entry was implicit and is no longer, we mark it non-implicit 1309 // and delete all of it's children. 1310 if (entry->IsImplicit() && !isImplicit) { 1311 entry->DeleteChildren(); 1312 entry->SetImplicit(false); 1313 entry->SetFD(fd); 1314 } 1315 } else { 1316 // nope -- create it 1317 entry = Entry::Create(name, nameLength, fd, isImplicit); 1318 parent->AddChild(entry); 1319 } 1320 1321 return entry; 1322 } 1323 1324 1325 void 1326 PackageWriterImpl::_WriteTOC(hpkg_header& header) 1327 { 1328 // prepare the writer (zlib writer on top of a file writer) 1329 off_t startOffset = fHeapEnd; 1330 1331 // write the sections 1332 uint32 compression = B_HPKG_COMPRESSION_ZLIB; 1333 uint64 uncompressedStringsSize; 1334 uint64 uncompressedMainSize; 1335 uint64 tocUncompressedSize; 1336 int32 cachedStringsWritten = _WriteTOCCompressed(uncompressedStringsSize, 1337 uncompressedMainSize, tocUncompressedSize); 1338 1339 off_t endOffset = fHeapEnd; 1340 1341 if (endOffset - startOffset >= (off_t)tocUncompressedSize) { 1342 // the compressed section isn't shorter -- write uncompressed 1343 fHeapEnd = startOffset; 1344 compression = B_HPKG_COMPRESSION_NONE; 1345 cachedStringsWritten = _WriteTOCUncompressed(uncompressedStringsSize, 1346 uncompressedMainSize, tocUncompressedSize); 1347 1348 endOffset = fHeapEnd; 1349 } 1350 1351 fListener->OnTOCSizeInfo(uncompressedStringsSize, uncompressedMainSize, 1352 tocUncompressedSize); 1353 1354 // update the header 1355 1356 // TOC 1357 header.toc_compression = B_HOST_TO_BENDIAN_INT32(compression); 1358 header.toc_length_compressed = B_HOST_TO_BENDIAN_INT64( 1359 endOffset - startOffset); 1360 header.toc_length_uncompressed = B_HOST_TO_BENDIAN_INT64( 1361 tocUncompressedSize); 1362 1363 // TOC subsections 1364 header.toc_strings_length = B_HOST_TO_BENDIAN_INT64( 1365 uncompressedStringsSize); 1366 header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten); 1367 } 1368 1369 1370 int32 1371 PackageWriterImpl::_WriteTOCCompressed(uint64& _uncompressedStringsSize, 1372 uint64& _uncompressedMainSize, uint64& _tocUncompressedSize) 1373 { 1374 FDDataWriter realWriter(FD(), fHeapEnd, fListener); 1375 ZlibDataWriter zlibWriter(&realWriter); 1376 SetDataWriter(&zlibWriter); 1377 zlibWriter.Init(); 1378 1379 // write the sections 1380 int32 cachedStringsWritten 1381 = _WriteTOCSections(_uncompressedStringsSize, _uncompressedMainSize); 1382 1383 // finish the writer 1384 zlibWriter.Finish(); 1385 fHeapEnd = realWriter.Offset(); 1386 SetDataWriter(NULL); 1387 1388 _tocUncompressedSize = zlibWriter.BytesWritten(); 1389 return cachedStringsWritten; 1390 } 1391 1392 1393 int32 1394 PackageWriterImpl::_WriteTOCUncompressed(uint64& _uncompressedStringsSize, 1395 uint64& _uncompressedMainSize, uint64& _tocUncompressedSize) 1396 { 1397 FDDataWriter realWriter(FD(), fHeapEnd, fListener); 1398 SetDataWriter(&realWriter); 1399 1400 // write the sections 1401 int32 cachedStringsWritten 1402 = _WriteTOCSections(_uncompressedStringsSize, _uncompressedMainSize); 1403 1404 fHeapEnd = realWriter.Offset(); 1405 SetDataWriter(NULL); 1406 1407 _tocUncompressedSize = realWriter.BytesWritten(); 1408 return cachedStringsWritten; 1409 } 1410 1411 1412 int32 1413 PackageWriterImpl::_WriteTOCSections(uint64& _stringsSize, uint64& _mainSize) 1414 { 1415 // write the cached strings 1416 uint64 cachedStringsOffset = DataWriter()->BytesWritten(); 1417 int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2); 1418 1419 // write the main TOC section 1420 uint64 mainOffset = DataWriter()->BytesWritten(); 1421 _WriteAttributeChildren(fRootAttribute); 1422 1423 _stringsSize = mainOffset - cachedStringsOffset; 1424 _mainSize = DataWriter()->BytesWritten() - mainOffset; 1425 1426 return cachedStringsWritten; 1427 } 1428 1429 1430 void 1431 PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute) 1432 { 1433 DoublyLinkedList<Attribute>::Iterator it 1434 = attribute->children.GetIterator(); 1435 while (Attribute* child = it.Next()) { 1436 // write tag 1437 uint8 encoding = child->value.ApplicableEncoding(); 1438 WriteUnsignedLEB128(HPKG_ATTRIBUTE_TAG_COMPOSE(child->id, 1439 child->value.type, encoding, !child->children.IsEmpty())); 1440 1441 // write value 1442 WriteAttributeValue(child->value, encoding); 1443 1444 if (!child->children.IsEmpty()) 1445 _WriteAttributeChildren(child); 1446 } 1447 1448 WriteUnsignedLEB128(0); 1449 } 1450 1451 1452 void 1453 PackageWriterImpl::_WritePackageAttributes(hpkg_header& header) 1454 { 1455 // write the package attributes (zlib writer on top of a file writer) 1456 off_t startOffset = fHeapEnd; 1457 1458 uint32 compression = B_HPKG_COMPRESSION_ZLIB; 1459 uint32 stringsLengthUncompressed; 1460 uint32 attributesLengthUncompressed; 1461 uint32 stringsCount = _WritePackageAttributesCompressed( 1462 stringsLengthUncompressed, attributesLengthUncompressed); 1463 1464 off_t endOffset = fHeapEnd; 1465 1466 if ((off_t)attributesLengthUncompressed <= endOffset - startOffset) { 1467 // the compressed section isn't shorter -- write uncompressed 1468 fHeapEnd = startOffset; 1469 compression = B_HPKG_COMPRESSION_NONE; 1470 stringsCount = _WritePackageAttributesUncompressed( 1471 stringsLengthUncompressed, attributesLengthUncompressed); 1472 1473 endOffset = fHeapEnd; 1474 } 1475 1476 fListener->OnPackageAttributesSizeInfo(stringsCount, 1477 attributesLengthUncompressed); 1478 1479 // update the header 1480 header.attributes_compression = B_HOST_TO_BENDIAN_INT32(compression); 1481 header.attributes_length_compressed 1482 = B_HOST_TO_BENDIAN_INT32(endOffset - startOffset); 1483 header.attributes_length_uncompressed 1484 = B_HOST_TO_BENDIAN_INT32(attributesLengthUncompressed); 1485 header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount); 1486 header.attributes_strings_length 1487 = B_HOST_TO_BENDIAN_INT32(stringsLengthUncompressed); 1488 } 1489 1490 1491 uint32 1492 PackageWriterImpl::_WritePackageAttributesCompressed( 1493 uint32& _stringsLengthUncompressed, uint32& _attributesLengthUncompressed) 1494 { 1495 off_t startOffset = fHeapEnd; 1496 FDDataWriter realWriter(FD(), startOffset, fListener); 1497 ZlibDataWriter zlibWriter(&realWriter); 1498 SetDataWriter(&zlibWriter); 1499 zlibWriter.Init(); 1500 1501 // write cached strings and package attributes tree 1502 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 1503 _stringsLengthUncompressed); 1504 1505 zlibWriter.Finish(); 1506 fHeapEnd = realWriter.Offset(); 1507 SetDataWriter(NULL); 1508 1509 _attributesLengthUncompressed = zlibWriter.BytesWritten(); 1510 return stringsCount; 1511 } 1512 1513 1514 uint32 1515 PackageWriterImpl::_WritePackageAttributesUncompressed( 1516 uint32& _stringsLengthUncompressed, uint32& _attributesLengthUncompressed) 1517 { 1518 off_t startOffset = fHeapEnd; 1519 FDDataWriter realWriter(FD(), startOffset, fListener); 1520 1521 SetDataWriter(&realWriter); 1522 1523 // write cached strings and package attributes tree 1524 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 1525 _stringsLengthUncompressed); 1526 1527 fHeapEnd = realWriter.Offset(); 1528 SetDataWriter(NULL); 1529 1530 _attributesLengthUncompressed = realWriter.BytesWritten(); 1531 return stringsCount; 1532 } 1533 1534 1535 void 1536 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName, 1537 char* pathBuffer) 1538 { 1539 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 1540 1541 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 1542 if (!isImplicitEntry) 1543 fListener->OnEntryAdded(pathBuffer); 1544 1545 // open the node 1546 int fd; 1547 FileDescriptorCloser fdCloser; 1548 1549 if (entry != NULL && entry->FD() >= 0) { 1550 // a file descriptor is already given -- use that 1551 fd = entry->FD(); 1552 } else { 1553 fd = openat(dirFD, fileName, 1554 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); 1555 if (fd < 0) { 1556 fListener->PrintError("Failed to open entry \"%s\": %s\n", 1557 pathBuffer, strerror(errno)); 1558 throw status_t(errno); 1559 } 1560 fdCloser.SetTo(fd); 1561 } 1562 1563 // stat the node 1564 struct stat st; 1565 if (fstat(fd, &st) < 0) { 1566 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer, 1567 strerror(errno)); 1568 throw status_t(errno); 1569 } 1570 1571 // implicit entries must be directories 1572 if (isImplicitEntry && !S_ISDIR(st.st_mode)) { 1573 fListener->PrintError("Non-leaf path component \"%s\" is not a " 1574 "directory\n", pathBuffer); 1575 throw status_t(B_BAD_VALUE); 1576 } 1577 1578 // In update mode we don't need to add an entry attribute for an implicit 1579 // directory, if there already is one. 1580 Attribute* entryAttribute = NULL; 1581 if (S_ISDIR(st.st_mode) && (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) { 1582 entryAttribute = fTopAttribute->FindEntryChild(fileName); 1583 if (entryAttribute != NULL && isImplicitEntry) { 1584 Stacker<Attribute> entryAttributeStacker(fTopAttribute, 1585 entryAttribute); 1586 _AddDirectoryChildren(entry, fd, pathBuffer); 1587 return; 1588 } 1589 } 1590 1591 // check/translate the node type 1592 uint8 fileType; 1593 uint32 defaultPermissions; 1594 if (S_ISREG(st.st_mode)) { 1595 fileType = B_HPKG_FILE_TYPE_FILE; 1596 defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS; 1597 } else if (S_ISLNK(st.st_mode)) { 1598 fileType = B_HPKG_FILE_TYPE_SYMLINK; 1599 defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS; 1600 } else if (S_ISDIR(st.st_mode)) { 1601 fileType = B_HPKG_FILE_TYPE_DIRECTORY; 1602 defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS; 1603 } else { 1604 // unsupported node type 1605 fListener->PrintError("Unsupported node type, entry: \"%s\"\n", 1606 pathBuffer); 1607 throw status_t(B_UNSUPPORTED); 1608 } 1609 1610 // add attribute entry, if it doesn't already exist (update mode, directory) 1611 bool isNewEntry = entryAttribute == NULL; 1612 if (entryAttribute == NULL) { 1613 entryAttribute = _AddStringAttribute( 1614 B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName); 1615 } 1616 1617 Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute); 1618 1619 if (isNewEntry) { 1620 // add stat data 1621 if (fileType != B_HPKG_DEFAULT_FILE_TYPE) 1622 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType); 1623 if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) { 1624 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS, 1625 uint32(st.st_mode & ALLPERMS)); 1626 } 1627 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime)); 1628 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime)); 1629 #ifdef __HAIKU__ 1630 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime)); 1631 #else 1632 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime)); 1633 #endif 1634 // TODO: File user/group! 1635 1636 // add file data/symlink path 1637 if (S_ISREG(st.st_mode)) { 1638 // regular file -- add data 1639 if (st.st_size > 0) { 1640 BFDDataReader dataReader(fd); 1641 status_t error = _AddData(dataReader, st.st_size); 1642 if (error != B_OK) 1643 throw status_t(error); 1644 } 1645 } else if (S_ISLNK(st.st_mode)) { 1646 // symlink -- add link address 1647 char path[B_PATH_NAME_LENGTH + 1]; 1648 ssize_t bytesRead = readlinkat(dirFD, fileName, path, 1649 B_PATH_NAME_LENGTH); 1650 if (bytesRead < 0) { 1651 fListener->PrintError("Failed to read symlink \"%s\": %s\n", 1652 pathBuffer, strerror(errno)); 1653 throw status_t(errno); 1654 } 1655 1656 path[bytesRead] = '\0'; 1657 _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path); 1658 } 1659 } 1660 1661 // add attributes 1662 if (DIR* attrDir = fs_fopen_attr_dir(fd)) { 1663 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir); 1664 1665 while (dirent* entry = fs_read_attr_dir(attrDir)) { 1666 attr_info attrInfo; 1667 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { 1668 fListener->PrintError( 1669 "Failed to stat attribute \"%s\" of file \"%s\": %s\n", 1670 entry->d_name, pathBuffer, strerror(errno)); 1671 throw status_t(errno); 1672 } 1673 1674 // create attribute entry 1675 Attribute* attributeAttribute = _AddStringAttribute( 1676 B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name); 1677 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, 1678 attributeAttribute); 1679 1680 // add type 1681 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE, 1682 (uint32)attrInfo.type); 1683 1684 // add data 1685 BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type); 1686 status_t error = _AddData(dataReader, attrInfo.size); 1687 if (error != B_OK) 1688 throw status_t(error); 1689 } 1690 } 1691 1692 if (S_ISDIR(st.st_mode)) 1693 _AddDirectoryChildren(entry, fd, pathBuffer); 1694 } 1695 1696 1697 void 1698 PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer) 1699 { 1700 // directory -- recursively add children 1701 if (entry != NULL && entry->IsImplicit()) { 1702 // this is an implicit entry -- just add it's children 1703 for (EntryList::ConstIterator it = entry->ChildIterator(); 1704 Entry* child = it.Next();) { 1705 _AddEntry(fd, child, child->Name(), pathBuffer); 1706 } 1707 } else { 1708 // we need to clone the directory FD for fdopendir() 1709 int clonedFD = dup(fd); 1710 if (clonedFD < 0) { 1711 fListener->PrintError( 1712 "Failed to dup() directory FD: %s\n", strerror(errno)); 1713 throw status_t(errno); 1714 } 1715 1716 DIR* dir = fdopendir(clonedFD); 1717 if (dir == NULL) { 1718 fListener->PrintError( 1719 "Failed to open directory \"%s\": %s\n", pathBuffer, 1720 strerror(errno)); 1721 close(clonedFD); 1722 throw status_t(errno); 1723 } 1724 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 1725 1726 while (dirent* entry = readdir(dir)) { 1727 // skip "." and ".." 1728 if (strcmp(entry->d_name, ".") == 0 1729 || strcmp(entry->d_name, "..") == 0) { 1730 continue; 1731 } 1732 1733 _AddEntry(fd, NULL, entry->d_name, pathBuffer); 1734 } 1735 } 1736 } 1737 1738 1739 PackageWriterImpl::Attribute* 1740 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id, 1741 const AttributeValue& value) 1742 { 1743 Attribute* attribute = new Attribute(id); 1744 1745 attribute->value = value; 1746 fTopAttribute->AddChild(attribute); 1747 1748 return attribute; 1749 } 1750 1751 1752 PackageWriterImpl::Attribute* 1753 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID, 1754 const char* value) 1755 { 1756 AttributeValue attributeValue; 1757 attributeValue.SetTo(fStringCache.Get(value)); 1758 return _AddAttribute(attributeID, attributeValue); 1759 } 1760 1761 1762 PackageWriterImpl::Attribute* 1763 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1764 uint64 dataSize, uint64 dataOffset) 1765 { 1766 AttributeValue attributeValue; 1767 attributeValue.SetToData(dataSize, dataOffset); 1768 return _AddAttribute(attributeID, attributeValue); 1769 } 1770 1771 1772 PackageWriterImpl::Attribute* 1773 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1774 uint64 dataSize, const uint8* data) 1775 { 1776 AttributeValue attributeValue; 1777 attributeValue.SetToData(dataSize, data); 1778 return _AddAttribute(attributeID, attributeValue); 1779 } 1780 1781 1782 status_t 1783 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size) 1784 { 1785 // add short data inline 1786 if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) { 1787 uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE]; 1788 status_t error = dataReader.ReadData(0, buffer, size); 1789 if (error != B_OK) { 1790 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1791 return error; 1792 } 1793 1794 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer); 1795 return B_OK; 1796 } 1797 1798 // longer data -- try to compress 1799 uint64 dataOffset = fHeapEnd; 1800 1801 uint64 compression = B_HPKG_COMPRESSION_NONE; 1802 uint64 compressedSize; 1803 1804 status_t error = _WriteZlibCompressedData(dataReader, size, dataOffset, 1805 compressedSize); 1806 if (error == B_OK) { 1807 compression = B_HPKG_COMPRESSION_ZLIB; 1808 } else { 1809 error = _WriteUncompressedData(dataReader, size, dataOffset); 1810 compressedSize = size; 1811 } 1812 if (error != B_OK) 1813 return error; 1814 1815 fHeapEnd = dataOffset + compressedSize; 1816 1817 // add data attribute 1818 Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, 1819 compressedSize, dataOffset - fHeapOffset); 1820 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, dataAttribute); 1821 1822 // if compressed, add compression attributes 1823 if (compression != B_HPKG_COMPRESSION_NONE) { 1824 _AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION, compression); 1825 _AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_SIZE, (uint64)size); 1826 // uncompressed size 1827 } 1828 1829 return B_OK; 1830 } 1831 1832 1833 status_t 1834 PackageWriterImpl::_WriteUncompressedData(BDataReader& dataReader, off_t size, 1835 uint64 writeOffset) 1836 { 1837 // copy the data to the heap 1838 off_t readOffset = 0; 1839 off_t remainingSize = size; 1840 while (remainingSize > 0) { 1841 // read data 1842 size_t toCopy = std::min(remainingSize, (off_t)fDataBufferSize); 1843 status_t error = dataReader.ReadData(readOffset, fDataBuffer, toCopy); 1844 if (error != B_OK) { 1845 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1846 return error; 1847 } 1848 1849 // write to heap 1850 ssize_t bytesWritten = pwrite(FD(), fDataBuffer, toCopy, writeOffset); 1851 if (bytesWritten < 0) { 1852 fListener->PrintError("Failed to write data: %s\n", 1853 strerror(errno)); 1854 return errno; 1855 } 1856 if ((size_t)bytesWritten != toCopy) { 1857 fListener->PrintError("Failed to write all data\n"); 1858 return B_ERROR; 1859 } 1860 1861 remainingSize -= toCopy; 1862 readOffset += toCopy; 1863 writeOffset += toCopy; 1864 } 1865 1866 return B_OK; 1867 } 1868 1869 1870 status_t 1871 PackageWriterImpl::_WriteZlibCompressedData(BDataReader& dataReader, off_t size, 1872 uint64 writeOffset, uint64& _compressedSize) 1873 { 1874 // Use zlib compression only for data large enough. 1875 if (size < (off_t)kZlibCompressionSizeThreshold) 1876 return B_BAD_VALUE; 1877 1878 // fDataBuffer is 2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, so split it into 1879 // two halves we can use for reading and compressing 1880 const size_t chunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB; 1881 uint8* inputBuffer = (uint8*)fDataBuffer; 1882 uint8* outputBuffer = (uint8*)fDataBuffer + chunkSize; 1883 1884 // account for the offset table 1885 uint64 chunkCount = (size + (chunkSize - 1)) / chunkSize; 1886 off_t offsetTableOffset = writeOffset; 1887 uint64* offsetTable = NULL; 1888 if (chunkCount > 1) { 1889 offsetTable = new uint64[chunkCount - 1]; 1890 writeOffset = offsetTableOffset + (chunkCount - 1) * sizeof(uint64); 1891 } 1892 ArrayDeleter<uint64> offsetTableDeleter(offsetTable); 1893 1894 const uint64 dataOffset = writeOffset; 1895 const uint64 dataEndLimit = offsetTableOffset + size; 1896 1897 // read the data, compress them and write them to the heap 1898 off_t readOffset = 0; 1899 off_t remainingSize = size; 1900 uint64 chunkIndex = 0; 1901 while (remainingSize > 0) { 1902 // read data 1903 size_t toCopy = std::min(remainingSize, (off_t)chunkSize); 1904 status_t error = dataReader.ReadData(readOffset, inputBuffer, toCopy); 1905 if (error != B_OK) { 1906 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1907 return error; 1908 } 1909 1910 // compress 1911 size_t compressedSize; 1912 error = ZlibCompressor::CompressSingleBuffer(inputBuffer, toCopy, 1913 outputBuffer, toCopy, compressedSize); 1914 1915 const void* writeBuffer; 1916 size_t bytesToWrite; 1917 if (error == B_OK) { 1918 writeBuffer = outputBuffer; 1919 bytesToWrite = compressedSize; 1920 } else { 1921 if (error != B_BUFFER_OVERFLOW) 1922 return error; 1923 writeBuffer = inputBuffer; 1924 bytesToWrite = toCopy; 1925 } 1926 1927 // check the total compressed data size 1928 if (writeOffset + bytesToWrite >= dataEndLimit) 1929 return B_BUFFER_OVERFLOW; 1930 1931 if (chunkIndex > 0) 1932 offsetTable[chunkIndex - 1] = writeOffset - dataOffset; 1933 1934 // write to heap 1935 ssize_t bytesWritten = pwrite(FD(), writeBuffer, bytesToWrite, 1936 writeOffset); 1937 if (bytesWritten < 0) { 1938 fListener->PrintError("Failed to write data: %s\n", 1939 strerror(errno)); 1940 return errno; 1941 } 1942 if ((size_t)bytesWritten != bytesToWrite) { 1943 fListener->PrintError("Failed to write all data\n"); 1944 return B_ERROR; 1945 } 1946 1947 remainingSize -= toCopy; 1948 readOffset += toCopy; 1949 writeOffset += bytesToWrite; 1950 chunkIndex++; 1951 } 1952 1953 // write the offset table 1954 if (chunkCount > 1) { 1955 size_t bytesToWrite = (chunkCount - 1) * sizeof(uint64); 1956 ssize_t bytesWritten = pwrite(FD(), offsetTable, bytesToWrite, 1957 offsetTableOffset); 1958 if (bytesWritten < 0) { 1959 fListener->PrintError("Failed to write data: %s\n", 1960 strerror(errno)); 1961 return errno; 1962 } 1963 if ((size_t)bytesWritten != bytesToWrite) { 1964 fListener->PrintError("Failed to write all data\n"); 1965 return B_ERROR; 1966 } 1967 } 1968 1969 _compressedSize = writeOffset - offsetTableOffset; 1970 return B_OK; 1971 } 1972 1973 1974 } // namespace BPrivate 1975 1976 } // namespace BHPKG 1977 1978 } // namespace BPackageKit 1979