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