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