1 /* 2 * Copyright 2009-2013, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <ctype.h> 8 #include <fcntl.h> 9 #include <errno.h> 10 #include <getopt.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/stat.h> 15 #include <unistd.h> 16 17 #include <algorithm> 18 #include <new> 19 20 #include <fs_attr.h> 21 #include <String.h> 22 23 #include <AutoDeleter.h> 24 #include <HashString.h> 25 26 #include <util/OpenHashTable.h> 27 28 #include <package/hpkg/BlockBufferPoolNoLock.h> 29 #include <package/hpkg/PackageContentHandler.h> 30 #include <package/hpkg/PackageDataReader.h> 31 #include <package/hpkg/PackageEntry.h> 32 #include <package/hpkg/PackageEntryAttribute.h> 33 #include <package/hpkg/PackageReader.h> 34 #include <package/hpkg/StandardErrorOutput.h> 35 #include <package/hpkg/v1/PackageContentHandler.h> 36 #include <package/hpkg/v1/PackageDataReader.h> 37 #include <package/hpkg/v1/PackageEntry.h> 38 #include <package/hpkg/v1/PackageEntryAttribute.h> 39 #include <package/hpkg/v1/PackageReader.h> 40 41 #include "package.h" 42 43 44 using BPackageKit::BHPKG::BAbstractBufferedDataReader; 45 using BPackageKit::BHPKG::BBlockBufferPoolNoLock; 46 using BPackageKit::BHPKG::BBufferDataReader; 47 using BPackageKit::BHPKG::BBufferPool; 48 using BPackageKit::BHPKG::BDataReader; 49 using BPackageKit::BHPKG::BErrorOutput; 50 using BPackageKit::BHPKG::BFDDataReader; 51 using BPackageKit::BHPKG::BPackageInfoAttributeValue; 52 using BPackageKit::BHPKG::BStandardErrorOutput; 53 54 55 struct VersionPolicyV1 { 56 typedef BPackageKit::BHPKG::V1::BPackageContentHandler 57 PackageContentHandler; 58 typedef BPackageKit::BHPKG::V1::BPackageData PackageData; 59 typedef BPackageKit::BHPKG::V1::BPackageEntry PackageEntry; 60 typedef BPackageKit::BHPKG::V1::BPackageEntryAttribute 61 PackageEntryAttribute; 62 typedef BPackageKit::BHPKG::V1::BPackageReader PackageReader; 63 typedef BDataReader HeapReaderBase; 64 65 static inline size_t BufferSize() 66 { 67 return BPackageKit::BHPKG::V1::B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB; 68 } 69 70 static inline const char* PackageInfoFileName() 71 { 72 return BPackageKit::BHPKG::V1::B_HPKG_PACKAGE_INFO_FILE_NAME; 73 } 74 75 static inline uint64 PackageDataCompressedSize(const PackageData& data) 76 { 77 return data.CompressedSize(); 78 } 79 80 static inline uint64 PackageDataUncompressedSize(const PackageData& data) 81 { 82 return data.UncompressedSize(); 83 } 84 85 static inline status_t InitReader(PackageReader& packageReader, 86 const char* fileName) 87 { 88 return packageReader.Init(fileName); 89 } 90 91 static status_t GetHeapReader(PackageReader& packageReader, 92 HeapReaderBase*& _heapReader, bool& _mustDelete) 93 { 94 _heapReader = new(std::nothrow) BFDDataReader( 95 packageReader.PackageFileFD()); 96 _mustDelete = false; 97 return _heapReader != NULL ? B_OK : B_NO_MEMORY; 98 } 99 100 static status_t CreatePackageDataReader(BBufferPool* bufferPool, 101 HeapReaderBase* heapReader, const PackageData& data, 102 BAbstractBufferedDataReader*& _reader) 103 { 104 return BPackageKit::BHPKG::V1::BPackageDataReaderFactory(bufferPool) 105 .CreatePackageDataReader(heapReader, data, _reader); 106 } 107 }; 108 109 struct VersionPolicyV2 { 110 typedef BPackageKit::BHPKG::BPackageContentHandler PackageContentHandler; 111 typedef BPackageKit::BHPKG::BPackageData PackageData; 112 typedef BPackageKit::BHPKG::BPackageEntry PackageEntry; 113 typedef BPackageKit::BHPKG::BPackageEntryAttribute PackageEntryAttribute; 114 typedef BPackageKit::BHPKG::BPackageReader PackageReader; 115 typedef BAbstractBufferedDataReader HeapReaderBase; 116 117 static inline size_t BufferSize() 118 { 119 return 64 * 1024; 120 } 121 122 static inline const char* PackageInfoFileName() 123 { 124 return BPackageKit::BHPKG::B_HPKG_PACKAGE_INFO_FILE_NAME; 125 } 126 127 static inline uint64 PackageDataCompressedSize(const PackageData& data) 128 { 129 return data.Size(); 130 } 131 132 static inline uint64 PackageDataUncompressedSize(const PackageData& data) 133 { 134 return data.Size(); 135 } 136 137 static inline status_t InitReader(PackageReader& packageReader, 138 const char* fileName) 139 { 140 return packageReader.Init(fileName, 141 BPackageKit::BHPKG 142 ::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE); 143 } 144 145 static status_t GetHeapReader(PackageReader& packageReader, 146 HeapReaderBase*& _heapReader, bool& _mustDelete) 147 { 148 _heapReader = packageReader.HeapReader(); 149 _mustDelete = false; 150 return B_OK; 151 } 152 153 static status_t CreatePackageDataReader(BBufferPool* bufferPool, 154 HeapReaderBase* heapReader, const PackageData& data, 155 BAbstractBufferedDataReader*& _reader) 156 { 157 return BPackageKit::BHPKG::BPackageDataReaderFactory() 158 .CreatePackageDataReader(heapReader, data, _reader); 159 } 160 }; 161 162 163 struct Entry { 164 Entry(Entry* parent, char* name, bool implicit) 165 : 166 fParent(parent), 167 fName(name), 168 fImplicit(implicit), 169 fSeen(false) 170 { 171 } 172 173 ~Entry() 174 { 175 _DeleteChildren(); 176 177 free(fName); 178 } 179 180 status_t Init() 181 { 182 return fChildren.Init(); 183 } 184 185 static status_t Create(Entry* parent, const char* name, bool implicit, 186 Entry*& _entry) 187 { 188 if (parent != NULL) { 189 Entry* entryInParent = parent->FindChild(name); 190 if (entryInParent != NULL) { 191 _entry = entryInParent; 192 return B_OK; 193 } 194 } 195 196 char* clonedName = strdup(name); 197 if (clonedName == NULL) 198 return B_NO_MEMORY; 199 200 Entry* entry = new(std::nothrow) Entry(parent, clonedName, implicit); 201 if (entry == NULL) { 202 free(clonedName); 203 return B_NO_MEMORY; 204 } 205 206 status_t error = entry->Init(); 207 if (error != B_OK) { 208 delete entry; 209 return error; 210 } 211 212 if (parent != NULL) 213 parent->fChildren.Insert(entry); 214 215 _entry = entry; 216 return B_OK; 217 } 218 219 Entry* Parent() const 220 { 221 return fParent; 222 } 223 224 const char* Name() const 225 { 226 return fName; 227 } 228 229 bool IsImplicit() const 230 { 231 return fImplicit; 232 } 233 234 void SetExplicit() 235 { 236 // remove all children and set this entry non-implicit 237 _DeleteChildren(); 238 fImplicit = false; 239 } 240 241 void SetSeen() 242 { 243 fSeen = true; 244 } 245 246 bool Seen() const 247 { 248 return fSeen; 249 } 250 251 Entry* FindChild(const char* name) const 252 { 253 return fChildren.Lookup(name); 254 } 255 256 private: 257 struct ChildHashDefinition { 258 typedef const char* KeyType; 259 typedef Entry ValueType; 260 261 size_t HashKey(const char* key) const 262 { 263 return BString::HashValue(key); 264 } 265 266 size_t Hash(const Entry* value) const 267 { 268 return HashKey(value->Name()); 269 } 270 271 bool Compare(const char* key, const Entry* value) const 272 { 273 return strcmp(value->Name(), key) == 0; 274 } 275 276 Entry*& GetLink(Entry* value) const 277 { 278 return value->fHashTableNext; 279 } 280 }; 281 282 typedef BOpenHashTable<ChildHashDefinition> ChildTable; 283 284 private: 285 void _DeleteChildren() 286 { 287 Entry* child = fChildren.Clear(true); 288 while (child != NULL) { 289 Entry* next = child->fHashTableNext; 290 delete child; 291 child = next; 292 } 293 } 294 295 public: 296 Entry* fHashTableNext; 297 298 private: 299 Entry* fParent; 300 char* fName; 301 bool fImplicit; 302 bool fSeen; 303 ChildTable fChildren; 304 }; 305 306 307 template<typename VersionPolicy> 308 struct PackageContentExtractHandler : VersionPolicy::PackageContentHandler { 309 PackageContentExtractHandler(BBufferPool* bufferPool, 310 typename VersionPolicy::HeapReaderBase* heapReader) 311 : 312 fBufferPool(bufferPool), 313 fPackageFileReader(heapReader), 314 fDataBuffer(NULL), 315 fDataBufferSize(0), 316 fRootFilterEntry(NULL, NULL, true), 317 fBaseDirectory(AT_FDCWD), 318 fInfoFileName(NULL), 319 fErrorOccurred(false) 320 { 321 } 322 323 ~PackageContentExtractHandler() 324 { 325 free(fDataBuffer); 326 } 327 328 status_t Init() 329 { 330 status_t error = fRootFilterEntry.Init(); 331 if (error != B_OK) 332 return error; 333 334 fDataBufferSize = 64 * 1024; 335 fDataBuffer = malloc(fDataBufferSize); 336 if (fDataBuffer == NULL) 337 return B_NO_MEMORY; 338 339 return B_OK; 340 } 341 342 void SetBaseDirectory(int fd) 343 { 344 fBaseDirectory = fd; 345 } 346 347 void SetPackageInfoFile(const char* infoFileName) 348 { 349 fInfoFileName = infoFileName; 350 } 351 352 void SetExtractAll() 353 { 354 fRootFilterEntry.SetExplicit(); 355 } 356 357 status_t AddFilterEntry(const char* fileName) 358 { 359 // add all components of the path 360 Entry* entry = &fRootFilterEntry; 361 while (*fileName != 0) { 362 const char* nextSlash = strchr(fileName, '/'); 363 // no slash, just add the file name 364 if (nextSlash == NULL) { 365 return _AddFilterEntry(entry, fileName, strlen(fileName), 366 false, entry); 367 } 368 369 // find the start of the next component, skipping slashes 370 const char* nextComponent = nextSlash + 1; 371 while (*nextComponent == '/') 372 nextComponent++; 373 374 status_t error = _AddFilterEntry(entry, fileName, 375 nextSlash - fileName, *nextComponent != '\0', entry); 376 if (error != B_OK) 377 return error; 378 379 fileName = nextComponent; 380 } 381 382 return B_OK; 383 } 384 385 Entry* FindFilterEntry(const char* fileName) 386 { 387 // add all components of the path 388 Entry* entry = &fRootFilterEntry; 389 while (entry != NULL && *fileName != 0) { 390 const char* nextSlash = strchr(fileName, '/'); 391 // no slash, just add the file name 392 if (nextSlash == NULL) 393 return entry->FindChild(fileName); 394 395 // find the start of the next component, skipping slashes 396 const char* nextComponent = nextSlash + 1; 397 while (*nextComponent == '/') 398 nextComponent++; 399 400 BString componentName(fileName, nextSlash - fileName); 401 entry = entry->FindChild(componentName); 402 403 fileName = nextComponent; 404 } 405 406 return entry; 407 } 408 409 virtual status_t HandleEntry(typename VersionPolicy::PackageEntry* entry) 410 { 411 // create a token 412 Token* token = new(std::nothrow) Token; 413 if (token == NULL) 414 return B_NO_MEMORY; 415 ObjectDeleter<Token> tokenDeleter(token); 416 417 // check whether this entry shall be ignored or is implicit 418 Entry* parentFilterEntry; 419 bool implicit; 420 if (entry->Parent() != NULL) { 421 Token* parentToken = (Token*)entry->Parent()->UserToken(); 422 if (parentToken == NULL) { 423 // parent is ignored, so ignore this entry, too 424 return B_OK; 425 } 426 427 parentFilterEntry = parentToken->filterEntry; 428 implicit = parentToken->implicit; 429 } else { 430 parentFilterEntry = &fRootFilterEntry; 431 implicit = fRootFilterEntry.IsImplicit(); 432 } 433 434 Entry* filterEntry = parentFilterEntry != NULL 435 ? parentFilterEntry->FindChild(entry->Name()) : NULL; 436 437 if (implicit && filterEntry == NULL) { 438 // parent is implicit and the filter doesn't include this entry 439 // -- ignore it 440 return B_OK; 441 } 442 443 // If the entry is in the filter, get its implicit flag. 444 if (filterEntry != NULL) { 445 implicit = filterEntry->IsImplicit(); 446 filterEntry->SetSeen(); 447 } 448 449 token->filterEntry = filterEntry; 450 token->implicit = implicit; 451 452 // get parent FD and the entry name 453 int parentFD; 454 const char* entryName; 455 _GetParentFDAndEntryName(entry, parentFD, entryName); 456 457 // check whether something is in the way 458 struct stat st; 459 bool entryExists = fstatat(parentFD, entryName, &st, 460 AT_SYMLINK_NOFOLLOW) == 0; 461 if (entryExists) { 462 if (S_ISREG(entry->Mode()) || S_ISLNK(entry->Mode())) { 463 // If the entry in the way is a regular file or a symlink, 464 // remove it, otherwise fail. 465 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) { 466 fprintf(stderr, "Error: Can't create entry \"%s\", since " 467 "something is in the way\n", 468 _EntryPath(entry).String()); 469 return B_FILE_EXISTS; 470 } 471 472 if (unlinkat(parentFD, entryName, 0) != 0) { 473 fprintf(stderr, "Error: Failed to unlink entry \"%s\": %s\n", 474 _EntryPath(entry).String(), strerror(errno)); 475 return errno; 476 } 477 478 entryExists = false; 479 } else if (S_ISDIR(entry->Mode())) { 480 // If the entry in the way is a directory, merge, otherwise 481 // fail. 482 if (!S_ISDIR(st.st_mode)) { 483 fprintf(stderr, "Error: Can't create directory \"%s\", " 484 "since something is in the way\n", 485 _EntryPath(entry).String()); 486 return B_FILE_EXISTS; 487 } 488 } 489 } 490 491 // create the entry 492 int fd = -1; 493 if (S_ISREG(entry->Mode())) { 494 if (implicit) { 495 fprintf(stderr, "Error: File \"%s\" was specified as a " 496 "path directory component.\n", _EntryPath(entry).String()); 497 return B_BAD_VALUE; 498 } 499 500 // create the file 501 fd = openat(parentFD, entryName, O_RDWR | O_CREAT | O_EXCL, 502 S_IRUSR | S_IWUSR); 503 // Note: We use read+write user permissions now -- so write 504 // operations (e.g. attributes) won't fail, but set them to the 505 // desired ones in HandleEntryDone(). 506 if (fd < 0) { 507 fprintf(stderr, "Error: Failed to create file \"%s\": %s\n", 508 _EntryPath(entry).String(), strerror(errno)); 509 return errno; 510 } 511 512 // write data 513 status_t error = _ExtractFileData(fPackageFileReader, entry->Data(), 514 fd); 515 if (error != B_OK) 516 return error; 517 } else if (S_ISLNK(entry->Mode())) { 518 if (implicit) { 519 fprintf(stderr, "Error: Symlink \"%s\" was specified as a " 520 "path directory component.\n", _EntryPath(entry).String()); 521 return B_BAD_VALUE; 522 } 523 524 // create the symlink 525 const char* symlinkPath = entry->SymlinkPath(); 526 if (symlinkat(symlinkPath != NULL ? symlinkPath : "", parentFD, 527 entryName) != 0) { 528 fprintf(stderr, "Error: Failed to create symlink \"%s\": %s\n", 529 _EntryPath(entry).String(), strerror(errno)); 530 return errno; 531 } 532 // TODO: Set symlink permissions? 533 } else if (S_ISDIR(entry->Mode())) { 534 // create the directory, if necessary 535 if (!entryExists 536 && mkdirat(parentFD, entryName, S_IRWXU) != 0) { 537 // Note: We use read+write+exec user permissions now -- so write 538 // operations (e.g. attributes) won't fail, but set them to the 539 // desired ones in HandleEntryDone(). 540 fprintf(stderr, "Error: Failed to create directory \"%s\": " 541 "%s\n", _EntryPath(entry).String(), strerror(errno)); 542 return errno; 543 } 544 } else { 545 fprintf(stderr, "Error: Invalid file type for entry \"%s\"\n", 546 _EntryPath(entry).String()); 547 return B_BAD_DATA; 548 } 549 550 // If not done yet (symlink, dir), open the node -- we need the FD. 551 if (fd < 0 && (!implicit || S_ISDIR(entry->Mode()))) { 552 fd = openat(parentFD, entryName, O_RDONLY | O_NOTRAVERSE); 553 if (fd < 0) { 554 fprintf(stderr, "Error: Failed to open entry \"%s\": %s\n", 555 _EntryPath(entry).String(), strerror(errno)); 556 return errno; 557 } 558 } 559 token->fd = fd; 560 561 // set the file times 562 if (!entryExists && !implicit) { 563 timespec times[2] = {entry->AccessTime(), entry->ModifiedTime()}; 564 futimens(fd, times); 565 566 // set user/group 567 // TODO:... 568 } 569 570 entry->SetUserToken(tokenDeleter.Detach()); 571 return B_OK; 572 } 573 574 virtual status_t HandleEntryAttribute( 575 typename VersionPolicy::PackageEntry* entry, 576 typename VersionPolicy::PackageEntryAttribute* attribute) 577 { 578 // don't write attributes of ignored or implicit entries 579 Token* token = (Token*)entry->UserToken(); 580 if (token == NULL || token->implicit) 581 return B_OK; 582 583 int entryFD = token->fd; 584 585 // create the attribute 586 int fd = fs_fopen_attr(entryFD, attribute->Name(), attribute->Type(), 587 O_WRONLY | O_CREAT | O_TRUNC); 588 if (fd < 0) { 589 int parentFD; 590 const char* entryName; 591 _GetParentFDAndEntryName(entry, parentFD, entryName); 592 593 fprintf(stderr, "Error: Failed to create attribute \"%s\" of " 594 "file \"%s\": %s\n", attribute->Name(), 595 _EntryPath(entry).String(), strerror(errno)); 596 return errno; 597 } 598 599 // write data 600 status_t error = _ExtractFileData(fPackageFileReader, attribute->Data(), 601 fd); 602 603 fs_close_attr(fd); 604 605 return error; 606 } 607 608 virtual status_t HandleEntryDone( 609 typename VersionPolicy::PackageEntry* entry) 610 { 611 Token* token = (Token*)entry->UserToken(); 612 613 // set the node permissions for non-symlinks 614 if (token != NULL && !S_ISLNK(entry->Mode())) { 615 // get parent FD and entry name 616 int parentFD; 617 const char* entryName; 618 _GetParentFDAndEntryName(entry, parentFD, entryName); 619 620 if (fchmodat(parentFD, entryName, entry->Mode() & ALLPERMS, 621 /*AT_SYMLINK_NOFOLLOW*/0) != 0) { 622 fprintf(stderr, "Warning: Failed to set permissions of file " 623 "\"%s\": %s\n", _EntryPath(entry).String(), 624 strerror(errno)); 625 } 626 } 627 628 if (token != NULL) { 629 delete token; 630 entry->SetUserToken(NULL); 631 } 632 633 return B_OK; 634 } 635 636 virtual status_t HandlePackageAttribute( 637 const BPackageInfoAttributeValue& value) 638 { 639 return B_OK; 640 } 641 642 virtual void HandleErrorOccurred() 643 { 644 fErrorOccurred = true; 645 } 646 647 private: 648 struct Token { 649 Entry* filterEntry; 650 int fd; 651 bool implicit; 652 653 Token() 654 : 655 filterEntry(NULL), 656 fd(-1), 657 implicit(true) 658 { 659 } 660 661 ~Token() 662 { 663 if (fd >= 0) 664 close(fd); 665 } 666 }; 667 668 private: 669 status_t _AddFilterEntry(Entry* parentEntry, const char* _name, 670 size_t nameLength, bool implicit, Entry*& _entry) 671 { 672 BString name(_name, nameLength); 673 if (name.IsEmpty()) 674 return B_NO_MEMORY; 675 676 return Entry::Create(parentEntry, name.String(), implicit, _entry); 677 } 678 679 void _GetParentFDAndEntryName(typename VersionPolicy::PackageEntry* entry, 680 int& _parentFD, const char*& _entryName) 681 { 682 _entryName = entry->Name(); 683 684 if (fInfoFileName != NULL 685 && strcmp(_entryName, VersionPolicy::PackageInfoFileName()) == 0) { 686 _parentFD = AT_FDCWD; 687 _entryName = fInfoFileName; 688 } else { 689 _parentFD = entry->Parent() != NULL 690 ? ((Token*)entry->Parent()->UserToken())->fd : fBaseDirectory; 691 } 692 } 693 694 BString _EntryPath(const typename VersionPolicy::PackageEntry* entry) 695 { 696 BString path; 697 698 if (const typename VersionPolicy::PackageEntry* parent 699 = entry->Parent()) { 700 path = _EntryPath(parent); 701 path << '/'; 702 } 703 704 path << entry->Name(); 705 return path; 706 } 707 708 status_t _ExtractFileData( 709 typename VersionPolicy::HeapReaderBase* dataReader, 710 const typename VersionPolicy::PackageData& data, int fd) 711 { 712 // create a PackageDataReader 713 BAbstractBufferedDataReader* reader; 714 status_t error = VersionPolicy::CreatePackageDataReader(fBufferPool, 715 dataReader, data, reader); 716 if (error != B_OK) 717 return error; 718 ObjectDeleter<BAbstractBufferedDataReader> readerDeleter(reader); 719 720 // write the data 721 off_t bytesRemaining = VersionPolicy::PackageDataUncompressedSize(data); 722 off_t offset = 0; 723 while (bytesRemaining > 0) { 724 // read 725 size_t toCopy = std::min((off_t)fDataBufferSize, bytesRemaining); 726 error = reader->ReadData(offset, fDataBuffer, toCopy); 727 if (error != B_OK) { 728 fprintf(stderr, "Error: Failed to read data: %s\n", 729 strerror(error)); 730 return error; 731 } 732 733 // write 734 ssize_t bytesWritten = write_pos(fd, offset, fDataBuffer, toCopy); 735 if (bytesWritten < 0) { 736 fprintf(stderr, "Error: Failed to write data: %s\n", 737 strerror(errno)); 738 return errno; 739 } 740 if ((size_t)bytesWritten != toCopy) { 741 fprintf(stderr, "Error: Failed to write all data (%zd of " 742 "%zu)\n", bytesWritten, toCopy); 743 return B_ERROR; 744 } 745 746 offset += toCopy; 747 bytesRemaining -= toCopy; 748 } 749 750 return B_OK; 751 } 752 753 private: 754 BBufferPool* fBufferPool; 755 typename VersionPolicy::HeapReaderBase* fPackageFileReader; 756 void* fDataBuffer; 757 size_t fDataBufferSize; 758 Entry fRootFilterEntry; 759 int fBaseDirectory; 760 const char* fInfoFileName; 761 bool fErrorOccurred; 762 }; 763 764 765 template<typename VersionPolicy> 766 static void 767 do_extract(const char* packageFileName, const char* changeToDirectory, 768 const char* packageInfoFileName, const char* const* explicitEntries, 769 int explicitEntryCount, bool ignoreVersionError) 770 { 771 // open package 772 BStandardErrorOutput errorOutput; 773 BBlockBufferPoolNoLock bufferPool(VersionPolicy::BufferSize(), 2); 774 if (bufferPool.Init() != B_OK) { 775 errorOutput.PrintError("Error: Out of memory!\n"); 776 exit(1); 777 } 778 779 typename VersionPolicy::PackageReader packageReader(&errorOutput); 780 status_t error = VersionPolicy::InitReader(packageReader, packageFileName); 781 if (error != B_OK) { 782 if (ignoreVersionError && error == B_MISMATCHED_VALUES) 783 return; 784 exit(1); 785 } 786 787 typename VersionPolicy::HeapReaderBase* heapReader; 788 bool mustDeleteHeapReader; 789 error = VersionPolicy::GetHeapReader(packageReader, heapReader, 790 mustDeleteHeapReader); 791 if (error != B_OK) { 792 fprintf(stderr, "Error: Failed to create heap reader: \"%s\"\n", 793 strerror(error)); 794 exit(1); 795 } 796 ObjectDeleter<BDataReader> heapReaderDeleter( 797 mustDeleteHeapReader ? heapReader : NULL); 798 799 PackageContentExtractHandler<VersionPolicy> handler(&bufferPool, 800 heapReader); 801 error = handler.Init(); 802 if (error != B_OK) 803 exit(1); 804 805 // If entries to extract have been specified explicitly, add those to the 806 // filtered ones. 807 if (explicitEntryCount > 0) { 808 for (int i = 0; i < explicitEntryCount; i++) { 809 const char* entryName = explicitEntries[i]; 810 if (entryName[0] == '\0' || entryName[0] == '/') { 811 fprintf(stderr, "Error: Invalid entry name: \"%s\"\n", 812 entryName); 813 exit(1); 814 } 815 if (handler.AddFilterEntry(entryName) != B_OK) 816 exit(1); 817 } 818 } else 819 handler.SetExtractAll(); 820 821 // get the target directory, if requested 822 if (changeToDirectory != NULL) { 823 int currentDirFD = open(changeToDirectory, O_RDONLY); 824 if (currentDirFD < 0) { 825 fprintf(stderr, "Error: Failed to change the current working " 826 "directory to \"%s\": %s\n", changeToDirectory, 827 strerror(errno)); 828 exit(1); 829 } 830 831 handler.SetBaseDirectory(currentDirFD); 832 } 833 834 // If a package info file name is given, set it. 835 if (packageInfoFileName != NULL) 836 handler.SetPackageInfoFile(packageInfoFileName); 837 838 // extract 839 error = packageReader.ParseContent(&handler); 840 if (error != B_OK) 841 exit(1); 842 843 // check whether all explicitly specified entries have been extracted 844 if (explicitEntryCount > 0) { 845 for (int i = 0; i < explicitEntryCount; i++) { 846 if (Entry* entry = handler.FindFilterEntry(explicitEntries[i])) { 847 if (!entry->Seen()) { 848 fprintf(stderr, "Warning: Entry \"%s\" not found.\n", 849 explicitEntries[i]); 850 } 851 } 852 } 853 } 854 855 exit(0); 856 } 857 858 859 int 860 command_extract(int argc, const char* const* argv) 861 { 862 const char* changeToDirectory = NULL; 863 const char* packageInfoFileName = NULL; 864 865 while (true) { 866 static struct option sLongOptions[] = { 867 { "help", no_argument, 0, 'h' }, 868 { 0, 0, 0, 0 } 869 }; 870 871 opterr = 0; // don't print errors 872 int c = getopt_long(argc, (char**)argv, "+C:hi:", sLongOptions, NULL); 873 if (c == -1) 874 break; 875 876 switch (c) { 877 case 'C': 878 changeToDirectory = optarg; 879 break; 880 881 case 'h': 882 print_usage_and_exit(false); 883 break; 884 885 case 'i': 886 packageInfoFileName = optarg; 887 break; 888 889 default: 890 print_usage_and_exit(true); 891 break; 892 } 893 } 894 895 // At least one argument should remain -- the package file name. Any further 896 // arguments are the names of the entries to extract. 897 if (optind + 1 > argc) 898 print_usage_and_exit(true); 899 900 const char* packageFileName = argv[optind++]; 901 const char* const* explicitEntries = argv + optind; 902 int explicitEntryCount = argc - optind; 903 do_extract<VersionPolicyV2>(packageFileName, changeToDirectory, 904 packageInfoFileName, explicitEntries, explicitEntryCount, true); 905 do_extract<VersionPolicyV1>(packageFileName, changeToDirectory, 906 packageInfoFileName, explicitEntries, explicitEntryCount, false); 907 908 return 0; 909 } 910