libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

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 */