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 <AutoDeleter.h> 30 31 #include <package/hpkg/HPKGDefsPrivate.h> 32 33 #include <package/hpkg/DataOutput.h> 34 #include <package/hpkg/DataReader.h> 35 #include <package/hpkg/Stacker.h> 36 37 38 using BPrivate::FileDescriptorCloser; 39 40 41 static const char* const kPublicDomainLicenseName = "Public Domain"; 42 43 44 // We need to remap a few file operations on non-Haiku platforms, so dealing 45 // with symlinks works correctly. 46 #ifndef __HAIKU__ 47 48 49 extern "C" int _kern_open(int fd, const char *path, int openMode, int perms); 50 extern "C" status_t _kern_close(int fd); 51 extern "C" status_t _kern_read_stat(int fd, const char *path, bool traverseLink, 52 struct stat *st, size_t statSize); 53 54 55 static int 56 build_openat(int dirFD, const char* fileName, int openMode, int permissions) 57 { 58 int fd = _kern_open(dirFD, fileName, openMode, permissions); 59 if (fd < 0) { 60 errno = fd; 61 return -1; 62 } 63 64 return fd; 65 } 66 67 68 static int 69 build_fstat(int fd, struct stat* st) 70 { 71 status_t error = _kern_read_stat(fd, NULL, false, st, sizeof(struct stat)); 72 if (error != B_OK) { 73 errno = error; 74 return -1; 75 } 76 77 return 0; 78 } 79 80 81 struct BuildFileDescriptorCloser { 82 BuildFileDescriptorCloser(int fd) 83 : 84 fFD(fd) 85 { 86 } 87 88 ~BuildFileDescriptorCloser() 89 { 90 if (fFD >= 0) 91 _kern_close(fFD); 92 } 93 94 private: 95 int fFD; 96 }; 97 98 99 #undef openat 100 #define openat(dirFD, fileName, openMode) \ 101 build_openat(dirFD, fileName, openMode, 0) 102 103 #undef fstat 104 #define fstat(fd, st) build_fstat(fd, st) 105 106 #undef FileDescriptorCloser 107 #define FileDescriptorCloser BuildFileDescriptorCloser 108 109 110 #endif 111 112 113 namespace BPackageKit { 114 115 namespace BHPKG { 116 117 namespace BPrivate { 118 119 120 // minimum length of data we require before trying to zlib compress them 121 static const size_t kZlibCompressionSizeThreshold = 64; 122 123 124 // #pragma mark - Attributes 125 126 127 struct PackageWriterImpl::Attribute 128 : public DoublyLinkedListLinkImpl<Attribute> { 129 BHPKGAttributeID id; 130 AttributeValue value; 131 DoublyLinkedList<Attribute> children; 132 133 Attribute(BHPKGAttributeID id_ = B_HPKG_ATTRIBUTE_ID_ENUM_COUNT) 134 : 135 id(id_) 136 { 137 } 138 139 ~Attribute() 140 { 141 DeleteChildren(); 142 } 143 144 void AddChild(Attribute* child) 145 { 146 children.Add(child); 147 } 148 149 void DeleteChildren() 150 { 151 while (Attribute* child = children.RemoveHead()) 152 delete child; 153 } 154 }; 155 156 157 // #pragma mark - Entry 158 159 160 struct PackageWriterImpl::Entry : DoublyLinkedListLinkImpl<Entry> { 161 Entry(char* name, size_t nameLength, bool isImplicit) 162 : 163 fName(name), 164 fNameLength(nameLength), 165 fIsImplicit(isImplicit) 166 { 167 } 168 169 ~Entry() 170 { 171 DeleteChildren(); 172 free(fName); 173 } 174 175 static Entry* Create(const char* name, size_t nameLength, bool isImplicit) 176 { 177 char* clonedName = (char*)malloc(nameLength + 1); 178 if (clonedName == NULL) 179 throw std::bad_alloc(); 180 memcpy(clonedName, name, nameLength); 181 clonedName[nameLength] = '\0'; 182 183 Entry* entry = new(std::nothrow) Entry(clonedName, nameLength, 184 isImplicit); 185 if (entry == NULL) { 186 free(clonedName); 187 throw std::bad_alloc(); 188 } 189 190 return entry; 191 } 192 193 const char* Name() const 194 { 195 return fName; 196 } 197 198 bool IsImplicit() const 199 { 200 return fIsImplicit; 201 } 202 203 void SetImplicit(bool isImplicit) 204 { 205 fIsImplicit = isImplicit; 206 } 207 208 bool HasName(const char* name, size_t nameLength) 209 { 210 return nameLength == fNameLength 211 && strncmp(name, fName, nameLength) == 0; 212 } 213 214 void AddChild(Entry* child) 215 { 216 fChildren.Add(child); 217 } 218 219 void DeleteChildren() 220 { 221 while (Entry* child = fChildren.RemoveHead()) 222 delete child; 223 } 224 225 Entry* GetChild(const char* name, size_t nameLength) const 226 { 227 EntryList::ConstIterator it = fChildren.GetIterator(); 228 while (Entry* child = it.Next()) { 229 if (child->HasName(name, nameLength)) 230 return child; 231 } 232 233 return NULL; 234 } 235 236 EntryList::ConstIterator ChildIterator() const 237 { 238 return fChildren.GetIterator(); 239 } 240 241 private: 242 char* fName; 243 size_t fNameLength; 244 bool fIsImplicit; 245 EntryList fChildren; 246 }; 247 248 249 // #pragma mark - SubPathAdder 250 251 252 struct PackageWriterImpl::SubPathAdder { 253 SubPathAdder(char* pathBuffer, const char* subPath) 254 : fOriginalPathEnd(pathBuffer + strlen(pathBuffer)) 255 { 256 strcat(pathBuffer, "/"); 257 strcat(pathBuffer, subPath); 258 } 259 260 ~SubPathAdder() 261 { 262 *fOriginalPathEnd = '\0'; 263 } 264 private: 265 char* fOriginalPathEnd; 266 }; 267 268 269 // #pragma mark - PackageWriterImpl (Inline Methods) 270 271 272 template<typename Type> 273 inline PackageWriterImpl::Attribute* 274 PackageWriterImpl::_AddAttribute(BHPKGAttributeID attributeID, Type value) 275 { 276 AttributeValue attributeValue; 277 attributeValue.SetTo(value); 278 return _AddAttribute(attributeID, attributeValue); 279 } 280 281 282 // #pragma mark - PackageWriterImpl 283 284 285 PackageWriterImpl::PackageWriterImpl(BPackageWriterListener* listener) 286 : 287 inherited(listener), 288 fListener(listener), 289 fDataBuffer(NULL), 290 fDataBufferSize(2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB), 291 fRootEntry(NULL), 292 fRootAttribute(NULL), 293 fTopAttribute(NULL) 294 { 295 } 296 297 298 PackageWriterImpl::~PackageWriterImpl() 299 { 300 delete fRootAttribute; 301 302 delete fRootEntry; 303 304 free(fDataBuffer); 305 } 306 307 308 status_t 309 PackageWriterImpl::Init(const char* fileName) 310 { 311 try { 312 return _Init(fileName); 313 } catch (status_t error) { 314 return error; 315 } catch (std::bad_alloc) { 316 fListener->PrintError("Out of memory!\n"); 317 return B_NO_MEMORY; 318 } 319 } 320 321 322 status_t 323 PackageWriterImpl::AddEntry(const char* fileName) 324 { 325 try { 326 // if it's ".PackageInfo", parse it 327 if (strcmp(fileName, B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) { 328 struct ErrorListener : public BPackageInfo::ParseErrorListener { 329 ErrorListener(BPackageWriterListener* _listener) 330 : listener(_listener) {} 331 virtual void OnError(const BString& msg, int line, int col) { 332 listener->PrintError("Parse error in %s(%d:%d) -> %s\n", 333 B_HPKG_PACKAGE_INFO_FILE_NAME, line, col, msg.String()); 334 } 335 BPackageWriterListener* listener; 336 } errorListener(fListener); 337 BEntry packageInfoEntry(fileName); 338 status_t result = fPackageInfo.ReadFromConfigFile(packageInfoEntry, 339 &errorListener); 340 if (result != B_OK || (result = fPackageInfo.InitCheck()) != B_OK) 341 return result; 342 RegisterPackageInfo(PackageAttributes(), fPackageInfo); 343 } 344 345 return _RegisterEntry(fileName); 346 } catch (status_t error) { 347 return error; 348 } catch (std::bad_alloc) { 349 fListener->PrintError("Out of memory!\n"); 350 return B_NO_MEMORY; 351 } 352 } 353 354 355 status_t 356 PackageWriterImpl::Finish() 357 { 358 try { 359 if (fPackageInfo.InitCheck() != B_OK) { 360 fListener->PrintError("No package-info file found (%s)!\n", 361 B_HPKG_PACKAGE_INFO_FILE_NAME); 362 return B_BAD_DATA; 363 } 364 365 status_t result = _CheckLicenses(); 366 if (result != B_OK) 367 return result; 368 369 return _Finish(); 370 } catch (status_t error) { 371 return error; 372 } catch (std::bad_alloc) { 373 fListener->PrintError("Out of memory!\n"); 374 return B_NO_MEMORY; 375 } 376 } 377 378 379 status_t 380 PackageWriterImpl::_Init(const char* fileName) 381 { 382 status_t result = inherited::Init(fileName, "package"); 383 if (result != B_OK) 384 return result; 385 386 // allocate data buffer 387 fDataBuffer = malloc(fDataBufferSize); 388 if (fDataBuffer == NULL) 389 throw std::bad_alloc(); 390 391 if (fStringCache.Init() != B_OK) 392 throw std::bad_alloc(); 393 394 // create entry list 395 fRootEntry = new Entry(NULL, 0, true); 396 397 fRootAttribute = new Attribute(); 398 399 fHeapOffset = fHeapEnd = sizeof(hpkg_header); 400 fTopAttribute = fRootAttribute; 401 402 return B_OK; 403 } 404 405 406 status_t 407 PackageWriterImpl::_CheckLicenses() 408 { 409 BPath systemLicensePath; 410 status_t result 411 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 412 = find_directory(B_SYSTEM_DATA_DIRECTORY, &systemLicensePath); 413 #else 414 = systemLicensePath.SetTo(HAIKU_BUILD_SYSTEM_DATA_DIRECTORY); 415 #endif 416 if (result != B_OK) { 417 fListener->PrintError("unable to find system data path!\n"); 418 return result; 419 } 420 if ((result = systemLicensePath.Append("licenses")) != B_OK) { 421 fListener->PrintError("unable to append to system data path!\n"); 422 return result; 423 } 424 425 BDirectory systemLicenseDir(systemLicensePath.Path()); 426 BDirectory packageLicenseDir("./data/licenses"); 427 428 const BObjectList<BString>& licenseList = fPackageInfo.LicenseList(); 429 for (int i = 0; i < licenseList.CountItems(); ++i) { 430 const BString& licenseName = *licenseList.ItemAt(i); 431 if (licenseName == kPublicDomainLicenseName) 432 continue; 433 434 BEntry license; 435 if (systemLicenseDir.FindEntry(licenseName.String(), &license) == B_OK) 436 continue; 437 438 // license is not a system license, so it must be contained in package 439 if (packageLicenseDir.FindEntry(licenseName.String(), 440 &license) != B_OK) { 441 fListener->PrintError("License '%s' isn't contained in package!\n", 442 licenseName.String()); 443 return B_BAD_DATA; 444 } 445 } 446 447 return B_OK; 448 } 449 450 451 status_t 452 PackageWriterImpl::_Finish() 453 { 454 // write entries 455 for (EntryList::ConstIterator it = fRootEntry->ChildIterator(); 456 Entry* entry = it.Next();) { 457 char pathBuffer[B_PATH_NAME_LENGTH]; 458 pathBuffer[0] = '\0'; 459 _AddEntry(AT_FDCWD, entry, entry->Name(), pathBuffer); 460 } 461 462 off_t heapSize = fHeapEnd - sizeof(hpkg_header); 463 464 hpkg_header header; 465 466 // write the TOC and package attributes 467 _WriteTOC(header); 468 _WritePackageAttributes(header); 469 470 off_t totalSize = fHeapEnd; 471 472 fListener->OnPackageSizeInfo(sizeof(hpkg_header), heapSize, 473 B_BENDIAN_TO_HOST_INT64(header.toc_length_compressed), 474 B_BENDIAN_TO_HOST_INT32(header.attributes_length_compressed), 475 totalSize); 476 477 // prepare the header 478 479 // general 480 header.magic = B_HOST_TO_BENDIAN_INT32(B_HPKG_MAGIC); 481 header.header_size = B_HOST_TO_BENDIAN_INT16( 482 (uint16)sizeof(hpkg_header)); 483 header.version = B_HOST_TO_BENDIAN_INT16(B_HPKG_VERSION); 484 header.total_size = B_HOST_TO_BENDIAN_INT64(totalSize); 485 486 // write the header 487 WriteBuffer(&header, sizeof(hpkg_header), 0); 488 489 SetFinished(true); 490 return B_OK; 491 } 492 493 494 status_t 495 PackageWriterImpl::_RegisterEntry(const char* fileName) 496 { 497 if (*fileName == '\0') { 498 fListener->PrintError("Invalid empty file name\n"); 499 return B_BAD_VALUE; 500 } 501 502 // add all components of the path 503 Entry* entry = fRootEntry; 504 while (*fileName != 0) { 505 const char* nextSlash = strchr(fileName, '/'); 506 // no slash, just add the file name 507 if (nextSlash == NULL) { 508 entry = _RegisterEntry(entry, fileName, strlen(fileName), false); 509 break; 510 } 511 512 // find the start of the next component, skipping slashes 513 const char* nextComponent = nextSlash + 1; 514 while (*nextComponent == '/') 515 nextComponent++; 516 517 if (nextSlash == fileName) { 518 // the FS root 519 entry = _RegisterEntry(entry, fileName, 1, 520 *nextComponent != '\0'); 521 } else { 522 entry = _RegisterEntry(entry, fileName, nextSlash - fileName, 523 *nextComponent != '\0'); 524 } 525 526 fileName = nextComponent; 527 } 528 529 return B_OK; 530 } 531 532 533 PackageWriterImpl::Entry* 534 PackageWriterImpl::_RegisterEntry(Entry* parent, const char* name, 535 size_t nameLength, bool isImplicit) 536 { 537 // check the component name -- don't allow "." or ".." 538 if (name[0] == '.' 539 && (nameLength == 1 || (nameLength == 2 && name[1] == '.'))) { 540 fListener->PrintError("Invalid file name: \".\" and \"..\" " 541 "are not allowed as path components\n"); 542 throw status_t(B_BAD_VALUE); 543 } 544 545 // the entry might already exist 546 Entry* entry = parent->GetChild(name, nameLength); 547 if (entry != NULL) { 548 // If the entry was implicit and is no longer, we mark it non-implicit 549 // and delete all of it's children. 550 if (entry->IsImplicit() && !isImplicit) { 551 entry->DeleteChildren(); 552 entry->SetImplicit(false); 553 } 554 } else { 555 // nope -- create it 556 entry = Entry::Create(name, nameLength, isImplicit); 557 parent->AddChild(entry); 558 } 559 560 return entry; 561 } 562 563 564 void 565 PackageWriterImpl::_WriteTOC(hpkg_header& header) 566 { 567 // prepare the writer (zlib writer on top of a file writer) 568 off_t startOffset = fHeapEnd; 569 FDDataWriter realWriter(FD(), startOffset, fListener); 570 ZlibDataWriter zlibWriter(&realWriter); 571 SetDataWriter(&zlibWriter); 572 zlibWriter.Init(); 573 574 // write the sections 575 uint64 uncompressedStringsSize; 576 uint64 uncompressedMainSize; 577 int32 cachedStringsWritten 578 = _WriteTOCSections(uncompressedStringsSize, uncompressedMainSize); 579 580 // finish the writer 581 zlibWriter.Finish(); 582 fHeapEnd = realWriter.Offset(); 583 SetDataWriter(NULL); 584 585 off_t endOffset = fHeapEnd; 586 587 fListener->OnTOCSizeInfo(uncompressedStringsSize, uncompressedMainSize, 588 zlibWriter.BytesWritten()); 589 590 // update the header 591 592 // TOC 593 header.toc_compression = B_HOST_TO_BENDIAN_INT32(B_HPKG_COMPRESSION_ZLIB); 594 header.toc_length_compressed = B_HOST_TO_BENDIAN_INT64( 595 endOffset - startOffset); 596 header.toc_length_uncompressed = B_HOST_TO_BENDIAN_INT64( 597 zlibWriter.BytesWritten()); 598 599 // TOC subsections 600 header.toc_strings_length = B_HOST_TO_BENDIAN_INT64( 601 uncompressedStringsSize); 602 header.toc_strings_count = B_HOST_TO_BENDIAN_INT64(cachedStringsWritten); 603 } 604 605 606 int32 607 PackageWriterImpl::_WriteTOCSections(uint64& _stringsSize, uint64& _mainSize) 608 { 609 // write the cached strings 610 uint64 cachedStringsOffset = DataWriter()->BytesWritten(); 611 int32 cachedStringsWritten = WriteCachedStrings(fStringCache, 2); 612 613 // write the main TOC section 614 uint64 mainOffset = DataWriter()->BytesWritten(); 615 _WriteAttributeChildren(fRootAttribute); 616 617 _stringsSize = mainOffset - cachedStringsOffset; 618 _mainSize = DataWriter()->BytesWritten() - mainOffset; 619 620 return cachedStringsWritten; 621 } 622 623 624 void 625 PackageWriterImpl::_WriteAttributeChildren(Attribute* attribute) 626 { 627 DoublyLinkedList<Attribute>::Iterator it 628 = attribute->children.GetIterator(); 629 while (Attribute* child = it.Next()) { 630 // write tag 631 uint8 encoding = child->value.ApplicableEncoding(); 632 WriteUnsignedLEB128(HPKG_ATTRIBUTE_TAG_COMPOSE(child->id, 633 child->value.type, encoding, !child->children.IsEmpty())); 634 635 // write value 636 WriteAttributeValue(child->value, encoding); 637 638 if (!child->children.IsEmpty()) 639 _WriteAttributeChildren(child); 640 } 641 642 WriteUnsignedLEB128(0); 643 } 644 645 646 void 647 PackageWriterImpl::_WritePackageAttributes(hpkg_header& header) 648 { 649 // write the package attributes (zlib writer on top of a file writer) 650 off_t startOffset = fHeapEnd; 651 FDDataWriter realWriter(FD(), startOffset, fListener); 652 ZlibDataWriter zlibWriter(&realWriter); 653 SetDataWriter(&zlibWriter); 654 zlibWriter.Init(); 655 656 // write cached strings and package attributes tree 657 uint32 stringsLengthUncompressed; 658 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 659 stringsLengthUncompressed); 660 661 zlibWriter.Finish(); 662 fHeapEnd = realWriter.Offset(); 663 SetDataWriter(NULL); 664 665 off_t endOffset = fHeapEnd; 666 667 fListener->OnPackageAttributesSizeInfo(stringsCount, 668 zlibWriter.BytesWritten()); 669 670 // update the header 671 header.attributes_compression 672 = B_HOST_TO_BENDIAN_INT32(B_HPKG_COMPRESSION_ZLIB); 673 header.attributes_length_compressed 674 = B_HOST_TO_BENDIAN_INT32(endOffset - startOffset); 675 header.attributes_length_uncompressed 676 = B_HOST_TO_BENDIAN_INT32(zlibWriter.BytesWritten()); 677 header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount); 678 header.attributes_strings_length 679 = B_HOST_TO_BENDIAN_INT32(stringsLengthUncompressed); 680 } 681 682 683 void 684 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName, 685 char* pathBuffer) 686 { 687 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 688 689 SubPathAdder pathAdder(pathBuffer, fileName); 690 if (!isImplicitEntry) { 691 fListener->OnEntryAdded(pathBuffer + 1); 692 // pathBuffer + 1 in order to skip leading slash 693 } 694 695 // open the node 696 int fd = openat(dirFD, fileName, 697 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); 698 if (fd < 0) { 699 fListener->PrintError("Failed to open entry \"%s\": %s\n", fileName, 700 strerror(errno)); 701 throw status_t(errno); 702 } 703 FileDescriptorCloser fdCloser(fd); 704 705 // stat the node 706 struct stat st; 707 if (fstat(fd, &st) < 0) { 708 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", fileName, 709 strerror(errno)); 710 throw status_t(errno); 711 } 712 713 // implicit entries must be directories 714 if (isImplicitEntry && !S_ISDIR(st.st_mode)) { 715 fListener->PrintError("Non-leaf path component \"%s\" is not a " 716 "directory\n", fileName); 717 throw status_t(B_BAD_VALUE); 718 } 719 720 // check/translate the node type 721 uint8 fileType; 722 uint32 defaultPermissions; 723 if (S_ISREG(st.st_mode)) { 724 fileType = B_HPKG_FILE_TYPE_FILE; 725 defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS; 726 } else if (S_ISLNK(st.st_mode)) { 727 fileType = B_HPKG_FILE_TYPE_SYMLINK; 728 defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS; 729 } else if (S_ISDIR(st.st_mode)) { 730 fileType = B_HPKG_FILE_TYPE_DIRECTORY; 731 defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS; 732 } else { 733 // unsupported node type 734 fListener->PrintError("Unsupported node type, entry: \"%s\"\n", 735 fileName); 736 throw status_t(B_UNSUPPORTED); 737 } 738 739 // add attribute entry 740 Attribute* entryAttribute = _AddStringAttribute( 741 B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName); 742 Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute); 743 744 // add stat data 745 if (fileType != B_HPKG_DEFAULT_FILE_TYPE) 746 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType); 747 if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) { 748 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS, 749 uint32(st.st_mode & ALLPERMS)); 750 } 751 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime)); 752 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime)); 753 #ifdef __HAIKU__ 754 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime)); 755 #else 756 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime)); 757 #endif 758 // TODO: File user/group! 759 760 // add file data/symlink path 761 if (S_ISREG(st.st_mode)) { 762 // regular file -- add data 763 if (st.st_size > 0) { 764 BFDDataReader dataReader(fd); 765 status_t error = _AddData(dataReader, st.st_size); 766 if (error != B_OK) 767 throw status_t(error); 768 } 769 } else if (S_ISLNK(st.st_mode)) { 770 // symlink -- add link address 771 char path[B_PATH_NAME_LENGTH + 1]; 772 ssize_t bytesRead = readlinkat(dirFD, fileName, path, 773 B_PATH_NAME_LENGTH); 774 if (bytesRead < 0) { 775 fListener->PrintError("Failed to read symlink \"%s\": %s\n", 776 fileName, strerror(errno)); 777 throw status_t(errno); 778 } 779 780 path[bytesRead] = '\0'; 781 _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path); 782 } 783 784 // add attributes 785 if (DIR* attrDir = fs_fopen_attr_dir(fd)) { 786 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir); 787 788 while (dirent* entry = fs_read_attr_dir(attrDir)) { 789 attr_info attrInfo; 790 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { 791 fListener->PrintError( 792 "Failed to stat attribute \"%s\" of file \"%s\": %s\n", 793 entry->d_name, fileName, strerror(errno)); 794 throw status_t(errno); 795 } 796 797 // create attribute entry 798 Attribute* attributeAttribute = _AddStringAttribute( 799 B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name); 800 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, 801 attributeAttribute); 802 803 // add type 804 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE, 805 (uint32)attrInfo.type); 806 807 // add data 808 BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type); 809 status_t error = _AddData(dataReader, attrInfo.size); 810 if (error != B_OK) 811 throw status_t(error); 812 } 813 } 814 815 if (S_ISDIR(st.st_mode)) { 816 // directory -- recursively add children 817 if (isImplicitEntry) { 818 // this is an implicit entry -- just add it's children 819 for (EntryList::ConstIterator it = entry->ChildIterator(); 820 Entry* child = it.Next();) { 821 _AddEntry(fd, child, child->Name(), pathBuffer); 822 } 823 } else { 824 // we need to clone the directory FD for fdopendir() 825 int clonedFD = dup(fd); 826 if (clonedFD < 0) { 827 fListener->PrintError( 828 "Failed to dup() directory FD: %s\n", strerror(errno)); 829 throw status_t(errno); 830 } 831 832 DIR* dir = fdopendir(clonedFD); 833 if (dir == NULL) { 834 fListener->PrintError( 835 "Failed to open directory \"%s\": %s\n", fileName, 836 strerror(errno)); 837 close(clonedFD); 838 throw status_t(errno); 839 } 840 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 841 842 while (dirent* entry = readdir(dir)) { 843 // skip "." and ".." 844 if (strcmp(entry->d_name, ".") == 0 845 || strcmp(entry->d_name, "..") == 0) { 846 continue; 847 } 848 849 _AddEntry(fd, NULL, entry->d_name, pathBuffer); 850 } 851 } 852 } 853 } 854 855 856 PackageWriterImpl::Attribute* 857 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id, 858 const AttributeValue& value) 859 { 860 Attribute* attribute = new Attribute(id); 861 862 attribute->value = value; 863 fTopAttribute->AddChild(attribute); 864 865 return attribute; 866 } 867 868 869 PackageWriterImpl::Attribute* 870 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID, 871 const char* value) 872 { 873 AttributeValue attributeValue; 874 attributeValue.SetTo(fStringCache.Get(value)); 875 return _AddAttribute(attributeID, attributeValue); 876 } 877 878 879 PackageWriterImpl::Attribute* 880 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 881 uint64 dataSize, uint64 dataOffset) 882 { 883 AttributeValue attributeValue; 884 attributeValue.SetToData(dataSize, dataOffset); 885 return _AddAttribute(attributeID, attributeValue); 886 } 887 888 889 PackageWriterImpl::Attribute* 890 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 891 uint64 dataSize, const uint8* data) 892 { 893 AttributeValue attributeValue; 894 attributeValue.SetToData(dataSize, data); 895 return _AddAttribute(attributeID, attributeValue); 896 } 897 898 899 status_t 900 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size) 901 { 902 // add short data inline 903 if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) { 904 uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE]; 905 status_t error = dataReader.ReadData(0, buffer, size); 906 if (error != B_OK) { 907 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 908 return error; 909 } 910 911 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer); 912 return B_OK; 913 } 914 915 // longer data -- try to compress 916 uint64 dataOffset = fHeapEnd; 917 918 uint64 compression = B_HPKG_COMPRESSION_NONE; 919 uint64 compressedSize; 920 921 status_t error = _WriteZlibCompressedData(dataReader, size, dataOffset, 922 compressedSize); 923 if (error == B_OK) { 924 compression = B_HPKG_COMPRESSION_ZLIB; 925 } else { 926 error = _WriteUncompressedData(dataReader, size, dataOffset); 927 compressedSize = size; 928 } 929 if (error != B_OK) 930 return error; 931 932 fHeapEnd = dataOffset + compressedSize; 933 934 // add data attribute 935 Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, 936 compressedSize, dataOffset - fHeapOffset); 937 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, dataAttribute); 938 939 // if compressed, add compression attributes 940 if (compression != B_HPKG_COMPRESSION_NONE) { 941 _AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION, compression); 942 _AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_SIZE, (uint64)size); 943 // uncompressed size 944 } 945 946 return B_OK; 947 } 948 949 950 status_t 951 PackageWriterImpl::_WriteUncompressedData(BDataReader& dataReader, off_t size, 952 uint64 writeOffset) 953 { 954 // copy the data to the heap 955 off_t readOffset = 0; 956 off_t remainingSize = size; 957 while (remainingSize > 0) { 958 // read data 959 size_t toCopy = std::min(remainingSize, (off_t)fDataBufferSize); 960 status_t error = dataReader.ReadData(readOffset, fDataBuffer, toCopy); 961 if (error != B_OK) { 962 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 963 return error; 964 } 965 966 // write to heap 967 ssize_t bytesWritten = pwrite(FD(), fDataBuffer, toCopy, writeOffset); 968 if (bytesWritten < 0) { 969 fListener->PrintError("Failed to write data: %s\n", 970 strerror(errno)); 971 return errno; 972 } 973 if ((size_t)bytesWritten != toCopy) { 974 fListener->PrintError("Failed to write all data\n"); 975 return B_ERROR; 976 } 977 978 remainingSize -= toCopy; 979 readOffset += toCopy; 980 writeOffset += toCopy; 981 } 982 983 return B_OK; 984 } 985 986 987 status_t 988 PackageWriterImpl::_WriteZlibCompressedData(BDataReader& dataReader, off_t size, 989 uint64 writeOffset, uint64& _compressedSize) 990 { 991 // Use zlib compression only for data large enough. 992 if (size < (off_t)kZlibCompressionSizeThreshold) 993 return B_BAD_VALUE; 994 995 // fDataBuffer is 2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, so split it into 996 // two halves we can use for reading and compressing 997 const size_t chunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB; 998 uint8* inputBuffer = (uint8*)fDataBuffer; 999 uint8* outputBuffer = (uint8*)fDataBuffer + chunkSize; 1000 1001 // account for the offset table 1002 uint64 chunkCount = (size + (chunkSize - 1)) / chunkSize; 1003 off_t offsetTableOffset = writeOffset; 1004 uint64* offsetTable = NULL; 1005 if (chunkCount > 1) { 1006 offsetTable = new uint64[chunkCount - 1]; 1007 writeOffset = offsetTableOffset + (chunkCount - 1) * sizeof(uint64); 1008 } 1009 ArrayDeleter<uint64> offsetTableDeleter(offsetTable); 1010 1011 const uint64 dataOffset = writeOffset; 1012 const uint64 dataEndLimit = offsetTableOffset + size; 1013 1014 // read the data, compress them and write them to the heap 1015 off_t readOffset = 0; 1016 off_t remainingSize = size; 1017 uint64 chunkIndex = 0; 1018 while (remainingSize > 0) { 1019 // read data 1020 size_t toCopy = std::min(remainingSize, (off_t)chunkSize); 1021 status_t error = dataReader.ReadData(readOffset, inputBuffer, toCopy); 1022 if (error != B_OK) { 1023 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1024 return error; 1025 } 1026 1027 // compress 1028 size_t compressedSize; 1029 error = ZlibCompressor::CompressSingleBuffer(inputBuffer, toCopy, 1030 outputBuffer, toCopy, compressedSize); 1031 1032 const void* writeBuffer; 1033 size_t bytesToWrite; 1034 if (error == B_OK) { 1035 writeBuffer = outputBuffer; 1036 bytesToWrite = compressedSize; 1037 } else { 1038 if (error != B_BUFFER_OVERFLOW) 1039 return error; 1040 writeBuffer = inputBuffer; 1041 bytesToWrite = toCopy; 1042 } 1043 1044 // check the total compressed data size 1045 if (writeOffset + bytesToWrite >= dataEndLimit) 1046 return B_BUFFER_OVERFLOW; 1047 1048 if (chunkIndex > 0) 1049 offsetTable[chunkIndex - 1] = writeOffset - dataOffset; 1050 1051 // write to heap 1052 ssize_t bytesWritten = pwrite(FD(), writeBuffer, bytesToWrite, 1053 writeOffset); 1054 if (bytesWritten < 0) { 1055 fListener->PrintError("Failed to write data: %s\n", 1056 strerror(errno)); 1057 return errno; 1058 } 1059 if ((size_t)bytesWritten != bytesToWrite) { 1060 fListener->PrintError("Failed to write all data\n"); 1061 return B_ERROR; 1062 } 1063 1064 remainingSize -= toCopy; 1065 readOffset += toCopy; 1066 writeOffset += bytesToWrite; 1067 chunkIndex++; 1068 } 1069 1070 // write the offset table 1071 if (chunkCount > 1) { 1072 size_t bytesToWrite = (chunkCount - 1) * sizeof(uint64); 1073 ssize_t bytesWritten = pwrite(FD(), offsetTable, bytesToWrite, 1074 offsetTableOffset); 1075 if (bytesWritten < 0) { 1076 fListener->PrintError("Failed to write data: %s\n", 1077 strerror(errno)); 1078 return errno; 1079 } 1080 if ((size_t)bytesWritten != bytesToWrite) { 1081 fListener->PrintError("Failed to write all data\n"); 1082 return B_ERROR; 1083 } 1084 } 1085 1086 _compressedSize = writeOffset - offsetTableOffset; 1087 return B_OK; 1088 } 1089 1090 1091 } // namespace BPrivate 1092 1093 } // namespace BHPKG 1094 1095 } // namespace BPackageKit 1096