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 _AddEntry(fd, NULL, entry->d_name, pathBuffer); 1071 _UpdateCheckEntryCollisions(entryAttribute, fd, NULL, entry->d_name, 1072 pathBuffer); 1073 } 1074 } 1075 } 1076 1077 1078 void 1079 PackageWriterImpl::_CompactHeap() 1080 { 1081 int32 count = fHeapRangesToRemove->CountRanges(); 1082 if (count == 0) 1083 return; 1084 1085 // compute the move deltas for the ranges 1086 Array<off_t> deltas; 1087 off_t delta = 0; 1088 for (int32 i = 0; i < count; i++) { 1089 if (!deltas.Add(delta)) 1090 throw std::bad_alloc(); 1091 1092 delta += fHeapRangesToRemove->RangeAt(i).size; 1093 } 1094 1095 if (!deltas.Add(delta)) 1096 throw std::bad_alloc(); 1097 1098 // offset the attributes 1099 HeapAttributeOffsetter(*fHeapRangesToRemove, deltas).ProcessAttribute( 1100 fRootAttribute); 1101 1102 // move the heap chunks in the file around 1103 off_t chunkOffset = fHeapOffset; 1104 delta = 0; 1105 1106 for (int32 i = 0; i < count; i++) { 1107 const Range<off_t>& range = fHeapRangesToRemove->RangeAt(i); 1108 1109 if (delta > 0 && chunkOffset < range.offset) { 1110 // move chunk 1111 _MoveHeapChunk(chunkOffset, chunkOffset - delta, 1112 range.offset - chunkOffset); 1113 } 1114 1115 chunkOffset = range.EndOffset(); 1116 delta += range.size; 1117 } 1118 1119 // move the final chunk 1120 off_t heapSize = fHeapEnd - fHeapOffset; 1121 if (delta > 0 && chunkOffset < heapSize) { 1122 _MoveHeapChunk(chunkOffset, chunkOffset - delta, 1123 heapSize - chunkOffset); 1124 } 1125 1126 fHeapEnd -= delta; 1127 } 1128 1129 1130 void 1131 PackageWriterImpl::_MoveHeapChunk(off_t fromOffset, off_t toOffset, off_t size) 1132 { 1133 // convert heap offsets to file offsets 1134 fromOffset += fHeapOffset; 1135 toOffset += fHeapOffset; 1136 1137 while (size > 0) { 1138 size_t toCopy = std::min(size, (off_t)fDataBufferSize); 1139 1140 // read data into buffer 1141 ssize_t bytesRead = read_pos(FD(), fromOffset, fDataBuffer, toCopy); 1142 if (bytesRead < 0) { 1143 fListener->PrintError("Failed to read from package file: %s\n", 1144 strerror(errno)); 1145 throw status_t(errno); 1146 } 1147 if ((size_t)bytesRead < toCopy) { 1148 fListener->PrintError("Failed to read from package file (wanted " 1149 "%zu bytes, got %zd).\n", toCopy, bytesRead); 1150 throw status_t(B_IO_ERROR); 1151 } 1152 1153 // write data to target offset 1154 ssize_t bytesWritten = write_pos(FD(), toOffset, fDataBuffer, toCopy); 1155 if (bytesWritten < 0) { 1156 fListener->PrintError("Failed to write to package file: %s\n", 1157 strerror(errno)); 1158 throw status_t(errno); 1159 } 1160 if ((size_t)bytesWritten < toCopy) { 1161 fListener->PrintError("Failed to write to package file.\n"); 1162 throw status_t(B_IO_ERROR); 1163 } 1164 1165 fromOffset += toCopy; 1166 toOffset += toCopy; 1167 size -= toCopy; 1168 } 1169 } 1170 1171 1172 void 1173 PackageWriterImpl::_AttributeRemoved(Attribute* attribute) 1174 { 1175 AttributeValue& value = attribute->value; 1176 if (value.type == B_HPKG_ATTRIBUTE_TYPE_RAW 1177 && value.encoding == B_HPKG_ATTRIBUTE_ENCODING_RAW_HEAP) { 1178 if (!fHeapRangesToRemove->AddRange(value.data.offset, value.data.size)) 1179 throw std::bad_alloc(); 1180 } 1181 1182 for (DoublyLinkedList<Attribute>::Iterator it 1183 = attribute->children.GetIterator(); 1184 Attribute* child = it.Next();) { 1185 _AttributeRemoved(child); 1186 } 1187 } 1188 1189 1190 status_t 1191 PackageWriterImpl::_Finish() 1192 { 1193 // write entries 1194 for (EntryList::ConstIterator it = fRootEntry->ChildIterator(); 1195 Entry* entry = it.Next();) { 1196 char pathBuffer[B_PATH_NAME_LENGTH]; 1197 pathBuffer[0] = '\0'; 1198 _AddEntry(AT_FDCWD, entry, entry->Name(), pathBuffer); 1199 } 1200 1201 off_t heapSize = fHeapEnd - fHeapOffset; 1202 1203 hpkg_header header; 1204 1205 // write the TOC and package attributes 1206 _WriteTOC(header); 1207 _WritePackageAttributes(header); 1208 1209 off_t totalSize = fHeapEnd; 1210 1211 // Truncate the file to the size it is supposed to have. In update mode, it 1212 // can be greater when one or more files are shrunk. In creation mode, when 1213 // writing compressed TOC or package attributes yields a larger size than 1214 // uncompressed, the file size may also be greater than it should be. 1215 if (ftruncate(FD(), totalSize) != 0) { 1216 fListener->PrintError("Failed to truncate package file to new " 1217 "size: %s\n", strerror(errno)); 1218 return errno; 1219 } 1220 1221 fListener->OnPackageSizeInfo(fHeapOffset, heapSize, 1222 B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed), 1223 B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed), 1224 totalSize); 1225 1226 // prepare the header 1227 1228 // general 1229 header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_MAGIC); 1230 header.header_size = B_HOST_TO_BENDIAN_INT16((uint16)fHeapOffset); 1231 header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_VERSION); 1232 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize); 1233 1234 // write the header 1235 WriteBuffer(&header, sizeof(hpkg_header), 0); 1236 1237 SetFinished(true); 1238 return B_OK; 1239 } 1240 1241 1242 status_t 1243 PackageWriterImpl::_RegisterEntry(const char* fileName, int fd) 1244 { 1245 if (*fileName == '\0') { 1246 fListener->PrintError("Invalid empty file name\n"); 1247 return B_BAD_VALUE; 1248 } 1249 1250 // add all components of the path 1251 Entry* entry = fRootEntry; 1252 while (*fileName != 0) { 1253 const char* nextSlash = strchr(fileName, '/'); 1254 // no slash, just add the file name 1255 if (nextSlash == NULL) { 1256 entry = _RegisterEntry(entry, fileName, strlen(fileName), fd, 1257 false); 1258 break; 1259 } 1260 1261 // find the start of the next component, skipping slashes 1262 const char* nextComponent = nextSlash + 1; 1263 while (*nextComponent == '/') 1264 nextComponent++; 1265 1266 bool lastComponent = *nextComponent != '\0'; 1267 1268 if (nextSlash == fileName) { 1269 // the FS root 1270 entry = _RegisterEntry(entry, fileName, 1, lastComponent ? fd : -1, 1271 lastComponent); 1272 } else { 1273 entry = _RegisterEntry(entry, fileName, nextSlash - fileName, 1274 lastComponent ? fd : -1, lastComponent); 1275 } 1276 1277 fileName = nextComponent; 1278 } 1279 1280 return B_OK; 1281 } 1282 1283 1284 PackageWriterImpl::Entry* 1285 PackageWriterImpl::_RegisterEntry(Entry* parent, const char* name, 1286 size_t nameLength, int fd, bool isImplicit) 1287 { 1288 // check the component name -- don't allow "." or ".." 1289 if (name[0] == '.' 1290 && (nameLength == 1 || (nameLength == 2 && name[1] == '.'))) { 1291 fListener->PrintError("Invalid file name: \".\" and \"..\" " 1292 "are not allowed as path components\n"); 1293 throw status_t(B_BAD_VALUE); 1294 } 1295 1296 // the entry might already exist 1297 Entry* entry = parent->GetChild(name, nameLength); 1298 if (entry != NULL) { 1299 // If the entry was implicit and is no longer, we mark it non-implicit 1300 // and delete all of it's children. 1301 if (entry->IsImplicit() && !isImplicit) { 1302 entry->DeleteChildren(); 1303 entry->SetImplicit(false); 1304 entry->SetFD(fd); 1305 } 1306 } else { 1307 // nope -- create it 1308 entry = Entry::Create(name, nameLength, fd, isImplicit); 1309 parent->AddChild(entry); 1310 } 1311 1312 return entry; 1313 } 1314 1315 1316 void 1317 PackageWriterImpl::_WriteTOC(hpkg_header& header) 1318 { 1319 // prepare the writer (zlib writer on top of a file writer) 1320 off_t startOffset = fHeapEnd; 1321 1322 // write the sections 1323 uint32 compression = B_HPKG_COMPRESSION_ZLIB; 1324 uint64 uncompressedStringsSize; 1325 uint64 uncompressedMainSize; 1326 uint64 tocUncompressedSize; 1327 int32 cachedStringsWritten = _WriteTOCCompressed(uncompressedStringsSize, 1328 uncompressedMainSize, tocUncompressedSize); 1329 1330 off_t endOffset = fHeapEnd; 1331 1332 if (endOffset - startOffset >= (off_t)tocUncompressedSize) { 1333 // the compressed section isn't shorter -- write uncompressed 1334 fHeapEnd = startOffset; 1335 compression = B_HPKG_COMPRESSION_NONE; 1336 cachedStringsWritten = _WriteTOCUncompressed(uncompressedStringsSize, 1337 uncompressedMainSize, tocUncompressedSize); 1338 1339 endOffset = fHeapEnd; 1340 } 1341 1342 fListener->OnTOCSizeInfo(uncompressedStringsSize, uncompressedMainSize, 1343 tocUncompressedSize); 1344 1345 // update the header 1346 1347 // TOC 1348 header.toc_compression = B_HOST_TO_BENDIAN_INT32(compression); 1349 header.toc_length_compressed = B_HOST_TO_BENDIAN_INT64( 1350 endOffset - startOffset); 1351 header.toc_length_uncompressed = B_HOST_TO_BENDIAN_INT64( 1352 tocUncompressedSize); 1353 1354 // TOC subsections 1355 header.toc_strings_length = B_HOST_TO_BENDIAN_INT64( 1356 uncompressedStringsSize); 1357 header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten); 1358 } 1359 1360 1361 int32 1362 PackageWriterImpl::_WriteTOCCompressed(uint64& _uncompressedStringsSize, 1363 uint64& _uncompressedMainSize, uint64& _tocUncompressedSize) 1364 { 1365 FDDataWriter realWriter(FD(), fHeapEnd, fListener); 1366 ZlibDataWriter zlibWriter(&realWriter); 1367 SetDataWriter(&zlibWriter); 1368 zlibWriter.Init(); 1369 1370 // write the sections 1371 int32 cachedStringsWritten 1372 = _WriteTOCSections(_uncompressedStringsSize, _uncompressedMainSize); 1373 1374 // finish the writer 1375 zlibWriter.Finish(); 1376 fHeapEnd = realWriter.Offset(); 1377 SetDataWriter(NULL); 1378 1379 _tocUncompressedSize = zlibWriter.BytesWritten(); 1380 return cachedStringsWritten; 1381 } 1382 1383 1384 int32 1385 PackageWriterImpl::_WriteTOCUncompressed(uint64& _uncompressedStringsSize, 1386 uint64& _uncompressedMainSize, uint64& _tocUncompressedSize) 1387 { 1388 FDDataWriter realWriter(FD(), fHeapEnd, fListener); 1389 SetDataWriter(&realWriter); 1390 1391 // write the sections 1392 int32 cachedStringsWritten 1393 = _WriteTOCSections(_uncompressedStringsSize, _uncompressedMainSize); 1394 1395 fHeapEnd = realWriter.Offset(); 1396 SetDataWriter(NULL); 1397 1398 _tocUncompressedSize = realWriter.BytesWritten(); 1399 return cachedStringsWritten; 1400 } 1401 1402 1403 int32 1404 PackageWriterImpl::_WriteTOCSections(uint64& _stringsSize, uint64& _mainSize) 1405 { 1406 // write the cached strings 1407 uint64 cachedStringsOffset = DataWriter()->BytesWritten(); 1408 int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2); 1409 1410 // write the main TOC section 1411 uint64 mainOffset = DataWriter()->BytesWritten(); 1412 _WriteAttributeChildren(fRootAttribute); 1413 1414 _stringsSize = mainOffset - cachedStringsOffset; 1415 _mainSize = DataWriter()->BytesWritten() - mainOffset; 1416 1417 return cachedStringsWritten; 1418 } 1419 1420 1421 void 1422 PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute) 1423 { 1424 DoublyLinkedList<Attribute>::Iterator it 1425 = attribute->children.GetIterator(); 1426 while (Attribute* child = it.Next()) { 1427 // write tag 1428 uint8 encoding = child->value.ApplicableEncoding(); 1429 WriteUnsignedLEB128(HPKG_ATTRIBUTE_TAG_COMPOSE(child->id, 1430 child->value.type, encoding, !child->children.IsEmpty())); 1431 1432 // write value 1433 WriteAttributeValue(child->value, encoding); 1434 1435 if (!child->children.IsEmpty()) 1436 _WriteAttributeChildren(child); 1437 } 1438 1439 WriteUnsignedLEB128(0); 1440 } 1441 1442 1443 void 1444 PackageWriterImpl::_WritePackageAttributes(hpkg_header& header) 1445 { 1446 // write the package attributes (zlib writer on top of a file writer) 1447 off_t startOffset = fHeapEnd; 1448 1449 uint32 compression = B_HPKG_COMPRESSION_ZLIB; 1450 uint32 stringsLengthUncompressed; 1451 uint32 attributesLengthUncompressed; 1452 uint32 stringsCount = _WritePackageAttributesCompressed( 1453 stringsLengthUncompressed, attributesLengthUncompressed); 1454 1455 off_t endOffset = fHeapEnd; 1456 1457 if ((off_t)attributesLengthUncompressed <= endOffset - startOffset) { 1458 // the compressed section isn't shorter -- write uncompressed 1459 fHeapEnd = startOffset; 1460 compression = B_HPKG_COMPRESSION_NONE; 1461 stringsCount = _WritePackageAttributesUncompressed( 1462 stringsLengthUncompressed, attributesLengthUncompressed); 1463 1464 endOffset = fHeapEnd; 1465 } 1466 1467 fListener->OnPackageAttributesSizeInfo(stringsCount, 1468 attributesLengthUncompressed); 1469 1470 // update the header 1471 header.attributes_compression = B_HOST_TO_BENDIAN_INT32(compression); 1472 header.attributes_length_compressed 1473 = B_HOST_TO_BENDIAN_INT32(endOffset - startOffset); 1474 header.attributes_length_uncompressed 1475 = B_HOST_TO_BENDIAN_INT32(attributesLengthUncompressed); 1476 header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount); 1477 header.attributes_strings_length 1478 = B_HOST_TO_BENDIAN_INT32(stringsLengthUncompressed); 1479 } 1480 1481 1482 uint32 1483 PackageWriterImpl::_WritePackageAttributesCompressed( 1484 uint32& _stringsLengthUncompressed, uint32& _attributesLengthUncompressed) 1485 { 1486 off_t startOffset = fHeapEnd; 1487 FDDataWriter realWriter(FD(), startOffset, fListener); 1488 ZlibDataWriter zlibWriter(&realWriter); 1489 SetDataWriter(&zlibWriter); 1490 zlibWriter.Init(); 1491 1492 // write cached strings and package attributes tree 1493 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 1494 _stringsLengthUncompressed); 1495 1496 zlibWriter.Finish(); 1497 fHeapEnd = realWriter.Offset(); 1498 SetDataWriter(NULL); 1499 1500 _attributesLengthUncompressed = zlibWriter.BytesWritten(); 1501 return stringsCount; 1502 } 1503 1504 1505 uint32 1506 PackageWriterImpl::_WritePackageAttributesUncompressed( 1507 uint32& _stringsLengthUncompressed, uint32& _attributesLengthUncompressed) 1508 { 1509 off_t startOffset = fHeapEnd; 1510 FDDataWriter realWriter(FD(), startOffset, fListener); 1511 1512 SetDataWriter(&realWriter); 1513 1514 // write cached strings and package attributes tree 1515 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 1516 _stringsLengthUncompressed); 1517 1518 fHeapEnd = realWriter.Offset(); 1519 SetDataWriter(NULL); 1520 1521 _attributesLengthUncompressed = realWriter.BytesWritten(); 1522 return stringsCount; 1523 } 1524 1525 1526 void 1527 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName, 1528 char* pathBuffer) 1529 { 1530 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 1531 1532 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 1533 if (!isImplicitEntry) 1534 fListener->OnEntryAdded(pathBuffer); 1535 1536 // open the node 1537 int fd; 1538 FileDescriptorCloser fdCloser; 1539 1540 if (entry != NULL && entry->FD() >= 0) { 1541 // a file descriptor is already given -- use that 1542 fd = entry->FD(); 1543 } else { 1544 fd = openat(dirFD, fileName, 1545 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); 1546 if (fd < 0) { 1547 fListener->PrintError("Failed to open entry \"%s\": %s\n", 1548 pathBuffer, strerror(errno)); 1549 throw status_t(errno); 1550 } 1551 fdCloser.SetTo(fd); 1552 } 1553 1554 // stat the node 1555 struct stat st; 1556 if (fstat(fd, &st) < 0) { 1557 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer, 1558 strerror(errno)); 1559 throw status_t(errno); 1560 } 1561 1562 // implicit entries must be directories 1563 if (isImplicitEntry && !S_ISDIR(st.st_mode)) { 1564 fListener->PrintError("Non-leaf path component \"%s\" is not a " 1565 "directory\n", pathBuffer); 1566 throw status_t(B_BAD_VALUE); 1567 } 1568 1569 // In update mode we don't need to add an entry attribute for an implicit 1570 // directory, if there already is one. 1571 if (isImplicitEntry && (Flags() & B_HPKG_WRITER_UPDATE_PACKAGE) != 0) { 1572 Attribute* entryAttribute = fTopAttribute->FindEntryChild(fileName); 1573 if (entryAttribute != NULL) { 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 1601 Attribute* entryAttribute = _AddStringAttribute( 1602 B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName); 1603 Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute); 1604 1605 // add stat data 1606 if (fileType != B_HPKG_DEFAULT_FILE_TYPE) 1607 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType); 1608 if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) { 1609 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS, 1610 uint32(st.st_mode & ALLPERMS)); 1611 } 1612 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime)); 1613 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime)); 1614 #ifdef __HAIKU__ 1615 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime)); 1616 #else 1617 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime)); 1618 #endif 1619 // TODO: File user/group! 1620 1621 // add file data/symlink path 1622 if (S_ISREG(st.st_mode)) { 1623 // regular file -- add data 1624 if (st.st_size > 0) { 1625 BFDDataReader dataReader(fd); 1626 status_t error = _AddData(dataReader, st.st_size); 1627 if (error != B_OK) 1628 throw status_t(error); 1629 } 1630 } else if (S_ISLNK(st.st_mode)) { 1631 // symlink -- add link address 1632 char path[B_PATH_NAME_LENGTH + 1]; 1633 ssize_t bytesRead = readlinkat(dirFD, fileName, path, 1634 B_PATH_NAME_LENGTH); 1635 if (bytesRead < 0) { 1636 fListener->PrintError("Failed to read symlink \"%s\": %s\n", 1637 pathBuffer, strerror(errno)); 1638 throw status_t(errno); 1639 } 1640 1641 path[bytesRead] = '\0'; 1642 _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path); 1643 } 1644 1645 // add attributes 1646 if (DIR* attrDir = fs_fopen_attr_dir(fd)) { 1647 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir); 1648 1649 while (dirent* entry = fs_read_attr_dir(attrDir)) { 1650 attr_info attrInfo; 1651 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { 1652 fListener->PrintError( 1653 "Failed to stat attribute \"%s\" of file \"%s\": %s\n", 1654 entry->d_name, pathBuffer, strerror(errno)); 1655 throw status_t(errno); 1656 } 1657 1658 // create attribute entry 1659 Attribute* attributeAttribute = _AddStringAttribute( 1660 B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name); 1661 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, 1662 attributeAttribute); 1663 1664 // add type 1665 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE, 1666 (uint32)attrInfo.type); 1667 1668 // add data 1669 BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type); 1670 status_t error = _AddData(dataReader, attrInfo.size); 1671 if (error != B_OK) 1672 throw status_t(error); 1673 } 1674 } 1675 1676 if (S_ISDIR(st.st_mode)) 1677 _AddDirectoryChildren(entry, fd, pathBuffer); 1678 } 1679 1680 1681 void 1682 PackageWriterImpl::_AddDirectoryChildren(Entry* entry, int fd, char* pathBuffer) 1683 { 1684 // directory -- recursively add children 1685 if (entry != NULL && entry->IsImplicit()) { 1686 // this is an implicit entry -- just add it's children 1687 for (EntryList::ConstIterator it = entry->ChildIterator(); 1688 Entry* child = it.Next();) { 1689 _AddEntry(fd, child, child->Name(), pathBuffer); 1690 } 1691 } else { 1692 // we need to clone the directory FD for fdopendir() 1693 int clonedFD = dup(fd); 1694 if (clonedFD < 0) { 1695 fListener->PrintError( 1696 "Failed to dup() directory FD: %s\n", strerror(errno)); 1697 throw status_t(errno); 1698 } 1699 1700 DIR* dir = fdopendir(clonedFD); 1701 if (dir == NULL) { 1702 fListener->PrintError( 1703 "Failed to open directory \"%s\": %s\n", pathBuffer, 1704 strerror(errno)); 1705 close(clonedFD); 1706 throw status_t(errno); 1707 } 1708 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 1709 1710 while (dirent* entry = readdir(dir)) { 1711 // skip "." and ".." 1712 if (strcmp(entry->d_name, ".") == 0 1713 || strcmp(entry->d_name, "..") == 0) { 1714 continue; 1715 } 1716 1717 _AddEntry(fd, NULL, entry->d_name, pathBuffer); 1718 } 1719 } 1720 } 1721 1722 1723 PackageWriterImpl::Attribute* 1724 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id, 1725 const AttributeValue& value) 1726 { 1727 Attribute* attribute = new Attribute(id); 1728 1729 attribute->value = value; 1730 fTopAttribute->AddChild(attribute); 1731 1732 return attribute; 1733 } 1734 1735 1736 PackageWriterImpl::Attribute* 1737 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID, 1738 const char* value) 1739 { 1740 AttributeValue attributeValue; 1741 attributeValue.SetTo(fStringCache.Get(value)); 1742 return _AddAttribute(attributeID, attributeValue); 1743 } 1744 1745 1746 PackageWriterImpl::Attribute* 1747 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1748 uint64 dataSize, uint64 dataOffset) 1749 { 1750 AttributeValue attributeValue; 1751 attributeValue.SetToData(dataSize, dataOffset); 1752 return _AddAttribute(attributeID, attributeValue); 1753 } 1754 1755 1756 PackageWriterImpl::Attribute* 1757 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 1758 uint64 dataSize, const uint8* data) 1759 { 1760 AttributeValue attributeValue; 1761 attributeValue.SetToData(dataSize, data); 1762 return _AddAttribute(attributeID, attributeValue); 1763 } 1764 1765 1766 status_t 1767 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size) 1768 { 1769 // add short data inline 1770 if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) { 1771 uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE]; 1772 status_t error = dataReader.ReadData(0, buffer, size); 1773 if (error != B_OK) { 1774 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1775 return error; 1776 } 1777 1778 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer); 1779 return B_OK; 1780 } 1781 1782 // longer data -- try to compress 1783 uint64 dataOffset = fHeapEnd; 1784 1785 uint64 compression = B_HPKG_COMPRESSION_NONE; 1786 uint64 compressedSize; 1787 1788 status_t error = _WriteZlibCompressedData(dataReader, size, dataOffset, 1789 compressedSize); 1790 if (error == B_OK) { 1791 compression = B_HPKG_COMPRESSION_ZLIB; 1792 } else { 1793 error = _WriteUncompressedData(dataReader, size, dataOffset); 1794 compressedSize = size; 1795 } 1796 if (error != B_OK) 1797 return error; 1798 1799 fHeapEnd = dataOffset + compressedSize; 1800 1801 // add data attribute 1802 Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, 1803 compressedSize, dataOffset - fHeapOffset); 1804 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, dataAttribute); 1805 1806 // if compressed, add compression attributes 1807 if (compression != B_HPKG_COMPRESSION_NONE) { 1808 _AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION, compression); 1809 _AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_SIZE, (uint64)size); 1810 // uncompressed size 1811 } 1812 1813 return B_OK; 1814 } 1815 1816 1817 status_t 1818 PackageWriterImpl::_WriteUncompressedData(BDataReader& dataReader, off_t size, 1819 uint64 writeOffset) 1820 { 1821 // copy the data to the heap 1822 off_t readOffset = 0; 1823 off_t remainingSize = size; 1824 while (remainingSize > 0) { 1825 // read data 1826 size_t toCopy = std::min(remainingSize, (off_t)fDataBufferSize); 1827 status_t error = dataReader.ReadData(readOffset, fDataBuffer, toCopy); 1828 if (error != B_OK) { 1829 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1830 return error; 1831 } 1832 1833 // write to heap 1834 ssize_t bytesWritten = pwrite(FD(), fDataBuffer, toCopy, writeOffset); 1835 if (bytesWritten < 0) { 1836 fListener->PrintError("Failed to write data: %s\n", 1837 strerror(errno)); 1838 return errno; 1839 } 1840 if ((size_t)bytesWritten != toCopy) { 1841 fListener->PrintError("Failed to write all data\n"); 1842 return B_ERROR; 1843 } 1844 1845 remainingSize -= toCopy; 1846 readOffset += toCopy; 1847 writeOffset += toCopy; 1848 } 1849 1850 return B_OK; 1851 } 1852 1853 1854 status_t 1855 PackageWriterImpl::_WriteZlibCompressedData(BDataReader& dataReader, off_t size, 1856 uint64 writeOffset, uint64& _compressedSize) 1857 { 1858 // Use zlib compression only for data large enough. 1859 if (size < (off_t)kZlibCompressionSizeThreshold) 1860 return B_BAD_VALUE; 1861 1862 // fDataBuffer is 2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, so split it into 1863 // two halves we can use for reading and compressing 1864 const size_t chunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB; 1865 uint8* inputBuffer = (uint8*)fDataBuffer; 1866 uint8* outputBuffer = (uint8*)fDataBuffer + chunkSize; 1867 1868 // account for the offset table 1869 uint64 chunkCount = (size + (chunkSize - 1)) / chunkSize; 1870 off_t offsetTableOffset = writeOffset; 1871 uint64* offsetTable = NULL; 1872 if (chunkCount > 1) { 1873 offsetTable = new uint64[chunkCount - 1]; 1874 writeOffset = offsetTableOffset + (chunkCount - 1) * sizeof(uint64); 1875 } 1876 ArrayDeleter<uint64> offsetTableDeleter(offsetTable); 1877 1878 const uint64 dataOffset = writeOffset; 1879 const uint64 dataEndLimit = offsetTableOffset + size; 1880 1881 // read the data, compress them and write them to the heap 1882 off_t readOffset = 0; 1883 off_t remainingSize = size; 1884 uint64 chunkIndex = 0; 1885 while (remainingSize > 0) { 1886 // read data 1887 size_t toCopy = std::min(remainingSize, (off_t)chunkSize); 1888 status_t error = dataReader.ReadData(readOffset, inputBuffer, toCopy); 1889 if (error != B_OK) { 1890 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1891 return error; 1892 } 1893 1894 // compress 1895 size_t compressedSize; 1896 error = ZlibCompressor::CompressSingleBuffer(inputBuffer, toCopy, 1897 outputBuffer, toCopy, compressedSize); 1898 1899 const void* writeBuffer; 1900 size_t bytesToWrite; 1901 if (error == B_OK) { 1902 writeBuffer = outputBuffer; 1903 bytesToWrite = compressedSize; 1904 } else { 1905 if (error != B_BUFFER_OVERFLOW) 1906 return error; 1907 writeBuffer = inputBuffer; 1908 bytesToWrite = toCopy; 1909 } 1910 1911 // check the total compressed data size 1912 if (writeOffset + bytesToWrite >= dataEndLimit) 1913 return B_BUFFER_OVERFLOW; 1914 1915 if (chunkIndex > 0) 1916 offsetTable[chunkIndex - 1] = writeOffset - dataOffset; 1917 1918 // write to heap 1919 ssize_t bytesWritten = pwrite(FD(), writeBuffer, bytesToWrite, 1920 writeOffset); 1921 if (bytesWritten < 0) { 1922 fListener->PrintError("Failed to write data: %s\n", 1923 strerror(errno)); 1924 return errno; 1925 } 1926 if ((size_t)bytesWritten != bytesToWrite) { 1927 fListener->PrintError("Failed to write all data\n"); 1928 return B_ERROR; 1929 } 1930 1931 remainingSize -= toCopy; 1932 readOffset += toCopy; 1933 writeOffset += bytesToWrite; 1934 chunkIndex++; 1935 } 1936 1937 // write the offset table 1938 if (chunkCount > 1) { 1939 size_t bytesToWrite = (chunkCount - 1) * sizeof(uint64); 1940 ssize_t bytesWritten = pwrite(FD(), offsetTable, bytesToWrite, 1941 offsetTableOffset); 1942 if (bytesWritten < 0) { 1943 fListener->PrintError("Failed to write data: %s\n", 1944 strerror(errno)); 1945 return errno; 1946 } 1947 if ((size_t)bytesWritten != bytesToWrite) { 1948 fListener->PrintError("Failed to write all data\n"); 1949 return B_ERROR; 1950 } 1951 } 1952 1953 _compressedSize = writeOffset - offsetTableOffset; 1954 return B_OK; 1955 } 1956 1957 1958 } // namespace BPrivate 1959 1960 } // namespace BHPKG 1961 1962 } // namespace BPackageKit 1963