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