libextractor

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

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