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