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