extractor_datasource.c (34409B)
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 if (bfds->fsize < -pos) 359 { 360 LOG ("Invalid seek operation\n"); 361 return -1; 362 } 363 pos = bfds->fsize + pos; 364 /* fall-through! */ 365 case SEEK_SET: 366 if (pos < 0) 367 { 368 LOG ("Invalid seek operation\n"); 369 return -1; 370 } 371 if (pos > bfds->fsize) 372 { 373 LOG ("Invalid seek operation (%lld > %llu) %d\n", 374 (long long) pos, 375 (unsigned long long) bfds->fsize, 376 SEEK_SET == whence); 377 return -1; 378 } 379 if ( (NULL == bfds->buffer) || 380 ( (bfds->fpos <= pos) && 381 (bfds->fpos + bfds->buffer_bytes > pos) ) ) 382 { 383 bfds->buffer_pos = pos - bfds->fpos; 384 return pos; 385 } 386 if (0 != bfds_pick_next_buffer_at (bfds, pos)) 387 { 388 LOG ("seek operation failed\n"); 389 return -1; 390 } 391 ASSERT (pos == bfds->fpos + bfds->buffer_pos); 392 return pos; 393 } 394 return -1; 395 } 396 397 398 /** 399 * Fills 'buf_ptr' with a chunk of data. Will 400 * fail if 'count' exceeds buffer size. 401 * 402 * @param bfds bfds 403 * @param buf_ptr location to store data 404 * @param count number of bytes to read 405 * @return number of bytes (<= count) available at location pointed by buf_ptr, 406 * 0 for end of stream, -1 on error 407 */ 408 static ssize_t 409 bfds_read (struct BufferedFileDataSource *bfds, 410 void *buf_ptr, 411 size_t count) 412 { 413 char *cbuf = buf_ptr; 414 uint64_t old_off; 415 size_t avail; 416 size_t ret; 417 418 old_off = bfds->fpos + bfds->buffer_pos; 419 if (old_off == bfds->fsize) 420 return 0; /* end of stream */ 421 ret = 0; 422 while (count > 0) 423 { 424 if ( (bfds->buffer_bytes == bfds->buffer_pos) && 425 (0 != bfds_pick_next_buffer_at (bfds, 426 bfds->fpos + bfds->buffer_bytes)) ) 427 { 428 /* revert to original position, invalidate buffer */ 429 bfds->fpos = old_off; 430 bfds->buffer_bytes = 0; 431 bfds->buffer_pos = 0; 432 LOG ("read operation failed\n"); 433 return -1; /* getting more failed */ 434 } 435 avail = bfds->buffer_bytes - bfds->buffer_pos; 436 if (avail > count) 437 avail = count; 438 if (0 == avail) 439 break; 440 memcpy (&cbuf[ret], bfds->data + bfds->buffer_pos, avail); 441 bfds->buffer_pos += avail; 442 count -= avail; 443 ret += avail; 444 } 445 return ret; 446 } 447 448 449 #if HAVE_ZLIB 450 451 #define FHCRC 0x02 452 #define FEXTRA 0x04 453 #define FNAME 0x08 454 #define FCOMMENT 0x10 455 456 /** 457 * Initializes gz-decompression object. Might report metadata about 458 * compresse stream, if available. Resets the stream to the beginning. 459 * 460 * @param cfs cfs to initialize 461 * @param proc callback for metadata 462 * @param proc_cls callback cls 463 * @return 1 on success, 0 to terminate extraction, -1 on error 464 */ 465 static int 466 cfs_init_decompressor_zlib (struct CompressedFileSource *cfs, 467 EXTRACTOR_MetaDataProcessor proc, void *proc_cls) 468 { 469 unsigned int gzip_header_length = 10; 470 unsigned char hdata[12]; 471 ssize_t rsize; 472 473 if (0 != bfds_seek (cfs->bfds, 0, SEEK_SET)) 474 { 475 LOG ("Failed to seek to offset 0!\n"); 476 return -1; 477 } 478 /* Process gzip header */ 479 rsize = bfds_read (cfs->bfds, hdata, sizeof (hdata)); 480 if ( (-1 == rsize) || 481 (sizeof (hdata) > (size_t) rsize) ) 482 return -1; 483 if (0 != (hdata[3] & FEXTRA)) 484 gzip_header_length += 2 + (hdata[10] & 0xff) + ((hdata[11] & 0xff) * 256); 485 486 if (0 != (hdata[3] & FNAME)) 487 { 488 /* FNAME set */ 489 char fname[1024]; 490 char *cptr; 491 size_t len; 492 ssize_t buf_bytes; 493 494 if (gzip_header_length > bfds_seek (cfs->bfds, gzip_header_length, 495 SEEK_SET)) 496 { 497 LOG ("Corrupt gzip, failed to seek to end of header\n"); 498 return -1; 499 } 500 buf_bytes = bfds_read (cfs->bfds, fname, sizeof (fname)); 501 if (buf_bytes <= 0) 502 { 503 LOG ("Corrupt gzip, failed to read filename\n"); 504 return -1; 505 } 506 if (NULL == (cptr = memchr (fname, 0, buf_bytes))) 507 { 508 LOG ("Corrupt gzip, failed to read filename terminator\n"); 509 return -1; 510 } 511 len = cptr - fname; 512 if ( (NULL != proc) && 513 (0 != proc (proc_cls, "<zlib>", EXTRACTOR_METATYPE_FILENAME, 514 EXTRACTOR_METAFORMAT_C_STRING, "text/plain", 515 fname, 516 len)) ) 517 return 0; /* done */ 518 gzip_header_length += len + 1; 519 } 520 521 if (0 != (hdata[3] & FCOMMENT)) 522 { 523 /* FCOMMENT set */ 524 char fcomment[1024]; 525 char *cptr; 526 ssize_t buf_bytes; 527 size_t len; 528 529 if (gzip_header_length > bfds_seek (cfs->bfds, gzip_header_length, 530 SEEK_SET)) 531 { 532 LOG ("Corrupt gzip, failed to seek to end of header\n"); 533 return -1; 534 } 535 buf_bytes = bfds_read (cfs->bfds, fcomment, sizeof (fcomment)); 536 if (buf_bytes <= 0) 537 { 538 LOG ("Corrupt gzip, failed to read comment\n"); 539 return -1; 540 } 541 if (NULL == (cptr = memchr (fcomment, 0, buf_bytes))) 542 { 543 LOG ("Corrupt gzip, failed to read comment terminator\n"); 544 return -1; 545 } 546 len = cptr - fcomment; 547 if ( (NULL != proc) && 548 (0 != proc (proc_cls, "<zlib>", EXTRACTOR_METATYPE_COMMENT, 549 EXTRACTOR_METAFORMAT_C_STRING, "text/plain", 550 (const char *) fcomment, 551 len)) ) 552 return 0; /* done */ 553 gzip_header_length += len + 1; 554 } 555 if (0 != (hdata[3] & FHCRC)) 556 gzip_header_length += 2; 557 memset (&cfs->strm, 0, sizeof (z_stream)); 558 559 #ifdef ZLIB_VERNUM 560 /* zlib will take care of its header */ 561 gzip_header_length = 0; 562 #endif 563 cfs->gzip_header_length = gzip_header_length; 564 565 if (cfs->gzip_header_length != 566 bfds_seek (cfs->bfds, cfs->gzip_header_length, SEEK_SET)) 567 { 568 LOG ("Failed to seek to start to initialize gzip decompressor\n"); 569 return -1; 570 } 571 cfs->strm.avail_out = COM_CHUNK_SIZE; 572 /* 573 * note: maybe plain inflateInit(&strm) is adequate, 574 * it looks more backward-compatible also ; 575 * 576 * ZLIB_VERNUM isn't defined by zlib version 1.1.4 ; 577 * there might be a better check. 578 */ 579 if (Z_OK != 580 inflateInit2 (&cfs->strm, 581 #ifdef ZLIB_VERNUM 582 15 + 32 583 #else 584 -MAX_WBITS 585 #endif 586 )) 587 { 588 LOG ("Failed to initialize zlib decompression\n"); 589 return -1; 590 } 591 return 1; 592 } 593 594 595 #endif 596 597 598 #if HAVE_LIBBZ2 599 /** 600 * Initializes bz2-decompression object. Might report metadata about 601 * compresse stream, if available. Resets the stream to the beginning. 602 * 603 * @param cfs cfs to initialize 604 * @param proc callback for metadata 605 * @param proc_cls callback cls 606 * @return 1 on success, -1 on error 607 */ 608 static int 609 cfs_init_decompressor_bz2 (struct CompressedFileSource *cfs, 610 EXTRACTOR_MetaDataProcessor proc, 611 void *proc_cls) 612 { 613 if (0 != 614 bfds_seek (cfs->bfds, 615 0, 616 SEEK_SET)) 617 { 618 LOG ("Failed to seek to start to initialize BZ2 decompressor\n"); 619 return -1; 620 } 621 memset (&cfs->bstrm, 622 0, 623 sizeof (bz_stream)); 624 if (BZ_OK != 625 BZ2_bzDecompressInit (&cfs->bstrm, 626 0, 627 0)) 628 { 629 LOG ("Failed to initialize BZ2 decompressor\n"); 630 return -1; 631 } 632 cfs->bstrm.avail_out = COM_CHUNK_SIZE; 633 return 1; 634 } 635 636 637 #endif 638 639 640 /** 641 * Initializes decompression object. Might report metadata about 642 * compresse stream, if available. Resets the stream to the beginning. 643 * 644 * @param cfs cfs to initialize 645 * @param proc callback for metadata 646 * @param proc_cls callback cls 647 * @return 1 on success, 0 to terminate extraction, -1 on error 648 */ 649 static int 650 cfs_init_decompressor (struct CompressedFileSource *cfs, 651 EXTRACTOR_MetaDataProcessor proc, 652 void *proc_cls) 653 { 654 cfs->result_pos = 0; 655 cfs->fpos = 0; 656 switch (cfs->compression_type) 657 { 658 #if HAVE_ZLIB 659 case COMP_TYPE_ZLIB: 660 return cfs_init_decompressor_zlib (cfs, proc, proc_cls); 661 #endif 662 #if HAVE_LIBBZ2 663 case COMP_TYPE_BZ2: 664 return cfs_init_decompressor_bz2 (cfs, proc, proc_cls); 665 #endif 666 default: 667 LOG ("invalid compression type selected\n"); 668 return -1; 669 } 670 } 671 672 673 #if HAVE_ZLIB 674 /** 675 * Deinitializes gz-decompression object. 676 * 677 * @param cfs cfs to deinitialize 678 * @return 1 on success, -1 on error 679 */ 680 static int 681 cfs_deinit_decompressor_zlib (struct CompressedFileSource *cfs) 682 { 683 inflateEnd (&cfs->strm); 684 return 1; 685 } 686 687 688 #endif 689 690 691 #if HAVE_LIBBZ2 692 /** 693 * Deinitializes bz2-decompression object. 694 * 695 * @param cfs cfs to deinitialize 696 * @return 1 on success, -1 on error 697 */ 698 static int 699 cfs_deinit_decompressor_bz2 (struct CompressedFileSource *cfs) 700 { 701 BZ2_bzDecompressEnd (&cfs->bstrm); 702 return 1; 703 } 704 705 706 #endif 707 708 709 /** 710 * Deinitializes decompression object. 711 * 712 * @param cfs cfs to deinitialize 713 * @return 1 on success, -1 on error 714 */ 715 static int 716 cfs_deinit_decompressor (struct CompressedFileSource *cfs) 717 { 718 switch (cfs->compression_type) 719 { 720 #if HAVE_ZLIB 721 case COMP_TYPE_ZLIB: 722 return cfs_deinit_decompressor_zlib (cfs); 723 #endif 724 #if HAVE_LIBBZ2 725 case COMP_TYPE_BZ2: 726 return cfs_deinit_decompressor_bz2 (cfs); 727 #endif 728 default: 729 LOG ("invalid compression type selected\n"); 730 return -1; 731 } 732 } 733 734 735 /** 736 * Resets the compression stream to begin uncompressing 737 * from the beginning. Used at initialization time, and when 738 * seeking backward. 739 * 740 * @param cfs cfs to reset 741 * @return 1 on success, 0 to terminate extraction, 742 * -1 on error 743 */ 744 static int 745 cfs_reset_stream (struct CompressedFileSource *cfs) 746 { 747 if (-1 == cfs_deinit_decompressor (cfs)) 748 return -1; 749 return cfs_init_decompressor (cfs, NULL, NULL); 750 } 751 752 753 /** 754 * Destroy compressed file source. 755 * 756 * @param cfs source to destroy 757 */ 758 static void 759 cfs_destroy (struct CompressedFileSource *cfs) 760 { 761 cfs_deinit_decompressor (cfs); 762 free (cfs); 763 } 764 765 766 /** 767 * Allocates and initializes new cfs object. 768 * 769 * @param bfds data source to use 770 * @param fsize size of the source 771 * @param compression_type type of compression used 772 * @param proc metadata callback to call with meta data found upon opening 773 * @param proc_cls callback cls 774 * @return newly allocated cfs on success, NULL on error 775 */ 776 static struct CompressedFileSource * 777 cfs_new (struct BufferedFileDataSource *bfds, 778 int64_t fsize, 779 enum ExtractorCompressionType compression_type, 780 EXTRACTOR_MetaDataProcessor proc, void *proc_cls) 781 { 782 struct CompressedFileSource *cfs; 783 784 if (NULL == (cfs = malloc (sizeof (struct CompressedFileSource)))) 785 { 786 LOG_STRERROR ("malloc"); 787 return NULL; 788 } 789 memset (cfs, 0, sizeof (struct CompressedFileSource)); 790 cfs->compression_type = compression_type; 791 cfs->bfds = bfds; 792 cfs->fsize = fsize; 793 cfs->uncompressed_size = -1; 794 if (1 != cfs_init_decompressor (cfs, 795 proc, proc_cls)) 796 { 797 free (cfs); 798 return NULL; 799 } 800 return cfs; 801 } 802 803 804 #if HAVE_ZLIB 805 /** 806 * Fills 'data' with new uncompressed data. Does the actual 807 * decompression. Will set uncompressed_size on the end of compressed 808 * stream. 809 * 810 * @param cfds cfs to read from 811 * @param data where to copy the data 812 * @param size number of bytes available in data 813 * @return number of bytes in data. 0 if no more data can be uncompressed, -1 on error 814 */ 815 static ssize_t 816 cfs_read_zlib (struct CompressedFileSource *cfs, 817 void *data, 818 size_t size) 819 { 820 char *dst = data; 821 int ret; 822 size_t rc; 823 ssize_t in; 824 unsigned char buf[COM_CHUNK_SIZE]; 825 826 if (cfs->fpos == cfs->uncompressed_size) 827 { 828 /* end of file */ 829 return 0; 830 } 831 rc = 0; 832 if (COM_CHUNK_SIZE > cfs->strm.avail_out + cfs->result_pos) 833 { 834 /* got left-over decompressed data from previous round! */ 835 in = COM_CHUNK_SIZE - (cfs->strm.avail_out + cfs->result_pos); 836 if (in > size) 837 in = size; 838 memcpy (&dst[rc], &cfs->result[cfs->result_pos], in); 839 cfs->fpos += in; 840 cfs->result_pos += in; 841 rc += in; 842 } 843 ret = Z_OK; 844 while ( (rc < size) && (Z_STREAM_END != ret) ) 845 { 846 /* read block from original data source */ 847 in = bfds_read (cfs->bfds, 848 buf, sizeof (buf)); 849 if (in < 0) 850 { 851 LOG ("unexpected EOF\n"); 852 return -1; /* unexpected EOF */ 853 } 854 if (0 == in) 855 { 856 cfs->uncompressed_size = cfs->fpos; 857 return rc; 858 } 859 cfs->strm.next_in = buf; 860 cfs->strm.avail_in = (uInt) in; 861 cfs->strm.next_out = (unsigned char *) cfs->result; 862 cfs->strm.avail_out = COM_CHUNK_SIZE; 863 cfs->result_pos = 0; 864 ret = inflate (&cfs->strm, Z_SYNC_FLUSH); 865 if ( (Z_OK != ret) && (Z_STREAM_END != ret) ) 866 { 867 LOG ("unexpected gzip inflate error: %d\n", ret); 868 return -1; /* unexpected error */ 869 } 870 /* go backwards by the number of bytes left in the buffer */ 871 if (-1 == bfds_seek (cfs->bfds, -(int64_t) cfs->strm.avail_in, SEEK_CUR)) 872 { 873 LOG ("seek failed\n"); 874 return -1; 875 } 876 /* copy decompressed bytes to target buffer */ 877 in = COM_CHUNK_SIZE - cfs->strm.avail_out; 878 if (in > size - rc) 879 { 880 if (Z_STREAM_END == ret) 881 { 882 cfs->uncompressed_size = cfs->fpos + in; 883 ret = Z_OK; 884 } 885 in = size - rc; 886 } 887 memcpy (&dst[rc], &cfs->result[cfs->result_pos], in); 888 cfs->fpos += in; 889 cfs->result_pos += in; 890 rc += in; 891 } 892 if (Z_STREAM_END == ret) 893 { 894 cfs->uncompressed_size = cfs->fpos; 895 } 896 return rc; 897 } 898 899 900 #endif 901 902 903 #if HAVE_LIBBZ2 904 /** 905 * Fills 'data' with new uncompressed data. Does the actual 906 * decompression. Will set uncompressed_size on the end of compressed 907 * stream. 908 * 909 * @param cfds cfs to read from 910 * @param data where to copy the data 911 * @param size number of bytes available in data 912 * @return number of bytes in data. 0 if no more data can be uncompressed, -1 on error 913 */ 914 static ssize_t 915 cfs_read_bz2 (struct CompressedFileSource *cfs, 916 void *data, 917 size_t size) 918 { 919 char *dst = data; 920 int ret; 921 size_t rc; 922 ssize_t in; 923 char buf[COM_CHUNK_SIZE]; 924 925 if (cfs->fpos == cfs->uncompressed_size) 926 { 927 /* end of file */ 928 return 0; 929 } 930 rc = 0; 931 if (COM_CHUNK_SIZE > cfs->bstrm.avail_out + cfs->result_pos) 932 { 933 /* got left-over decompressed data from previous round! */ 934 in = COM_CHUNK_SIZE - (cfs->bstrm.avail_out + cfs->result_pos); 935 if (in > size) 936 in = size; 937 memcpy (&dst[rc], &cfs->result[cfs->result_pos], in); 938 cfs->fpos += in; 939 cfs->result_pos += in; 940 rc += in; 941 } 942 ret = BZ_OK; 943 while ( (rc < size) && (BZ_STREAM_END != ret) ) 944 { 945 /* read block from original data source */ 946 in = bfds_read (cfs->bfds, 947 buf, sizeof (buf)); 948 if (in < 0) 949 { 950 LOG ("unexpected EOF\n"); 951 return -1; /* unexpected EOF */ 952 } 953 if (0 == in) 954 { 955 cfs->uncompressed_size = cfs->fpos; 956 return rc; 957 } 958 cfs->bstrm.next_in = buf; 959 cfs->bstrm.avail_in = (unsigned int) in; 960 cfs->bstrm.next_out = cfs->result; 961 cfs->bstrm.avail_out = COM_CHUNK_SIZE; 962 cfs->result_pos = 0; 963 ret = BZ2_bzDecompress (&cfs->bstrm); 964 if ( (BZ_OK != ret) && (BZ_STREAM_END != ret) ) 965 { 966 LOG ("unexpected bzip2 decompress error: %d\n", ret); 967 return -1; /* unexpected error */ 968 } 969 /* go backwards by the number of bytes left in the buffer */ 970 if (-1 == bfds_seek (cfs->bfds, -(int64_t) cfs->bstrm.avail_in, SEEK_CUR)) 971 { 972 LOG ("seek failed\n"); 973 return -1; 974 } 975 /* copy decompressed bytes to target buffer */ 976 in = COM_CHUNK_SIZE - cfs->bstrm.avail_out; 977 if (in > size - rc) 978 { 979 if (BZ_STREAM_END == ret) 980 { 981 cfs->uncompressed_size = cfs->fpos + in; 982 ret = BZ_OK; 983 } 984 in = size - rc; 985 } 986 memcpy (&dst[rc], &cfs->result[cfs->result_pos], in); 987 cfs->fpos += in; 988 cfs->result_pos += in; 989 rc += in; 990 } 991 if (BZ_STREAM_END == ret) 992 { 993 cfs->uncompressed_size = cfs->fpos; 994 } 995 return rc; 996 } 997 998 999 #endif 1000 1001 1002 /** 1003 * Fills 'data' with new uncompressed data. Does the actual 1004 * decompression. Will set uncompressed_size on the end of compressed 1005 * stream. 1006 * 1007 * @param cfds cfs to read from 1008 * @param data where to copy the data 1009 * @param size number of bytes available in data 1010 * @return number of bytes in data. 0 if no more data can be uncompressed, -1 on error 1011 */ 1012 static ssize_t 1013 cfs_read (struct CompressedFileSource *cfs, 1014 void *data, 1015 size_t size) 1016 { 1017 switch (cfs->compression_type) 1018 { 1019 #if HAVE_ZLIB 1020 case COMP_TYPE_ZLIB: 1021 return cfs_read_zlib (cfs, data, size); 1022 #endif 1023 #if HAVE_LIBBZ2 1024 case COMP_TYPE_BZ2: 1025 return cfs_read_bz2 (cfs, data, size); 1026 #endif 1027 default: 1028 LOG ("invalid compression type selected\n"); 1029 return -1; 1030 } 1031 } 1032 1033 1034 /** 1035 * Moves the buffer to 'position' in uncompressed steam. If position 1036 * requires seeking backwards beyond the boundaries of the buffer, resets the 1037 * stream and repeats decompression from the beginning to 'position'. 1038 * 1039 * @param cfs cfs to seek on 1040 * @param position new starting point for the buffer 1041 * @param whence one of the seek constants (SEEK_CUR, SEEK_SET, SEEK_END) 1042 * @return new absolute buffer position, -1 on error or EOS 1043 */ 1044 static int64_t 1045 cfs_seek (struct CompressedFileSource *cfs, 1046 int64_t position, 1047 int whence) 1048 { 1049 uint64_t nposition; 1050 int64_t delta; 1051 1052 switch (whence) 1053 { 1054 case SEEK_CUR: 1055 if (cfs->fpos + position < 0) 1056 { 1057 /* underflow */ 1058 LOG ("Invalid seek operation\n"); 1059 return -1; 1060 } 1061 if ( (-1 != cfs->uncompressed_size) && 1062 (cfs->fpos + position > cfs->uncompressed_size) ) 1063 { 1064 LOG ("Invalid seek operation\n"); 1065 return -1; 1066 } 1067 nposition = cfs->fpos + position; 1068 break; 1069 case SEEK_END: 1070 ASSERT (-1 != cfs->uncompressed_size); 1071 if (position > 0) 1072 { 1073 LOG ("Invalid seek operation\n"); 1074 return -1; 1075 } 1076 if (cfs->uncompressed_size < -position) 1077 { 1078 LOG ("Invalid seek operation\n"); 1079 return -1; 1080 } 1081 nposition = cfs->uncompressed_size + position; 1082 break; 1083 case SEEK_SET: 1084 if (position < 0) 1085 { 1086 LOG ("Invalid seek operation\n"); 1087 return -1; 1088 } 1089 if ( (-1 != cfs->uncompressed_size) && 1090 (cfs->uncompressed_size < position) ) 1091 { 1092 LOG ("Invalid seek operation\n"); 1093 return -1; 1094 } 1095 nposition = (uint64_t) position; 1096 break; 1097 default: 1098 LOG ("Invalid seek operation\n"); 1099 return -1; 1100 } 1101 delta = nposition - cfs->fpos; 1102 if (delta < 0) 1103 { 1104 if (cfs->result_pos >= -delta) 1105 { 1106 cfs->result_pos += delta; 1107 cfs->fpos += delta; 1108 delta = 0; 1109 } 1110 else 1111 { 1112 if (-1 == cfs_reset_stream (cfs)) 1113 { 1114 LOG ("Failed to restart compressed stream for seek operation\n"); 1115 return -1; 1116 } 1117 delta = nposition; 1118 } 1119 } 1120 while (delta > 0) 1121 { 1122 char buf[COM_CHUNK_SIZE]; 1123 size_t max; 1124 int64_t ret; 1125 1126 max = (sizeof (buf) > delta) ? delta : sizeof (buf); 1127 ret = cfs_read (cfs, buf, max); 1128 if (-1 == ret) 1129 { 1130 LOG ("Failed to read decompressed stream for seek operation\n"); 1131 return -1; 1132 } 1133 if (0 == ret) 1134 { 1135 LOG ( 1136 "Reached unexpected end of stream at %llu during seek operation to %llu (%d left)\n", 1137 (unsigned long long) cfs->fpos, 1138 (unsigned long long) nposition, 1139 delta); 1140 return -1; 1141 } 1142 ASSERT (ret <= delta); 1143 delta -= ret; 1144 } 1145 return cfs->fpos; 1146 } 1147 1148 1149 /** 1150 * Detect if we have compressed data on our hands. 1151 * 1152 * @param data pointer to a data buffer or NULL (in case fd is not -1) 1153 * @param fd a file to read data from, or -1 (if data is not NULL) 1154 * @param fsize size of data (if data is not NULL) or of file (if fd is not -1) 1155 * @return -1 to indicate an error, 0 to indicate uncompressed data, or a type (> 0) of compression 1156 */ 1157 static enum ExtractorCompressionType 1158 get_compression_type (struct BufferedFileDataSource *bfds) 1159 { 1160 unsigned char read_data[3]; 1161 1162 if (0 != bfds_seek (bfds, 0, SEEK_SET)) 1163 return COMP_TYPE_INVALID; 1164 if (sizeof (read_data) != 1165 bfds_read (bfds, read_data, sizeof (read_data))) 1166 return COMP_TYPE_UNDEFINED; 1167 1168 #if HAVE_ZLIB 1169 if ( (bfds->fsize >= MIN_ZLIB_HEADER) && 1170 (read_data[0] == 0x1f) && 1171 (read_data[1] == 0x8b) && 1172 (read_data[2] == 0x08) ) 1173 return COMP_TYPE_ZLIB; 1174 #endif 1175 #if HAVE_LIBBZ2 1176 if ( (bfds->fsize >= MIN_BZ2_HEADER) && 1177 (read_data[0] == 'B') && 1178 (read_data[1] == 'Z') && 1179 (read_data[2] == 'h')) 1180 return COMP_TYPE_BZ2; 1181 #endif 1182 return COMP_TYPE_INVALID; 1183 } 1184 1185 1186 /** 1187 * Handle to a datasource we can use for the plugins. 1188 */ 1189 struct EXTRACTOR_Datasource 1190 { 1191 1192 /** 1193 * Underlying buffered data source. 1194 */ 1195 struct BufferedFileDataSource *bfds; 1196 1197 /** 1198 * Compressed file source (NULL if not applicable). 1199 */ 1200 struct CompressedFileSource *cfs; 1201 1202 /** 1203 * Underlying file descriptor, -1 for none. 1204 */ 1205 int fd; 1206 }; 1207 1208 1209 /** 1210 * Create a datasource from a file on disk. 1211 * 1212 * @param filename name of the file on disk 1213 * @param proc metadata callback to call with meta data found upon opening 1214 * @param proc_cls callback cls 1215 * @return handle to the datasource, NULL on error 1216 */ 1217 struct EXTRACTOR_Datasource * 1218 EXTRACTOR_datasource_create_from_file_ (const char *filename, 1219 EXTRACTOR_MetaDataProcessor proc, 1220 void *proc_cls) 1221 { 1222 struct BufferedFileDataSource *bfds; 1223 struct EXTRACTOR_Datasource *ds; 1224 enum ExtractorCompressionType ct; 1225 int fd; 1226 struct stat sb; 1227 int64_t fsize; 1228 int winmode = 0; 1229 #if WINDOWS 1230 winmode = O_BINARY; 1231 #endif 1232 1233 if (-1 == (fd = open (filename, O_RDONLY | O_LARGEFILE | winmode))) 1234 { 1235 LOG_STRERROR_FILE ("open", filename); 1236 return NULL; 1237 } 1238 if ( (0 != fstat (fd, &sb)) || 1239 (S_ISDIR (sb.st_mode)) ) 1240 { 1241 if (! S_ISDIR (sb.st_mode)) 1242 LOG_STRERROR_FILE ("fstat", filename); 1243 else 1244 LOG ("Skipping directory `%s'\n", filename); 1245 (void) close (fd); 1246 return NULL; 1247 } 1248 fsize = (int64_t) sb.st_size; 1249 if (0 == fsize) 1250 { 1251 (void) close (fd); 1252 return NULL; 1253 } 1254 bfds = bfds_new (NULL, fd, fsize); 1255 if (NULL == bfds) 1256 { 1257 (void) close (fd); 1258 return NULL; 1259 } 1260 if (NULL == (ds = malloc (sizeof (struct EXTRACTOR_Datasource)))) 1261 { 1262 LOG_STRERROR ("malloc"); 1263 bfds_delete (bfds); 1264 (void) close (fd); 1265 return NULL; 1266 } 1267 ds->bfds = bfds; 1268 ds->fd = fd; 1269 ds->cfs = NULL; 1270 ct = get_compression_type (bfds); 1271 if ( (COMP_TYPE_ZLIB == ct) || 1272 (COMP_TYPE_BZ2 == ct) ) 1273 { 1274 ds->cfs = cfs_new (bfds, fsize, ct, proc, proc_cls); 1275 if (NULL == ds->cfs) 1276 { 1277 LOG ("Failed to initialize decompressor\n"); 1278 bfds_delete (bfds); 1279 free (ds); 1280 (void) close (fd); 1281 return NULL; 1282 } 1283 } 1284 return ds; 1285 } 1286 1287 1288 /** 1289 * Create a datasource from a buffer in memory. 1290 * 1291 * @param buf data in memory 1292 * @param size number of bytes in 'buf' 1293 * @param proc metadata callback to call with meta data found upon opening 1294 * @param proc_cls callback cls 1295 * @return handle to the datasource 1296 */ 1297 struct EXTRACTOR_Datasource * 1298 EXTRACTOR_datasource_create_from_buffer_ (const char *buf, 1299 size_t size, 1300 EXTRACTOR_MetaDataProcessor proc, 1301 void *proc_cls) 1302 { 1303 struct BufferedFileDataSource *bfds; 1304 struct EXTRACTOR_Datasource *ds; 1305 enum ExtractorCompressionType ct; 1306 1307 if (0 == size) 1308 return NULL; 1309 if (NULL == (bfds = bfds_new (buf, -1, size))) 1310 { 1311 LOG ("Failed to initialize buffer data source\n"); 1312 return NULL; 1313 } 1314 if (NULL == (ds = malloc (sizeof (struct EXTRACTOR_Datasource)))) 1315 { 1316 LOG_STRERROR ("malloc"); 1317 bfds_delete (bfds); 1318 return NULL; 1319 } 1320 ds->bfds = bfds; 1321 ds->fd = -1; 1322 ds->cfs = NULL; 1323 ct = get_compression_type (bfds); 1324 if ( (COMP_TYPE_ZLIB == ct) || 1325 (COMP_TYPE_BZ2 == ct) ) 1326 { 1327 ds->cfs = cfs_new (bfds, size, ct, proc, proc_cls); 1328 if (NULL == ds->cfs) 1329 { 1330 LOG ("Failed to initialize decompressor\n"); 1331 bfds_delete (bfds); 1332 free (ds); 1333 return NULL; 1334 } 1335 } 1336 return ds; 1337 } 1338 1339 1340 /** 1341 * Destroy a data source. 1342 * 1343 * @param ds source to destroy 1344 */ 1345 void 1346 EXTRACTOR_datasource_destroy_ (struct EXTRACTOR_Datasource *ds) 1347 { 1348 if (NULL != ds->cfs) 1349 cfs_destroy (ds->cfs); 1350 bfds_delete (ds->bfds); 1351 if (-1 != ds->fd) 1352 (void) close (ds->fd); 1353 free (ds); 1354 } 1355 1356 1357 /** 1358 * Make 'size' bytes of data from the data source available at 'data'. 1359 * 1360 * @param cls must be a 'struct EXTRACTOR_Datasource' 1361 * @param data where the data should be copied to 1362 * @param size maximum number of bytes requested 1363 * @return number of bytes now available in data (can be smaller than 'size'), 1364 * -1 on error 1365 */ 1366 ssize_t 1367 EXTRACTOR_datasource_read_ (void *cls, 1368 void *data, 1369 size_t size) 1370 { 1371 struct EXTRACTOR_Datasource *ds = cls; 1372 1373 if (NULL != ds->cfs) 1374 return cfs_read (ds->cfs, data, size); 1375 return bfds_read (ds->bfds, data, size); 1376 } 1377 1378 1379 /** 1380 * Seek in the datasource. Use 'SEEK_CUR' for whence and 'pos' of 0 to 1381 * obtain the current position in the file. 1382 * 1383 * @param cls must be a 'struct EXTRACTOR_Datasource' 1384 * @param pos position to seek (see 'man lseek') 1385 * @param whence how to see (absolute to start, relative, absolute to end) 1386 * @return new absolute position, UINT64_MAX on error (i.e. desired position 1387 * does not exist) 1388 */ 1389 int64_t 1390 EXTRACTOR_datasource_seek_ (void *cls, 1391 int64_t pos, 1392 int whence) 1393 { 1394 struct EXTRACTOR_Datasource *ds = cls; 1395 1396 if (NULL != ds->cfs) 1397 { 1398 if ( (SEEK_END == whence) && 1399 (-1 == ds->cfs->uncompressed_size) ) 1400 { 1401 /* need to obtain uncompressed size */ 1402 (void) EXTRACTOR_datasource_get_size_ (ds, 1); 1403 if (-1 == ds->cfs->uncompressed_size) 1404 return -1; 1405 } 1406 return cfs_seek (ds->cfs, pos, whence); 1407 } 1408 return bfds_seek (ds->bfds, pos, whence); 1409 } 1410 1411 1412 /** 1413 * Determine the overall size of the data source (after compression). 1414 * 1415 * @param cls must be a 'struct EXTRACTOR_Datasource' 1416 * @param force force computing the size if it is unavailable 1417 * @return overall file size, -1 on error or unknown 1418 */ 1419 int64_t 1420 EXTRACTOR_datasource_get_size_ (void *cls, 1421 int force) 1422 { 1423 struct EXTRACTOR_Datasource *ds = cls; 1424 char buf[32 * 1024]; 1425 uint64_t pos; 1426 1427 if (NULL != ds->cfs) 1428 { 1429 if ( (force) && 1430 (-1 == ds->cfs->uncompressed_size) ) 1431 { 1432 pos = ds->cfs->fpos; 1433 while ( (-1 == ds->cfs->uncompressed_size) && 1434 (-1 != cfs_read (ds->cfs, buf, sizeof (buf))) ) 1435 ; 1436 if (-1 == cfs_seek (ds->cfs, pos, SEEK_SET)) 1437 { 1438 LOG ( 1439 "Serious problem, I moved the buffer to determine the file size but could not restore it...\n"); 1440 return -1; 1441 } 1442 if (-1 == ds->cfs->uncompressed_size) 1443 return -1; 1444 } 1445 return ds->cfs->uncompressed_size; 1446 } 1447 return ds->bfds->fsize; 1448 } 1449 1450 1451 /* end of extractor_datasource.c */