extractor_datasource.c (35089B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2002, 2003, 2004, 2005, 2006, 2009, 2012 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file main/extractor_datasource.c 22 * @brief random access and possibly decompression of data from buffer in memory or file on disk 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include "extractor_logging.h" 27 #include "extractor_datasource.h" 28 29 #if HAVE_LIBBZ2 30 #include <bzlib.h> 31 #define MIN_BZ2_HEADER 4 32 #ifndef MIN_COMPRESSED_HEADER 33 #define MIN_COMPRESSED_HEADER MIN_ZLIB_HEADER 34 #endif 35 #endif 36 37 #if HAVE_ZLIB 38 #include <zlib.h> 39 #define MIN_ZLIB_HEADER 12 40 #ifndef MIN_COMPRESSED_HEADER 41 #define MIN_COMPRESSED_HEADER MIN_BZ2_HEADER 42 #endif 43 #endif 44 45 #ifndef MIN_COMPRESSED_HEADER 46 #define MIN_COMPRESSED_HEADER -1 47 #endif 48 49 #ifndef O_LARGEFILE 50 #define O_LARGEFILE 0 51 #endif 52 53 /** 54 * Maximum size of an IO buffer. 55 */ 56 #define MAX_READ (4 * 1024 * 1024) 57 58 /** 59 * Data is read from the source and shoved into decompressor 60 * in chunks this big. 61 */ 62 #define COM_CHUNK_SIZE (16 * 1024) 63 64 65 /** 66 * Enum with the various possible types of compression supported. 67 */ 68 enum ExtractorCompressionType 69 { 70 /** 71 * We cannot tell from the data (header incomplete). 72 */ 73 COMP_TYPE_UNDEFINED = -1, 74 75 /** 76 * Invalid header (likely uncompressed) 77 */ 78 COMP_TYPE_INVALID = 0, 79 80 /** 81 * libz / gzip compression. 82 */ 83 COMP_TYPE_ZLIB = 1, 84 85 /** 86 * bz2 compression 87 */ 88 COMP_TYPE_BZ2 = 2 89 }; 90 91 92 /** 93 * Abstraction of the data source (file or a memory buffer) 94 * for the decompressor. 95 */ 96 struct BufferedFileDataSource 97 { 98 /** 99 * Pointer to the buffer to read from (may be NULL) 100 */ 101 const void *data; 102 103 /** 104 * A buffer to read into. For fd != -1: when data != NULL, 105 * data is used directly. 106 */ 107 void *buffer; 108 109 /** 110 * Size of the file (or the data buffer) 111 */ 112 uint64_t fsize; 113 114 /** 115 * Position of the buffer in the file. 116 */ 117 uint64_t fpos; 118 119 /** 120 * Position within the buffer. Our absolute offset in the file 121 * is thus 'fpos + buffer_pos'. 122 */ 123 size_t buffer_pos; 124 125 /** 126 * Number of valid bytes in the buffer (<= buffer_size) 127 */ 128 size_t buffer_bytes; 129 130 /** 131 * Allocated size of the buffer 132 */ 133 size_t buffer_size; 134 135 /** 136 * Descriptor of the file to read data from (may be -1) 137 */ 138 int fd; 139 140 }; 141 142 143 /** 144 * An object from which uncompressed data can be read 145 */ 146 struct CompressedFileSource 147 { 148 /** 149 * The source of data 150 */ 151 struct BufferedFileDataSource *bfds; 152 153 /** 154 * Decompression target buffer. 155 */ 156 char result[COM_CHUNK_SIZE]; 157 158 /** 159 * At which offset in 'result' is 'fpos'? 160 */ 161 size_t result_pos; 162 163 /** 164 * Size of the source (same as bfds->fsize) 165 */ 166 int64_t fsize; 167 168 /** 169 * Position within the (decompressed) source 170 */ 171 int64_t fpos; 172 173 /** 174 * Total size of the uncompressed data. Remains -1 until 175 * decompression is finished. 176 */ 177 int64_t uncompressed_size; 178 179 #if HAVE_LIBBZ2 180 /** 181 * BZ2 stream object 182 */ 183 bz_stream bstrm; 184 #endif 185 186 #if HAVE_ZLIB 187 /** 188 * ZLIB stream object 189 */ 190 z_stream strm; 191 192 /** 193 * Length of gzip header (may be 0, in that case ZLIB parses the header) 194 */ 195 int gzip_header_length; 196 #endif 197 198 /** 199 * The type of compression used in the source 200 */ 201 enum ExtractorCompressionType compression_type; 202 203 }; 204 205 206 /** 207 * Makes bfds seek to 'pos' and read a chunk of bytes there. 208 * Changes bfds->fpos, bfds->buffer_bytes and bfds->buffer_pos. 209 * Does almost nothing for memory-backed bfds. 210 * 211 * @param bfds bfds 212 * @param pos position 213 * @return 0 on success, -1 on error 214 */ 215 static int 216 bfds_pick_next_buffer_at (struct BufferedFileDataSource *bfds, 217 uint64_t pos) 218 { 219 int64_t position; 220 ssize_t rd; 221 222 if (pos > bfds->fsize) 223 { 224 LOG ("Invalid seek operation\n"); 225 return -1; /* invalid */ 226 } 227 if (NULL == bfds->buffer) 228 { 229 bfds->buffer_pos = pos; 230 return 0; 231 } 232 position = (int64_t) lseek (bfds->fd, pos, SEEK_SET); 233 if (position < 0) 234 { 235 LOG_STRERROR ("lseek"); 236 return -1; 237 } 238 bfds->fpos = position; 239 bfds->buffer_pos = 0; 240 rd = read (bfds->fd, bfds->buffer, bfds->buffer_size); 241 if (rd < 0) 242 { 243 LOG_STRERROR ("read"); 244 return -1; 245 } 246 bfds->buffer_bytes = rd; 247 return 0; 248 } 249 250 251 /** 252 * Creates a bfds 253 * 254 * @param data data buffer to use as a source (NULL if fd != -1) 255 * @param fd file descriptor to use as a source (-1 if data != NULL) 256 * @param fsize size of the file (or the buffer) 257 * @return newly allocated bfds 258 */ 259 static struct BufferedFileDataSource * 260 bfds_new (const void *data, 261 int fd, 262 int64_t fsize) 263 { 264 struct BufferedFileDataSource *result; 265 size_t xtra; 266 267 if (fsize > MAX_READ) 268 xtra = MAX_READ; 269 else 270 xtra = (size_t) fsize; 271 if ( (-1 == fd) && (NULL == data) ) 272 { 273 LOG ("Invalid arguments\n"); 274 return NULL; 275 } 276 if ( (-1 != fd) && (NULL != data) ) 277 fd = -1; /* don't need fd */ 278 if (NULL != data) 279 xtra = 0; 280 if (NULL == (result = malloc (sizeof (struct BufferedFileDataSource) + xtra))) 281 { 282 LOG_STRERROR ("malloc"); 283 return NULL; 284 } 285 memset (result, 0, sizeof (struct BufferedFileDataSource)); 286 result->data = (NULL != data) ? data : &result[1]; 287 result->buffer = (NULL != data) ? NULL : &result[1]; 288 result->buffer_size = (NULL != data) ? fsize : xtra; 289 result->buffer_bytes = (NULL != data) ? fsize : 0; 290 result->fsize = fsize; 291 result->fd = fd; 292 bfds_pick_next_buffer_at (result, 0); 293 return result; 294 } 295 296 297 /** 298 * Unallocates bfds 299 * 300 * @param bfds bfds to deallocate 301 */ 302 static void 303 bfds_delete (struct BufferedFileDataSource *bfds) 304 { 305 free (bfds); 306 } 307 308 309 /** 310 * Makes bfds seek to 'pos' in 'whence' mode. 311 * Will try to seek within the buffer, will move the buffer location if 312 * the seek request falls outside of the buffer range. 313 * 314 * @param bfds bfds 315 * @param pos position to seek to 316 * @param whence one of the seek constants (SEEK_CUR, SEEK_SET, SEEK_END) 317 * @return new absolute position, -1 on error 318 */ 319 static int64_t 320 bfds_seek (struct BufferedFileDataSource *bfds, 321 int64_t pos, int whence) 322 { 323 uint64_t npos; 324 size_t nbpos; 325 326 switch (whence) 327 { 328 case SEEK_CUR: 329 npos = bfds->fpos + bfds->buffer_pos + pos; 330 if (npos > bfds->fsize) 331 { 332 LOG ("Invalid seek operation to %lld from %llu (max is %llu)\n", 333 (long long) pos, 334 bfds->fpos + bfds->buffer_pos, 335 (unsigned long long) bfds->fsize); 336 return -1; 337 } 338 nbpos = bfds->buffer_pos + pos; 339 if ( (NULL == bfds->buffer) || 340 (nbpos < bfds->buffer_bytes) ) 341 { 342 bfds->buffer_pos = nbpos; 343 return npos; 344 } 345 if (0 != bfds_pick_next_buffer_at (bfds, 346 npos)) 347 { 348 LOG ("seek operation failed\n"); 349 return -1; 350 } 351 return npos; 352 case SEEK_END: 353 if (pos > 0) 354 { 355 LOG ("Invalid seek operation\n"); 356 return -1; 357 } 358 /* -pos is undefined for INT64_MIN; negate in unsigned arithmetic. */ 359 if (bfds->fsize < (uint64_t) -(uint64_t) pos) 360 { 361 LOG ("Invalid seek operation\n"); 362 return -1; 363 } 364 pos = (int64_t) (bfds->fsize + (uint64_t) pos); 365 /* fall-through! */ 366 case SEEK_SET: 367 if (pos < 0) 368 { 369 LOG ("Invalid seek operation\n"); 370 return -1; 371 } 372 if (pos > bfds->fsize) 373 { 374 LOG ("Invalid seek operation (%lld > %llu) %d\n", 375 (long long) pos, 376 (unsigned long long) bfds->fsize, 377 SEEK_SET == whence); 378 return -1; 379 } 380 if ( (NULL == bfds->buffer) || 381 ( (bfds->fpos <= pos) && 382 (bfds->fpos + bfds->buffer_bytes > pos) ) ) 383 { 384 bfds->buffer_pos = pos - bfds->fpos; 385 return pos; 386 } 387 if (0 != bfds_pick_next_buffer_at (bfds, pos)) 388 { 389 LOG ("seek operation failed\n"); 390 return -1; 391 } 392 ASSERT (pos == bfds->fpos + bfds->buffer_pos); 393 return pos; 394 } 395 return -1; 396 } 397 398 399 /** 400 * Fills 'buf_ptr' with a chunk of data. Will 401 * fail if 'count' exceeds buffer size. 402 * 403 * @param bfds bfds 404 * @param buf_ptr location to store data 405 * @param count number of bytes to read 406 * @return number of bytes (<= count) available at location pointed by buf_ptr, 407 * 0 for end of stream, -1 on error 408 */ 409 static ssize_t 410 bfds_read (struct BufferedFileDataSource *bfds, 411 void *buf_ptr, 412 size_t count) 413 { 414 char *cbuf = buf_ptr; 415 uint64_t old_off; 416 size_t avail; 417 size_t ret; 418 419 old_off = bfds->fpos + bfds->buffer_pos; 420 if (old_off == bfds->fsize) 421 return 0; /* end of stream */ 422 ret = 0; 423 while (count > 0) 424 { 425 if ( (bfds->buffer_bytes == bfds->buffer_pos) && 426 (0 != bfds_pick_next_buffer_at (bfds, 427 bfds->fpos + bfds->buffer_bytes)) ) 428 { 429 /* revert to original position, invalidate buffer */ 430 bfds->fpos = old_off; 431 bfds->buffer_bytes = 0; 432 bfds->buffer_pos = 0; 433 LOG ("read operation failed\n"); 434 return -1; /* getting more failed */ 435 } 436 avail = bfds->buffer_bytes - bfds->buffer_pos; 437 if (avail > count) 438 avail = count; 439 if (0 == avail) 440 break; 441 memcpy (&cbuf[ret], bfds->data + bfds->buffer_pos, avail); 442 bfds->buffer_pos += avail; 443 count -= avail; 444 ret += avail; 445 } 446 return ret; 447 } 448 449 450 #if HAVE_ZLIB 451 452 #define FHCRC 0x02 453 #define FEXTRA 0x04 454 #define FNAME 0x08 455 #define FCOMMENT 0x10 456 457 /** 458 * Initializes gz-decompression object. Might report metadata about 459 * compresse stream, if available. Resets the stream to the beginning. 460 * 461 * @param cfs cfs to initialize 462 * @param proc callback for metadata 463 * @param proc_cls callback cls 464 * @return 1 on success, 0 to terminate extraction, -1 on error 465 */ 466 static int 467 cfs_init_decompressor_zlib (struct CompressedFileSource *cfs, 468 EXTRACTOR_MetaDataProcessor proc, void *proc_cls) 469 { 470 unsigned int gzip_header_length = 10; 471 unsigned char hdata[12]; 472 ssize_t rsize; 473 474 if (0 != bfds_seek (cfs->bfds, 0, SEEK_SET)) 475 { 476 LOG ("Failed to seek to offset 0!\n"); 477 return -1; 478 } 479 /* Process gzip header */ 480 rsize = bfds_read (cfs->bfds, hdata, sizeof (hdata)); 481 if ( (-1 == rsize) || 482 (sizeof (hdata) > (size_t) rsize) ) 483 return -1; 484 if (0 != (hdata[3] & FEXTRA)) 485 gzip_header_length += 2 + (hdata[10] & 0xff) + ((hdata[11] & 0xff) * 256); 486 487 if (0 != (hdata[3] & FNAME)) 488 { 489 /* FNAME set */ 490 char fname[1024]; 491 char *cptr; 492 size_t len; 493 ssize_t buf_bytes; 494 495 if (gzip_header_length > bfds_seek (cfs->bfds, gzip_header_length, 496 SEEK_SET)) 497 { 498 LOG ("Corrupt gzip, failed to seek to end of header\n"); 499 return -1; 500 } 501 buf_bytes = bfds_read (cfs->bfds, fname, sizeof (fname)); 502 if (buf_bytes <= 0) 503 { 504 LOG ("Corrupt gzip, failed to read filename\n"); 505 return -1; 506 } 507 if (NULL == (cptr = memchr (fname, 0, buf_bytes))) 508 { 509 LOG ("Corrupt gzip, failed to read filename terminator\n"); 510 return -1; 511 } 512 len = cptr - fname; 513 if ( (NULL != proc) && 514 (0 != proc (proc_cls, "<zlib>", EXTRACTOR_METATYPE_FILENAME, 515 EXTRACTOR_METAFORMAT_C_STRING, "text/plain", 516 fname, 517 len)) ) 518 return 0; /* done */ 519 gzip_header_length += len + 1; 520 } 521 522 if (0 != (hdata[3] & FCOMMENT)) 523 { 524 /* FCOMMENT set */ 525 char fcomment[1024]; 526 char *cptr; 527 ssize_t buf_bytes; 528 size_t len; 529 530 if (gzip_header_length > bfds_seek (cfs->bfds, gzip_header_length, 531 SEEK_SET)) 532 { 533 LOG ("Corrupt gzip, failed to seek to end of header\n"); 534 return -1; 535 } 536 buf_bytes = bfds_read (cfs->bfds, fcomment, sizeof (fcomment)); 537 if (buf_bytes <= 0) 538 { 539 LOG ("Corrupt gzip, failed to read comment\n"); 540 return -1; 541 } 542 if (NULL == (cptr = memchr (fcomment, 0, buf_bytes))) 543 { 544 LOG ("Corrupt gzip, failed to read comment terminator\n"); 545 return -1; 546 } 547 len = cptr - fcomment; 548 if ( (NULL != proc) && 549 (0 != proc (proc_cls, "<zlib>", EXTRACTOR_METATYPE_COMMENT, 550 EXTRACTOR_METAFORMAT_C_STRING, "text/plain", 551 (const char *) fcomment, 552 len)) ) 553 return 0; /* done */ 554 gzip_header_length += len + 1; 555 } 556 if (0 != (hdata[3] & FHCRC)) 557 gzip_header_length += 2; 558 memset (&cfs->strm, 0, sizeof (z_stream)); 559 560 #ifdef ZLIB_VERNUM 561 /* zlib will take care of its header */ 562 gzip_header_length = 0; 563 #endif 564 cfs->gzip_header_length = gzip_header_length; 565 566 if (cfs->gzip_header_length != 567 bfds_seek (cfs->bfds, cfs->gzip_header_length, SEEK_SET)) 568 { 569 LOG ("Failed to seek to start to initialize gzip decompressor\n"); 570 return -1; 571 } 572 cfs->strm.avail_out = COM_CHUNK_SIZE; 573 /* 574 * note: maybe plain inflateInit(&strm) is adequate, 575 * it looks more backward-compatible also ; 576 * 577 * ZLIB_VERNUM isn't defined by zlib version 1.1.4 ; 578 * there might be a better check. 579 */ 580 if (Z_OK != 581 inflateInit2 (&cfs->strm, 582 #ifdef ZLIB_VERNUM 583 15 + 32 584 #else 585 -MAX_WBITS 586 #endif 587 )) 588 { 589 LOG ("Failed to initialize zlib decompression\n"); 590 return -1; 591 } 592 return 1; 593 } 594 595 596 #endif 597 598 599 #if HAVE_LIBBZ2 600 /** 601 * Initializes bz2-decompression object. Might report metadata about 602 * compresse stream, if available. Resets the stream to the beginning. 603 * 604 * @param cfs cfs to initialize 605 * @param proc callback for metadata 606 * @param proc_cls callback cls 607 * @return 1 on success, -1 on error 608 */ 609 static int 610 cfs_init_decompressor_bz2 (struct CompressedFileSource *cfs, 611 EXTRACTOR_MetaDataProcessor proc, 612 void *proc_cls) 613 { 614 if (0 != 615 bfds_seek (cfs->bfds, 616 0, 617 SEEK_SET)) 618 { 619 LOG ("Failed to seek to start to initialize BZ2 decompressor\n"); 620 return -1; 621 } 622 memset (&cfs->bstrm, 623 0, 624 sizeof (bz_stream)); 625 if (BZ_OK != 626 BZ2_bzDecompressInit (&cfs->bstrm, 627 0, 628 0)) 629 { 630 LOG ("Failed to initialize BZ2 decompressor\n"); 631 return -1; 632 } 633 cfs->bstrm.avail_out = COM_CHUNK_SIZE; 634 return 1; 635 } 636 637 638 #endif 639 640 641 /** 642 * Initializes decompression object. Might report metadata about 643 * compresse stream, if available. Resets the stream to the beginning. 644 * 645 * @param cfs cfs to initialize 646 * @param proc callback for metadata 647 * @param proc_cls callback cls 648 * @return 1 on success, 0 to terminate extraction, -1 on error 649 */ 650 static int 651 cfs_init_decompressor (struct CompressedFileSource *cfs, 652 EXTRACTOR_MetaDataProcessor proc, 653 void *proc_cls) 654 { 655 cfs->result_pos = 0; 656 cfs->fpos = 0; 657 switch (cfs->compression_type) 658 { 659 #if HAVE_ZLIB 660 case COMP_TYPE_ZLIB: 661 return cfs_init_decompressor_zlib (cfs, proc, proc_cls); 662 #endif 663 #if HAVE_LIBBZ2 664 case COMP_TYPE_BZ2: 665 return cfs_init_decompressor_bz2 (cfs, proc, proc_cls); 666 #endif 667 default: 668 LOG ("invalid compression type selected\n"); 669 return -1; 670 } 671 } 672 673 674 #if HAVE_ZLIB 675 /** 676 * Deinitializes gz-decompression object. 677 * 678 * @param cfs cfs to deinitialize 679 * @return 1 on success, -1 on error 680 */ 681 static int 682 cfs_deinit_decompressor_zlib (struct CompressedFileSource *cfs) 683 { 684 inflateEnd (&cfs->strm); 685 return 1; 686 } 687 688 689 #endif 690 691 692 #if HAVE_LIBBZ2 693 /** 694 * Deinitializes bz2-decompression object. 695 * 696 * @param cfs cfs to deinitialize 697 * @return 1 on success, -1 on error 698 */ 699 static int 700 cfs_deinit_decompressor_bz2 (struct CompressedFileSource *cfs) 701 { 702 BZ2_bzDecompressEnd (&cfs->bstrm); 703 return 1; 704 } 705 706 707 #endif 708 709 710 /** 711 * Deinitializes decompression object. 712 * 713 * @param cfs cfs to deinitialize 714 * @return 1 on success, -1 on error 715 */ 716 static int 717 cfs_deinit_decompressor (struct CompressedFileSource *cfs) 718 { 719 switch (cfs->compression_type) 720 { 721 #if HAVE_ZLIB 722 case COMP_TYPE_ZLIB: 723 return cfs_deinit_decompressor_zlib (cfs); 724 #endif 725 #if HAVE_LIBBZ2 726 case COMP_TYPE_BZ2: 727 return cfs_deinit_decompressor_bz2 (cfs); 728 #endif 729 default: 730 LOG ("invalid compression type selected\n"); 731 return -1; 732 } 733 } 734 735 736 /** 737 * Resets the compression stream to begin uncompressing 738 * from the beginning. Used at initialization time, and when 739 * seeking backward. 740 * 741 * @param cfs cfs to reset 742 * @return 1 on success, 0 to terminate extraction, 743 * -1 on error 744 */ 745 static int 746 cfs_reset_stream (struct CompressedFileSource *cfs) 747 { 748 if (-1 == cfs_deinit_decompressor (cfs)) 749 return -1; 750 return cfs_init_decompressor (cfs, NULL, NULL); 751 } 752 753 754 /** 755 * Destroy compressed file source. 756 * 757 * @param cfs source to destroy 758 */ 759 static void 760 cfs_destroy (struct CompressedFileSource *cfs) 761 { 762 cfs_deinit_decompressor (cfs); 763 free (cfs); 764 } 765 766 767 /** 768 * Allocates and initializes new cfs object. 769 * 770 * @param bfds data source to use 771 * @param fsize size of the source 772 * @param compression_type type of compression used 773 * @param proc metadata callback to call with meta data found upon opening 774 * @param proc_cls callback cls 775 * @return newly allocated cfs on success, NULL on error 776 */ 777 static struct CompressedFileSource * 778 cfs_new (struct BufferedFileDataSource *bfds, 779 int64_t fsize, 780 enum ExtractorCompressionType compression_type, 781 EXTRACTOR_MetaDataProcessor proc, void *proc_cls) 782 { 783 struct CompressedFileSource *cfs; 784 785 if (NULL == (cfs = malloc (sizeof (struct CompressedFileSource)))) 786 { 787 LOG_STRERROR ("malloc"); 788 return NULL; 789 } 790 memset (cfs, 0, sizeof (struct CompressedFileSource)); 791 cfs->compression_type = compression_type; 792 cfs->bfds = bfds; 793 cfs->fsize = fsize; 794 cfs->uncompressed_size = -1; 795 if (1 != cfs_init_decompressor (cfs, 796 proc, proc_cls)) 797 { 798 free (cfs); 799 return NULL; 800 } 801 return cfs; 802 } 803 804 805 #if HAVE_ZLIB 806 /** 807 * Fills 'data' with new uncompressed data. Does the actual 808 * decompression. Will set uncompressed_size on the end of compressed 809 * stream. 810 * 811 * @param cfds cfs to read from 812 * @param data where to copy the data 813 * @param size number of bytes available in data 814 * @return number of bytes in data. 0 if no more data can be uncompressed, -1 on error 815 */ 816 static ssize_t 817 cfs_read_zlib (struct CompressedFileSource *cfs, 818 void *data, 819 size_t size) 820 { 821 char *dst = data; 822 int ret; 823 size_t rc; 824 ssize_t in; 825 unsigned char buf[COM_CHUNK_SIZE]; 826 827 if (cfs->fpos == cfs->uncompressed_size) 828 { 829 /* end of file */ 830 return 0; 831 } 832 rc = 0; 833 if (COM_CHUNK_SIZE > cfs->strm.avail_out + cfs->result_pos) 834 { 835 /* got left-over decompressed data from previous round! */ 836 in = COM_CHUNK_SIZE - (cfs->strm.avail_out + cfs->result_pos); 837 if (in > size) 838 in = size; 839 memcpy (&dst[rc], &cfs->result[cfs->result_pos], in); 840 cfs->fpos += in; 841 cfs->result_pos += in; 842 rc += in; 843 } 844 ret = Z_OK; 845 while ( (rc < size) && (Z_STREAM_END != ret) ) 846 { 847 /* read block from original data source */ 848 in = bfds_read (cfs->bfds, 849 buf, sizeof (buf)); 850 if (in < 0) 851 { 852 LOG ("unexpected EOF\n"); 853 return -1; /* unexpected EOF */ 854 } 855 if (0 == in) 856 { 857 cfs->uncompressed_size = cfs->fpos; 858 return rc; 859 } 860 cfs->strm.next_in = buf; 861 cfs->strm.avail_in = (uInt) in; 862 cfs->strm.next_out = (unsigned char *) cfs->result; 863 cfs->strm.avail_out = COM_CHUNK_SIZE; 864 cfs->result_pos = 0; 865 ret = inflate (&cfs->strm, Z_SYNC_FLUSH); 866 if ( (Z_OK != ret) && (Z_STREAM_END != ret) ) 867 { 868 LOG ("unexpected gzip inflate error: %d\n", ret); 869 return -1; /* unexpected error */ 870 } 871 /* go backwards by the number of bytes left in the buffer */ 872 if (-1 == bfds_seek (cfs->bfds, -(int64_t) cfs->strm.avail_in, SEEK_CUR)) 873 { 874 LOG ("seek failed\n"); 875 return -1; 876 } 877 /* copy decompressed bytes to target buffer */ 878 in = COM_CHUNK_SIZE - cfs->strm.avail_out; 879 if (in > size - rc) 880 { 881 if (Z_STREAM_END == ret) 882 { 883 cfs->uncompressed_size = cfs->fpos + in; 884 ret = Z_OK; 885 } 886 in = size - rc; 887 } 888 memcpy (&dst[rc], &cfs->result[cfs->result_pos], in); 889 cfs->fpos += in; 890 cfs->result_pos += in; 891 rc += in; 892 } 893 if (Z_STREAM_END == ret) 894 { 895 cfs->uncompressed_size = cfs->fpos; 896 } 897 return rc; 898 } 899 900 901 #endif 902 903 904 #if HAVE_LIBBZ2 905 /** 906 * Fills 'data' with new uncompressed data. Does the actual 907 * decompression. Will set uncompressed_size on the end of compressed 908 * stream. 909 * 910 * @param cfds cfs to read from 911 * @param data where to copy the data 912 * @param size number of bytes available in data 913 * @return number of bytes in data. 0 if no more data can be uncompressed, -1 on error 914 */ 915 static ssize_t 916 cfs_read_bz2 (struct CompressedFileSource *cfs, 917 void *data, 918 size_t size) 919 { 920 char *dst = data; 921 int ret; 922 size_t rc; 923 ssize_t in; 924 char buf[COM_CHUNK_SIZE]; 925 926 if (cfs->fpos == cfs->uncompressed_size) 927 { 928 /* end of file */ 929 return 0; 930 } 931 rc = 0; 932 if (COM_CHUNK_SIZE > cfs->bstrm.avail_out + cfs->result_pos) 933 { 934 /* got left-over decompressed data from previous round! */ 935 in = COM_CHUNK_SIZE - (cfs->bstrm.avail_out + cfs->result_pos); 936 if (in > size) 937 in = size; 938 memcpy (&dst[rc], &cfs->result[cfs->result_pos], in); 939 cfs->fpos += in; 940 cfs->result_pos += in; 941 rc += in; 942 } 943 ret = BZ_OK; 944 while ( (rc < size) && (BZ_STREAM_END != ret) ) 945 { 946 /* read block from original data source */ 947 in = bfds_read (cfs->bfds, 948 buf, sizeof (buf)); 949 if (in < 0) 950 { 951 LOG ("unexpected EOF\n"); 952 return -1; /* unexpected EOF */ 953 } 954 if (0 == in) 955 { 956 cfs->uncompressed_size = cfs->fpos; 957 return rc; 958 } 959 cfs->bstrm.next_in = buf; 960 cfs->bstrm.avail_in = (unsigned int) in; 961 cfs->bstrm.next_out = cfs->result; 962 cfs->bstrm.avail_out = COM_CHUNK_SIZE; 963 cfs->result_pos = 0; 964 ret = BZ2_bzDecompress (&cfs->bstrm); 965 if ( (BZ_OK != ret) && (BZ_STREAM_END != ret) ) 966 { 967 LOG ("unexpected bzip2 decompress error: %d\n", ret); 968 return -1; /* unexpected error */ 969 } 970 /* go backwards by the number of bytes left in the buffer */ 971 if (-1 == bfds_seek (cfs->bfds, -(int64_t) cfs->bstrm.avail_in, SEEK_CUR)) 972 { 973 LOG ("seek failed\n"); 974 return -1; 975 } 976 /* copy decompressed bytes to target buffer */ 977 in = COM_CHUNK_SIZE - cfs->bstrm.avail_out; 978 if (in > size - rc) 979 { 980 if (BZ_STREAM_END == ret) 981 { 982 cfs->uncompressed_size = cfs->fpos + in; 983 ret = BZ_OK; 984 } 985 in = size - rc; 986 } 987 memcpy (&dst[rc], &cfs->result[cfs->result_pos], in); 988 cfs->fpos += in; 989 cfs->result_pos += in; 990 rc += in; 991 } 992 if (BZ_STREAM_END == ret) 993 { 994 cfs->uncompressed_size = cfs->fpos; 995 } 996 return rc; 997 } 998 999 1000 #endif 1001 1002 1003 /** 1004 * Fills 'data' with new uncompressed data. Does the actual 1005 * decompression. Will set uncompressed_size on the end of compressed 1006 * stream. 1007 * 1008 * @param cfds cfs to read from 1009 * @param data where to copy the data 1010 * @param size number of bytes available in data 1011 * @return number of bytes in data. 0 if no more data can be uncompressed, -1 on error 1012 */ 1013 static ssize_t 1014 cfs_read (struct CompressedFileSource *cfs, 1015 void *data, 1016 size_t size) 1017 { 1018 switch (cfs->compression_type) 1019 { 1020 #if HAVE_ZLIB 1021 case COMP_TYPE_ZLIB: 1022 return cfs_read_zlib (cfs, data, size); 1023 #endif 1024 #if HAVE_LIBBZ2 1025 case COMP_TYPE_BZ2: 1026 return cfs_read_bz2 (cfs, data, size); 1027 #endif 1028 default: 1029 LOG ("invalid compression type selected\n"); 1030 return -1; 1031 } 1032 } 1033 1034 1035 /** 1036 * Moves the buffer to 'position' in uncompressed steam. If position 1037 * requires seeking backwards beyond the boundaries of the buffer, resets the 1038 * stream and repeats decompression from the beginning to 'position'. 1039 * 1040 * @param cfs cfs to seek on 1041 * @param position new starting point for the buffer 1042 * @param whence one of the seek constants (SEEK_CUR, SEEK_SET, SEEK_END) 1043 * @return new absolute buffer position, -1 on error or EOS 1044 */ 1045 static int64_t 1046 cfs_seek (struct CompressedFileSource *cfs, 1047 int64_t position, 1048 int whence) 1049 { 1050 uint64_t nposition; 1051 int64_t delta; 1052 1053 switch (whence) 1054 { 1055 case SEEK_CUR: 1056 if (position < 0) 1057 { 1058 /* underflow; -position is undefined for INT64_MIN, so the 1059 magnitude is taken in unsigned arithmetic */ 1060 if ((uint64_t) cfs->fpos < (uint64_t) -(uint64_t) position) 1061 { 1062 LOG ("Invalid seek operation\n"); 1063 return -1; 1064 } 1065 } 1066 else 1067 { 1068 /* the addition itself must not overflow, which it can whenever 1069 the uncompressed size is still unknown */ 1070 if (position > INT64_MAX - cfs->fpos) 1071 { 1072 LOG ("Invalid seek operation\n"); 1073 return -1; 1074 } 1075 if ( (-1 != cfs->uncompressed_size) && 1076 (cfs->fpos + position > cfs->uncompressed_size) ) 1077 { 1078 LOG ("Invalid seek operation\n"); 1079 return -1; 1080 } 1081 } 1082 nposition = (uint64_t) (cfs->fpos + position); 1083 break; 1084 case SEEK_END: 1085 ASSERT (-1 != cfs->uncompressed_size); 1086 if (position > 0) 1087 { 1088 LOG ("Invalid seek operation\n"); 1089 return -1; 1090 } 1091 if ((uint64_t) cfs->uncompressed_size < (uint64_t) -(uint64_t) position) 1092 { 1093 LOG ("Invalid seek operation\n"); 1094 return -1; 1095 } 1096 nposition = (uint64_t) (cfs->uncompressed_size + position); 1097 break; 1098 case SEEK_SET: 1099 if (position < 0) 1100 { 1101 LOG ("Invalid seek operation\n"); 1102 return -1; 1103 } 1104 if ( (-1 != cfs->uncompressed_size) && 1105 (cfs->uncompressed_size < position) ) 1106 { 1107 LOG ("Invalid seek operation\n"); 1108 return -1; 1109 } 1110 nposition = (uint64_t) position; 1111 break; 1112 default: 1113 LOG ("Invalid seek operation\n"); 1114 return -1; 1115 } 1116 delta = (int64_t) (nposition - (uint64_t) cfs->fpos); 1117 if (delta < 0) 1118 { 1119 if ((uint64_t) cfs->result_pos >= (uint64_t) -(uint64_t) delta) 1120 { 1121 cfs->result_pos += delta; 1122 cfs->fpos += delta; 1123 delta = 0; 1124 } 1125 else 1126 { 1127 if (-1 == cfs_reset_stream (cfs)) 1128 { 1129 LOG ("Failed to restart compressed stream for seek operation\n"); 1130 return -1; 1131 } 1132 delta = nposition; 1133 } 1134 } 1135 while (delta > 0) 1136 { 1137 char buf[COM_CHUNK_SIZE]; 1138 size_t max; 1139 int64_t ret; 1140 1141 max = (sizeof (buf) > delta) ? delta : sizeof (buf); 1142 ret = cfs_read (cfs, buf, max); 1143 if (-1 == ret) 1144 { 1145 LOG ("Failed to read decompressed stream for seek operation\n"); 1146 return -1; 1147 } 1148 if (0 == ret) 1149 { 1150 LOG ( 1151 "Reached unexpected end of stream at %llu during seek operation to %llu (%d left)\n", 1152 (unsigned long long) cfs->fpos, 1153 (unsigned long long) nposition, 1154 delta); 1155 return -1; 1156 } 1157 ASSERT (ret <= delta); 1158 delta -= ret; 1159 } 1160 return cfs->fpos; 1161 } 1162 1163 1164 /** 1165 * Detect if we have compressed data on our hands. 1166 * 1167 * @param data pointer to a data buffer or NULL (in case fd is not -1) 1168 * @param fd a file to read data from, or -1 (if data is not NULL) 1169 * @param fsize size of data (if data is not NULL) or of file (if fd is not -1) 1170 * @return -1 to indicate an error, 0 to indicate uncompressed data, or a type (> 0) of compression 1171 */ 1172 static enum ExtractorCompressionType 1173 get_compression_type (struct BufferedFileDataSource *bfds) 1174 { 1175 unsigned char read_data[3]; 1176 1177 if (0 != bfds_seek (bfds, 0, SEEK_SET)) 1178 return COMP_TYPE_INVALID; 1179 if (sizeof (read_data) != 1180 bfds_read (bfds, read_data, sizeof (read_data))) 1181 return COMP_TYPE_UNDEFINED; 1182 1183 #if HAVE_ZLIB 1184 if ( (bfds->fsize >= MIN_ZLIB_HEADER) && 1185 (read_data[0] == 0x1f) && 1186 (read_data[1] == 0x8b) && 1187 (read_data[2] == 0x08) ) 1188 return COMP_TYPE_ZLIB; 1189 #endif 1190 #if HAVE_LIBBZ2 1191 if ( (bfds->fsize >= MIN_BZ2_HEADER) && 1192 (read_data[0] == 'B') && 1193 (read_data[1] == 'Z') && 1194 (read_data[2] == 'h')) 1195 return COMP_TYPE_BZ2; 1196 #endif 1197 return COMP_TYPE_INVALID; 1198 } 1199 1200 1201 /** 1202 * Handle to a datasource we can use for the plugins. 1203 */ 1204 struct EXTRACTOR_Datasource 1205 { 1206 1207 /** 1208 * Underlying buffered data source. 1209 */ 1210 struct BufferedFileDataSource *bfds; 1211 1212 /** 1213 * Compressed file source (NULL if not applicable). 1214 */ 1215 struct CompressedFileSource *cfs; 1216 1217 /** 1218 * Underlying file descriptor, -1 for none. 1219 */ 1220 int fd; 1221 }; 1222 1223 1224 /** 1225 * Create a datasource from a file on disk. 1226 * 1227 * @param filename name of the file on disk 1228 * @param proc metadata callback to call with meta data found upon opening 1229 * @param proc_cls callback cls 1230 * @return handle to the datasource, NULL on error 1231 */ 1232 struct EXTRACTOR_Datasource * 1233 EXTRACTOR_datasource_create_from_file_ (const char *filename, 1234 EXTRACTOR_MetaDataProcessor proc, 1235 void *proc_cls) 1236 { 1237 struct BufferedFileDataSource *bfds; 1238 struct EXTRACTOR_Datasource *ds; 1239 enum ExtractorCompressionType ct; 1240 int fd; 1241 struct stat sb; 1242 int64_t fsize; 1243 int winmode = 0; 1244 #if WINDOWS 1245 winmode = O_BINARY; 1246 #endif 1247 1248 if (-1 == (fd = open (filename, O_RDONLY | O_LARGEFILE | winmode))) 1249 { 1250 LOG_STRERROR_FILE ("open", filename); 1251 return NULL; 1252 } 1253 if ( (0 != fstat (fd, &sb)) || 1254 (S_ISDIR (sb.st_mode)) ) 1255 { 1256 if (! S_ISDIR (sb.st_mode)) 1257 LOG_STRERROR_FILE ("fstat", filename); 1258 else 1259 LOG ("Skipping directory `%s'\n", filename); 1260 (void) close (fd); 1261 return NULL; 1262 } 1263 fsize = (int64_t) sb.st_size; 1264 if (0 == fsize) 1265 { 1266 (void) close (fd); 1267 return NULL; 1268 } 1269 bfds = bfds_new (NULL, fd, fsize); 1270 if (NULL == bfds) 1271 { 1272 (void) close (fd); 1273 return NULL; 1274 } 1275 if (NULL == (ds = malloc (sizeof (struct EXTRACTOR_Datasource)))) 1276 { 1277 LOG_STRERROR ("malloc"); 1278 bfds_delete (bfds); 1279 (void) close (fd); 1280 return NULL; 1281 } 1282 ds->bfds = bfds; 1283 ds->fd = fd; 1284 ds->cfs = NULL; 1285 ct = get_compression_type (bfds); 1286 if ( (COMP_TYPE_ZLIB == ct) || 1287 (COMP_TYPE_BZ2 == ct) ) 1288 { 1289 ds->cfs = cfs_new (bfds, fsize, ct, proc, proc_cls); 1290 if (NULL == ds->cfs) 1291 { 1292 LOG ("Failed to initialize decompressor\n"); 1293 bfds_delete (bfds); 1294 free (ds); 1295 (void) close (fd); 1296 return NULL; 1297 } 1298 } 1299 return ds; 1300 } 1301 1302 1303 /** 1304 * Create a datasource from a buffer in memory. 1305 * 1306 * @param buf data in memory 1307 * @param size number of bytes in 'buf' 1308 * @param proc metadata callback to call with meta data found upon opening 1309 * @param proc_cls callback cls 1310 * @return handle to the datasource 1311 */ 1312 struct EXTRACTOR_Datasource * 1313 EXTRACTOR_datasource_create_from_buffer_ (const char *buf, 1314 size_t size, 1315 EXTRACTOR_MetaDataProcessor proc, 1316 void *proc_cls) 1317 { 1318 struct BufferedFileDataSource *bfds; 1319 struct EXTRACTOR_Datasource *ds; 1320 enum ExtractorCompressionType ct; 1321 1322 if (0 == size) 1323 return NULL; 1324 if (NULL == (bfds = bfds_new (buf, -1, size))) 1325 { 1326 LOG ("Failed to initialize buffer data source\n"); 1327 return NULL; 1328 } 1329 if (NULL == (ds = malloc (sizeof (struct EXTRACTOR_Datasource)))) 1330 { 1331 LOG_STRERROR ("malloc"); 1332 bfds_delete (bfds); 1333 return NULL; 1334 } 1335 ds->bfds = bfds; 1336 ds->fd = -1; 1337 ds->cfs = NULL; 1338 ct = get_compression_type (bfds); 1339 if ( (COMP_TYPE_ZLIB == ct) || 1340 (COMP_TYPE_BZ2 == ct) ) 1341 { 1342 ds->cfs = cfs_new (bfds, size, ct, proc, proc_cls); 1343 if (NULL == ds->cfs) 1344 { 1345 LOG ("Failed to initialize decompressor\n"); 1346 bfds_delete (bfds); 1347 free (ds); 1348 return NULL; 1349 } 1350 } 1351 return ds; 1352 } 1353 1354 1355 /** 1356 * Destroy a data source. 1357 * 1358 * @param ds source to destroy 1359 */ 1360 void 1361 EXTRACTOR_datasource_destroy_ (struct EXTRACTOR_Datasource *ds) 1362 { 1363 if (NULL != ds->cfs) 1364 cfs_destroy (ds->cfs); 1365 bfds_delete (ds->bfds); 1366 if (-1 != ds->fd) 1367 (void) close (ds->fd); 1368 free (ds); 1369 } 1370 1371 1372 /** 1373 * Make 'size' bytes of data from the data source available at 'data'. 1374 * 1375 * @param cls must be a 'struct EXTRACTOR_Datasource' 1376 * @param data where the data should be copied to 1377 * @param size maximum number of bytes requested 1378 * @return number of bytes now available in data (can be smaller than 'size'), 1379 * -1 on error 1380 */ 1381 ssize_t 1382 EXTRACTOR_datasource_read_ (void *cls, 1383 void *data, 1384 size_t size) 1385 { 1386 struct EXTRACTOR_Datasource *ds = cls; 1387 1388 if (NULL != ds->cfs) 1389 return cfs_read (ds->cfs, data, size); 1390 return bfds_read (ds->bfds, data, size); 1391 } 1392 1393 1394 /** 1395 * Seek in the datasource. Use 'SEEK_CUR' for whence and 'pos' of 0 to 1396 * obtain the current position in the file. 1397 * 1398 * @param cls must be a 'struct EXTRACTOR_Datasource' 1399 * @param pos position to seek (see 'man lseek') 1400 * @param whence how to see (absolute to start, relative, absolute to end) 1401 * @return new absolute position, UINT64_MAX on error (i.e. desired position 1402 * does not exist) 1403 */ 1404 int64_t 1405 EXTRACTOR_datasource_seek_ (void *cls, 1406 int64_t pos, 1407 int whence) 1408 { 1409 struct EXTRACTOR_Datasource *ds = cls; 1410 1411 if (NULL != ds->cfs) 1412 { 1413 if ( (SEEK_END == whence) && 1414 (-1 == ds->cfs->uncompressed_size) ) 1415 { 1416 /* need to obtain uncompressed size */ 1417 (void) EXTRACTOR_datasource_get_size_ (ds, 1); 1418 if (-1 == ds->cfs->uncompressed_size) 1419 return -1; 1420 } 1421 return cfs_seek (ds->cfs, pos, whence); 1422 } 1423 return bfds_seek (ds->bfds, pos, whence); 1424 } 1425 1426 1427 /** 1428 * Determine the overall size of the data source (after compression). 1429 * 1430 * @param cls must be a 'struct EXTRACTOR_Datasource' 1431 * @param force force computing the size if it is unavailable 1432 * @return overall file size, -1 on error or unknown 1433 */ 1434 int64_t 1435 EXTRACTOR_datasource_get_size_ (void *cls, 1436 int force) 1437 { 1438 struct EXTRACTOR_Datasource *ds = cls; 1439 char buf[32 * 1024]; 1440 uint64_t pos; 1441 1442 if (NULL != ds->cfs) 1443 { 1444 if ( (force) && 1445 (-1 == ds->cfs->uncompressed_size) ) 1446 { 1447 pos = ds->cfs->fpos; 1448 while ( (-1 == ds->cfs->uncompressed_size) && 1449 (-1 != cfs_read (ds->cfs, buf, sizeof (buf))) ) 1450 ; 1451 if (-1 == cfs_seek (ds->cfs, pos, SEEK_SET)) 1452 { 1453 LOG ( 1454 "Serious problem, I moved the buffer to determine the file size but could not restore it...\n"); 1455 return -1; 1456 } 1457 if (-1 == ds->cfs->uncompressed_size) 1458 return -1; 1459 } 1460 return ds->cfs->uncompressed_size; 1461 } 1462 return ds->bfds->fsize; 1463 } 1464 1465 1466 /* end of extractor_datasource.c */