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 644 uint32 compression = B_HPKG_COMPRESSION_ZLIB; 645 uint32 stringsLengthUncompressed; 646 uint32 attributesLengthUncompressed; 647 uint32 stringsCount = _WritePackageAttributesCompressed( 648 stringsLengthUncompressed, attributesLengthUncompressed); 649 650 off_t endOffset = fHeapEnd; 651 652 if ((off_t)attributesLengthUncompressed <= endOffset - startOffset) { 653 // the compressed section isn't shorter -- write uncompressed 654 fHeapEnd = startOffset; 655 compression = B_HPKG_COMPRESSION_NONE; 656 stringsCount = _WritePackageAttributesUncompressed( 657 stringsLengthUncompressed, attributesLengthUncompressed); 658 659 endOffset = fHeapEnd; 660 } 661 662 fListener->OnPackageAttributesSizeInfo(stringsCount, 663 attributesLengthUncompressed); 664 665 // update the header 666 header.attributes_compression = B_HOST_TO_BENDIAN_INT32(compression); 667 header.attributes_length_compressed 668 = B_HOST_TO_BENDIAN_INT32(endOffset - startOffset); 669 header.attributes_length_uncompressed 670 = B_HOST_TO_BENDIAN_INT32(attributesLengthUncompressed); 671 header.attributes_strings_count = B_HOST_TO_BENDIAN_INT32(stringsCount); 672 header.attributes_strings_length 673 = B_HOST_TO_BENDIAN_INT32(stringsLengthUncompressed); 674 } 675 676 677 uint32 678 PackageWriterImpl::_WritePackageAttributesCompressed( 679 uint32& _stringsLengthUncompressed, uint32& _attributesLengthUncompressed) 680 { 681 off_t startOffset = fHeapEnd; 682 FDDataWriter realWriter(FD(), startOffset, fListener); 683 ZlibDataWriter zlibWriter(&realWriter); 684 SetDataWriter(&zlibWriter); 685 zlibWriter.Init(); 686 687 // write cached strings and package attributes tree 688 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 689 _stringsLengthUncompressed); 690 691 zlibWriter.Finish(); 692 fHeapEnd = realWriter.Offset(); 693 SetDataWriter(NULL); 694 695 _attributesLengthUncompressed = zlibWriter.BytesWritten(); 696 return stringsCount; 697 } 698 699 700 uint32 701 PackageWriterImpl::_WritePackageAttributesUncompressed( 702 uint32& _stringsLengthUncompressed, uint32& _attributesLengthUncompressed) 703 { 704 off_t startOffset = fHeapEnd; 705 FDDataWriter realWriter(FD(), startOffset, fListener); 706 707 SetDataWriter(&realWriter); 708 709 // write cached strings and package attributes tree 710 uint32 stringsCount = WritePackageAttributes(PackageAttributes(), 711 _stringsLengthUncompressed); 712 713 fHeapEnd = realWriter.Offset(); 714 SetDataWriter(NULL); 715 716 _attributesLengthUncompressed = realWriter.BytesWritten(); 717 return stringsCount; 718 } 719 720 721 void 722 PackageWriterImpl::_AddEntry(int dirFD, Entry* entry, const char* fileName, 723 char* pathBuffer) 724 { 725 bool isImplicitEntry = entry != NULL && entry->IsImplicit(); 726 727 SubPathAdder pathAdder(fListener, pathBuffer, fileName); 728 if (!isImplicitEntry) 729 fListener->OnEntryAdded(pathBuffer); 730 731 // open the node 732 int fd; 733 FileDescriptorCloser fdCloser; 734 735 if (entry != NULL && entry->FD() >= 0) { 736 // a file descriptor is already given -- use that 737 fd = entry->FD(); 738 } else { 739 fd = openat(dirFD, fileName, 740 O_RDONLY | (isImplicitEntry ? 0 : O_NOTRAVERSE)); 741 if (fd < 0) { 742 fListener->PrintError("Failed to open entry \"%s\": %s\n", 743 pathBuffer, strerror(errno)); 744 throw status_t(errno); 745 } 746 fdCloser.SetTo(fd); 747 } 748 749 // stat the node 750 struct stat st; 751 if (fstat(fd, &st) < 0) { 752 fListener->PrintError("Failed to fstat() file \"%s\": %s\n", pathBuffer, 753 strerror(errno)); 754 throw status_t(errno); 755 } 756 757 // implicit entries must be directories 758 if (isImplicitEntry && !S_ISDIR(st.st_mode)) { 759 fListener->PrintError("Non-leaf path component \"%s\" is not a " 760 "directory\n", pathBuffer); 761 throw status_t(B_BAD_VALUE); 762 } 763 764 // check/translate the node type 765 uint8 fileType; 766 uint32 defaultPermissions; 767 if (S_ISREG(st.st_mode)) { 768 fileType = B_HPKG_FILE_TYPE_FILE; 769 defaultPermissions = B_HPKG_DEFAULT_FILE_PERMISSIONS; 770 } else if (S_ISLNK(st.st_mode)) { 771 fileType = B_HPKG_FILE_TYPE_SYMLINK; 772 defaultPermissions = B_HPKG_DEFAULT_SYMLINK_PERMISSIONS; 773 } else if (S_ISDIR(st.st_mode)) { 774 fileType = B_HPKG_FILE_TYPE_DIRECTORY; 775 defaultPermissions = B_HPKG_DEFAULT_DIRECTORY_PERMISSIONS; 776 } else { 777 // unsupported node type 778 fListener->PrintError("Unsupported node type, entry: \"%s\"\n", 779 pathBuffer); 780 throw status_t(B_UNSUPPORTED); 781 } 782 783 // add attribute entry 784 Attribute* entryAttribute = _AddStringAttribute( 785 B_HPKG_ATTRIBUTE_ID_DIRECTORY_ENTRY, fileName); 786 Stacker<Attribute> entryAttributeStacker(fTopAttribute, entryAttribute); 787 788 // add stat data 789 if (fileType != B_HPKG_DEFAULT_FILE_TYPE) 790 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_TYPE, fileType); 791 if (defaultPermissions != uint32(st.st_mode & ALLPERMS)) { 792 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_PERMISSIONS, 793 uint32(st.st_mode & ALLPERMS)); 794 } 795 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATIME, uint32(st.st_atime)); 796 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_MTIME, uint32(st.st_mtime)); 797 #ifdef __HAIKU__ 798 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_crtime)); 799 #else 800 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_CRTIME, uint32(st.st_mtime)); 801 #endif 802 // TODO: File user/group! 803 804 // add file data/symlink path 805 if (S_ISREG(st.st_mode)) { 806 // regular file -- add data 807 if (st.st_size > 0) { 808 BFDDataReader dataReader(fd); 809 status_t error = _AddData(dataReader, st.st_size); 810 if (error != B_OK) 811 throw status_t(error); 812 } 813 } else if (S_ISLNK(st.st_mode)) { 814 // symlink -- add link address 815 char path[B_PATH_NAME_LENGTH + 1]; 816 ssize_t bytesRead = readlinkat(dirFD, fileName, path, 817 B_PATH_NAME_LENGTH); 818 if (bytesRead < 0) { 819 fListener->PrintError("Failed to read symlink \"%s\": %s\n", 820 pathBuffer, strerror(errno)); 821 throw status_t(errno); 822 } 823 824 path[bytesRead] = '\0'; 825 _AddStringAttribute(B_HPKG_ATTRIBUTE_ID_SYMLINK_PATH, path); 826 } 827 828 // add attributes 829 if (DIR* attrDir = fs_fopen_attr_dir(fd)) { 830 CObjectDeleter<DIR, int> attrDirCloser(attrDir, fs_close_attr_dir); 831 832 while (dirent* entry = fs_read_attr_dir(attrDir)) { 833 attr_info attrInfo; 834 if (fs_stat_attr(fd, entry->d_name, &attrInfo) < 0) { 835 fListener->PrintError( 836 "Failed to stat attribute \"%s\" of file \"%s\": %s\n", 837 entry->d_name, pathBuffer, strerror(errno)); 838 throw status_t(errno); 839 } 840 841 // create attribute entry 842 Attribute* attributeAttribute = _AddStringAttribute( 843 B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE, entry->d_name); 844 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, 845 attributeAttribute); 846 847 // add type 848 _AddAttribute(B_HPKG_ATTRIBUTE_ID_FILE_ATTRIBUTE_TYPE, 849 (uint32)attrInfo.type); 850 851 // add data 852 BAttributeDataReader dataReader(fd, entry->d_name, attrInfo.type); 853 status_t error = _AddData(dataReader, attrInfo.size); 854 if (error != B_OK) 855 throw status_t(error); 856 } 857 } 858 859 if (S_ISDIR(st.st_mode)) { 860 // directory -- recursively add children 861 if (isImplicitEntry) { 862 // this is an implicit entry -- just add it's children 863 for (EntryList::ConstIterator it = entry->ChildIterator(); 864 Entry* child = it.Next();) { 865 _AddEntry(fd, child, child->Name(), pathBuffer); 866 } 867 } else { 868 // we need to clone the directory FD for fdopendir() 869 int clonedFD = dup(fd); 870 if (clonedFD < 0) { 871 fListener->PrintError( 872 "Failed to dup() directory FD: %s\n", strerror(errno)); 873 throw status_t(errno); 874 } 875 876 DIR* dir = fdopendir(clonedFD); 877 if (dir == NULL) { 878 fListener->PrintError( 879 "Failed to open directory \"%s\": %s\n", pathBuffer, 880 strerror(errno)); 881 close(clonedFD); 882 throw status_t(errno); 883 } 884 CObjectDeleter<DIR, int> dirCloser(dir, closedir); 885 886 while (dirent* entry = readdir(dir)) { 887 // skip "." and ".." 888 if (strcmp(entry->d_name, ".") == 0 889 || strcmp(entry->d_name, "..") == 0) { 890 continue; 891 } 892 893 _AddEntry(fd, NULL, entry->d_name, pathBuffer); 894 } 895 } 896 } 897 } 898 899 900 PackageWriterImpl::Attribute* 901 PackageWriterImpl::_AddAttribute(BHPKGAttributeID id, 902 const AttributeValue& value) 903 { 904 Attribute* attribute = new Attribute(id); 905 906 attribute->value = value; 907 fTopAttribute->AddChild(attribute); 908 909 return attribute; 910 } 911 912 913 PackageWriterImpl::Attribute* 914 PackageWriterImpl::_AddStringAttribute(BHPKGAttributeID attributeID, 915 const char* value) 916 { 917 AttributeValue attributeValue; 918 attributeValue.SetTo(fStringCache.Get(value)); 919 return _AddAttribute(attributeID, attributeValue); 920 } 921 922 923 PackageWriterImpl::Attribute* 924 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 925 uint64 dataSize, uint64 dataOffset) 926 { 927 AttributeValue attributeValue; 928 attributeValue.SetToData(dataSize, dataOffset); 929 return _AddAttribute(attributeID, attributeValue); 930 } 931 932 933 PackageWriterImpl::Attribute* 934 PackageWriterImpl::_AddDataAttribute(BHPKGAttributeID attributeID, 935 uint64 dataSize, const uint8* data) 936 { 937 AttributeValue attributeValue; 938 attributeValue.SetToData(dataSize, data); 939 return _AddAttribute(attributeID, attributeValue); 940 } 941 942 943 status_t 944 PackageWriterImpl::_AddData(BDataReader& dataReader, off_t size) 945 { 946 // add short data inline 947 if (size <= B_HPKG_MAX_INLINE_DATA_SIZE) { 948 uint8 buffer[B_HPKG_MAX_INLINE_DATA_SIZE]; 949 status_t error = dataReader.ReadData(0, buffer, size); 950 if (error != B_OK) { 951 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 952 return error; 953 } 954 955 _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, size, buffer); 956 return B_OK; 957 } 958 959 // longer data -- try to compress 960 uint64 dataOffset = fHeapEnd; 961 962 uint64 compression = B_HPKG_COMPRESSION_NONE; 963 uint64 compressedSize; 964 965 status_t error = _WriteZlibCompressedData(dataReader, size, dataOffset, 966 compressedSize); 967 if (error == B_OK) { 968 compression = B_HPKG_COMPRESSION_ZLIB; 969 } else { 970 error = _WriteUncompressedData(dataReader, size, dataOffset); 971 compressedSize = size; 972 } 973 if (error != B_OK) 974 return error; 975 976 fHeapEnd = dataOffset + compressedSize; 977 978 // add data attribute 979 Attribute* dataAttribute = _AddDataAttribute(B_HPKG_ATTRIBUTE_ID_DATA, 980 compressedSize, dataOffset - fHeapOffset); 981 Stacker<Attribute> attributeAttributeStacker(fTopAttribute, dataAttribute); 982 983 // if compressed, add compression attributes 984 if (compression != B_HPKG_COMPRESSION_NONE) { 985 _AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_COMPRESSION, compression); 986 _AddAttribute(B_HPKG_ATTRIBUTE_ID_DATA_SIZE, (uint64)size); 987 // uncompressed size 988 } 989 990 return B_OK; 991 } 992 993 994 status_t 995 PackageWriterImpl::_WriteUncompressedData(BDataReader& dataReader, off_t size, 996 uint64 writeOffset) 997 { 998 // copy the data to the heap 999 off_t readOffset = 0; 1000 off_t remainingSize = size; 1001 while (remainingSize > 0) { 1002 // read data 1003 size_t toCopy = std::min(remainingSize, (off_t)fDataBufferSize); 1004 status_t error = dataReader.ReadData(readOffset, fDataBuffer, toCopy); 1005 if (error != B_OK) { 1006 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1007 return error; 1008 } 1009 1010 // write to heap 1011 ssize_t bytesWritten = pwrite(FD(), fDataBuffer, toCopy, writeOffset); 1012 if (bytesWritten < 0) { 1013 fListener->PrintError("Failed to write data: %s\n", 1014 strerror(errno)); 1015 return errno; 1016 } 1017 if ((size_t)bytesWritten != toCopy) { 1018 fListener->PrintError("Failed to write all data\n"); 1019 return B_ERROR; 1020 } 1021 1022 remainingSize -= toCopy; 1023 readOffset += toCopy; 1024 writeOffset += toCopy; 1025 } 1026 1027 return B_OK; 1028 } 1029 1030 1031 status_t 1032 PackageWriterImpl::_WriteZlibCompressedData(BDataReader& dataReader, off_t size, 1033 uint64 writeOffset, uint64& _compressedSize) 1034 { 1035 // Use zlib compression only for data large enough. 1036 if (size < (off_t)kZlibCompressionSizeThreshold) 1037 return B_BAD_VALUE; 1038 1039 // fDataBuffer is 2 * B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB, so split it into 1040 // two halves we can use for reading and compressing 1041 const size_t chunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB; 1042 uint8* inputBuffer = (uint8*)fDataBuffer; 1043 uint8* outputBuffer = (uint8*)fDataBuffer + chunkSize; 1044 1045 // account for the offset table 1046 uint64 chunkCount = (size + (chunkSize - 1)) / chunkSize; 1047 off_t offsetTableOffset = writeOffset; 1048 uint64* offsetTable = NULL; 1049 if (chunkCount > 1) { 1050 offsetTable = new uint64[chunkCount - 1]; 1051 writeOffset = offsetTableOffset + (chunkCount - 1) * sizeof(uint64); 1052 } 1053 ArrayDeleter<uint64> offsetTableDeleter(offsetTable); 1054 1055 const uint64 dataOffset = writeOffset; 1056 const uint64 dataEndLimit = offsetTableOffset + size; 1057 1058 // read the data, compress them and write them to the heap 1059 off_t readOffset = 0; 1060 off_t remainingSize = size; 1061 uint64 chunkIndex = 0; 1062 while (remainingSize > 0) { 1063 // read data 1064 size_t toCopy = std::min(remainingSize, (off_t)chunkSize); 1065 status_t error = dataReader.ReadData(readOffset, inputBuffer, toCopy); 1066 if (error != B_OK) { 1067 fListener->PrintError("Failed to read data: %s\n", strerror(error)); 1068 return error; 1069 } 1070 1071 // compress 1072 size_t compressedSize; 1073 error = ZlibCompressor::CompressSingleBuffer(inputBuffer, toCopy, 1074 outputBuffer, toCopy, compressedSize); 1075 1076 const void* writeBuffer; 1077 size_t bytesToWrite; 1078 if (error == B_OK) { 1079 writeBuffer = outputBuffer; 1080 bytesToWrite = compressedSize; 1081 } else { 1082 if (error != B_BUFFER_OVERFLOW) 1083 return error; 1084 writeBuffer = inputBuffer; 1085 bytesToWrite = toCopy; 1086 } 1087 1088 // check the total compressed data size 1089 if (writeOffset + bytesToWrite >= dataEndLimit) 1090 return B_BUFFER_OVERFLOW; 1091 1092 if (chunkIndex > 0) 1093 offsetTable[chunkIndex - 1] = writeOffset - dataOffset; 1094 1095 // write to heap 1096 ssize_t bytesWritten = pwrite(FD(), writeBuffer, bytesToWrite, 1097 writeOffset); 1098 if (bytesWritten < 0) { 1099 fListener->PrintError("Failed to write data: %s\n", 1100 strerror(errno)); 1101 return errno; 1102 } 1103 if ((size_t)bytesWritten != bytesToWrite) { 1104 fListener->PrintError("Failed to write all data\n"); 1105 return B_ERROR; 1106 } 1107 1108 remainingSize -= toCopy; 1109 readOffset += toCopy; 1110 writeOffset += bytesToWrite; 1111 chunkIndex++; 1112 } 1113 1114 // write the offset table 1115 if (chunkCount > 1) { 1116 size_t bytesToWrite = (chunkCount - 1) * sizeof(uint64); 1117 ssize_t bytesWritten = pwrite(FD(), offsetTable, bytesToWrite, 1118 offsetTableOffset); 1119 if (bytesWritten < 0) { 1120 fListener->PrintError("Failed to write data: %s\n", 1121 strerror(errno)); 1122 return errno; 1123 } 1124 if ((size_t)bytesWritten != bytesToWrite) { 1125 fListener->PrintError("Failed to write all data\n"); 1126 return B_ERROR; 1127 } 1128 } 1129 1130 _compressedSize = writeOffset - offsetTableOffset; 1131 return B_OK; 1132 } 1133 1134 1135 } // namespace BPrivate 1136 1137 } // namespace BHPKG 1138 1139 } // namespace BPackageKit 1140