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