LCOV - code coverage report
Current view: top level - libdwfl - gzip.c (source / functions) Hit Total Coverage
Test: lcov.out Lines: 31 101 30.7 %
Date: 2017-01-05 09:15:16 Functions: 4 7 57.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
       2             :    Copyright (C) 2009 Red Hat, Inc.
       3             :    This file is part of elfutils.
       4             : 
       5             :    This file is free software; you can redistribute it and/or modify
       6             :    it under the terms of either
       7             : 
       8             :      * the GNU Lesser General Public License as published by the Free
       9             :        Software Foundation; either version 3 of the License, or (at
      10             :        your option) any later version
      11             : 
      12             :    or
      13             : 
      14             :      * the GNU General Public License as published by the Free
      15             :        Software Foundation; either version 2 of the License, or (at
      16             :        your option) any later version
      17             : 
      18             :    or both in parallel, as here.
      19             : 
      20             :    elfutils is distributed in the hope that it will be useful, but
      21             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      22             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      23             :    General Public License for more details.
      24             : 
      25             :    You should have received copies of the GNU General Public License and
      26             :    the GNU Lesser General Public License along with this program.  If
      27             :    not, see <http://www.gnu.org/licenses/>.  */
      28             : 
      29             : #include "libdwflP.h"
      30             : #include "system.h"
      31             : 
      32             : #include <unistd.h>
      33             : 
      34             : #ifdef LZMA
      35             : # define USE_INFLATE    1
      36             : # include <lzma.h>
      37             : # define unzip          __libdw_unlzma
      38             : # define DWFL_E_ZLIB    DWFL_E_LZMA
      39             : # define MAGIC          "\xFD" "7zXZ\0" /* XZ file format.  */
      40             : # define MAGIC2         "\x5d\0"      /* Raw LZMA format.  */
      41             : # define Z(what)        LZMA_##what
      42             : # define LZMA_ERRNO     LZMA_PROG_ERROR
      43             : # define z_stream       lzma_stream
      44             : # define inflateInit(z) lzma_auto_decoder (z, 1 << 30, 0)
      45             : # define do_inflate(z)  lzma_code (z, LZMA_RUN)
      46             : # define inflateEnd(z)  lzma_end (z)
      47             : #elif defined BZLIB
      48             : # define USE_INFLATE    1
      49             : # include <bzlib.h>
      50             : # define unzip          __libdw_bunzip2
      51             : # define DWFL_E_ZLIB    DWFL_E_BZLIB
      52             : # define MAGIC          "BZh"
      53             : # define Z(what)        BZ_##what
      54             : # define BZ_ERRNO       BZ_IO_ERROR
      55             : # define z_stream       bz_stream
      56             : # define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0)
      57             : # define do_inflate(z)  BZ2_bzDecompress (z)
      58             : # define inflateEnd(z)  BZ2_bzDecompressEnd (z)
      59             : #else
      60             : # define USE_INFLATE    0
      61             : # define crc32          loser_crc32
      62             : # include <zlib.h>
      63             : # define unzip          __libdw_gunzip
      64             : # define MAGIC          "\037\213"
      65             : # define Z(what)        Z_##what
      66             : #endif
      67             : 
      68             : #define READ_SIZE               (1 << 20)
      69             : 
      70             : struct unzip_state {
      71             : #if !USE_INFLATE
      72             :   gzFile zf;
      73             : #endif
      74             :   size_t mapped_size;
      75             :   void **whole;
      76             :   void *buffer;
      77             :   size_t size;
      78             :   void *input_buffer;
      79             :   off_t input_pos;
      80             : };
      81             : 
      82             : static inline bool
      83          46 : bigger_buffer (struct unzip_state *state, size_t start)
      84             : {
      85          46 :   size_t more = state->size ? state->size * 2 : start;
      86          46 :   char *b = realloc (state->buffer, more);
      87          46 :   while (unlikely (b == NULL) && more >= state->size + 1024)
      88           0 :     b = realloc (state->buffer, more -= 1024);
      89          46 :   if (unlikely (b == NULL))
      90             :     return false;
      91          46 :   state->buffer = b;
      92          46 :   state->size = more;
      93             :   return true;
      94             : }
      95             : 
      96             : static inline void
      97             : smaller_buffer (struct unzip_state *state, size_t end)
      98             : {
      99          12 :   state->buffer =
     100          12 :       realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
     101          12 :   state->size = end;
     102             : }
     103             : 
     104             : static inline Dwfl_Error
     105           0 : fail (struct unzip_state *state, Dwfl_Error failure)
     106             : {
     107           0 :   if (state->input_pos == (off_t) state->mapped_size)
     108           0 :     *state->whole = state->input_buffer;
     109             :   else
     110             :     {
     111           0 :       free (state->input_buffer);
     112           0 :       *state->whole = NULL;
     113             :     }
     114           0 :   free (state->buffer);
     115           0 :   return failure;
     116             : }
     117             : 
     118             : static inline Dwfl_Error
     119           0 : zlib_fail (struct unzip_state *state, int result)
     120             : {
     121           0 :   switch (result)
     122             :     {
     123             :     case Z (MEM_ERROR):
     124           0 :       return fail (state, DWFL_E_NOMEM);
     125             :     case Z (ERRNO):
     126           0 :       return fail (state, DWFL_E_ERRNO);
     127             :     default:
     128           0 :       return fail (state, DWFL_E_ZLIB);
     129             :     }
     130             : }
     131             : 
     132             : #if !USE_INFLATE
     133             : static Dwfl_Error
     134           0 : open_stream (int fd, off_t start_offset, struct unzip_state *state)
     135             : {
     136           0 :     int d = dup (fd);
     137           0 :     if (unlikely (d < 0))
     138             :       return DWFL_E_BADELF;
     139           0 :     if (start_offset != 0)
     140             :       {
     141           0 :         off_t off = lseek (d, start_offset, SEEK_SET);
     142           0 :         if (off != start_offset)
     143             :           {
     144           0 :             close (d);
     145           0 :             return DWFL_E_BADELF;
     146             :           }
     147             :       }
     148           0 :     state->zf = gzdopen (d, "r");
     149           0 :     if (unlikely (state->zf == NULL))
     150             :       {
     151           0 :         close (d);
     152           0 :         return zlib_fail (state, Z (MEM_ERROR));
     153             :       }
     154             : 
     155             :     /* From here on, zlib will close D.  */
     156             : 
     157             :     return DWFL_E_NOERROR;
     158             : }
     159             : #endif
     160             : 
     161             : /* If this is not a compressed image, return DWFL_E_BADELF.
     162             :    If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
     163             :    Otherwise return an error for bad compressed data or I/O failure.
     164             :    If we return an error after reading the first part of the file,
     165             :    leave that portion malloc'd in *WHOLE, *WHOLE_SIZE.  If *WHOLE
     166             :    is not null on entry, we'll use it in lieu of repeating a read.  */
     167             : 
     168             : Dwfl_Error internal_function
     169          20 : unzip (int fd, off_t start_offset,
     170             :        void *mapped, size_t _mapped_size,
     171             :        void **_whole, size_t *whole_size)
     172             : {
     173          20 :   struct unzip_state state =
     174             :     {
     175             : #if !USE_INFLATE
     176             :       .zf = NULL,
     177             : #endif
     178             :       .mapped_size = _mapped_size,
     179             :       .whole = _whole,
     180             :       .buffer = NULL,
     181             :       .size = 0,
     182             :       .input_buffer = NULL,
     183             :       .input_pos = 0
     184             :     };
     185             : 
     186          20 :   if (mapped == NULL)
     187             :     {
     188           0 :       if (*state.whole == NULL)
     189             :         {
     190           0 :           state.input_buffer = malloc (READ_SIZE);
     191           0 :           if (unlikely (state.input_buffer == NULL))
     192             :             return DWFL_E_NOMEM;
     193             : 
     194           0 :           ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
     195           0 :           if (unlikely (n < 0))
     196           0 :             return zlib_fail (&state, Z (ERRNO));
     197             : 
     198           0 :           state.input_pos = n;
     199           0 :           mapped = state.input_buffer;
     200           0 :           state.mapped_size = n;
     201             :         }
     202             :       else
     203             :         {
     204           0 :           state.input_buffer = *state.whole;
     205           0 :           state.input_pos = state.mapped_size = *whole_size;
     206             :         }
     207             :     }
     208             : 
     209             : #define NOMAGIC(magic) \
     210             :   (state.mapped_size <= sizeof magic || \
     211             :    memcmp (mapped, magic, sizeof magic - 1))
     212             : 
     213             :   /* First, look at the header.  */
     214          20 :   if (NOMAGIC (MAGIC)
     215             : #ifdef MAGIC2
     216           0 :       && NOMAGIC (MAGIC2)
     217             : #endif
     218             :       )
     219             :     /* Not a compressed file.  */
     220             :     return DWFL_E_BADELF;
     221             : 
     222             : #if USE_INFLATE
     223             : 
     224             :   /* This style actually only works with bzlib and liblzma.
     225             :      The stupid zlib interface has nothing to grok the
     226             :      gzip file headers except the slow gzFile interface.  */
     227             : 
     228          12 :   z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
     229          12 :   int result = inflateInit (&z);
     230          12 :   if (result != Z (OK))
     231             :     {
     232           0 :       inflateEnd (&z);
     233           0 :       return zlib_fail (&state, result);
     234             :     }
     235             : 
     236             :   do
     237             :     {
     238          46 :       if (z.avail_in == 0 && state.input_buffer != NULL)
     239             :         {
     240           0 :           ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
     241           0 :                                    start_offset + state.input_pos);
     242           0 :           if (unlikely (n < 0))
     243             :             {
     244           0 :               inflateEnd (&z);
     245           0 :               return zlib_fail (&state, Z (ERRNO));
     246             :             }
     247           0 :           z.next_in = state.input_buffer;
     248           0 :           z.avail_in = n;
     249           0 :           state.input_pos += n;
     250             :         }
     251          46 :       if (z.avail_out == 0)
     252             :         {
     253          46 :           ptrdiff_t pos = (void *) z.next_out - state.buffer;
     254          46 :           if (!bigger_buffer (&state, z.avail_in))
     255             :             {
     256             :               result = Z (MEM_ERROR);
     257             :               break;
     258             :             }
     259          46 :           z.next_out = state.buffer + pos;
     260          46 :           z.avail_out = state.size - pos;
     261             :         }
     262             :     }
     263          46 :   while ((result = do_inflate (&z)) == Z (OK));
     264             : 
     265             : #ifdef BZLIB
     266           0 :   uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
     267           0 :                         | z.total_out_lo32);
     268           0 :   smaller_buffer (&state, total_out);
     269             : #else
     270          24 :   smaller_buffer (&state, z.total_out);
     271             : #endif
     272             : 
     273          12 :   inflateEnd (&z);
     274             : 
     275          12 :   if (result != Z (STREAM_END))
     276           0 :     return zlib_fail (&state, result);
     277             : 
     278             : #else  /* gzip only.  */
     279             : 
     280             :   /* Let the decompression library read the file directly.  */
     281             : 
     282           0 :   Dwfl_Error result = open_stream (fd, start_offset, &state);
     283             : 
     284           0 :   if (result == DWFL_E_NOERROR && gzdirect (state.zf))
     285             :     {
     286           0 :       gzclose (state.zf);
     287           0 :       return fail (&state, DWFL_E_BADELF);
     288             :     }
     289             : 
     290           0 :   if (result != DWFL_E_NOERROR)
     291           0 :     return fail (&state, result);
     292             : 
     293             :   ptrdiff_t pos = 0;
     294             :   while (1)
     295           0 :     {
     296           0 :       if (!bigger_buffer (&state, 1024))
     297             :         {
     298           0 :           gzclose (state.zf);
     299           0 :           return zlib_fail (&state, Z (MEM_ERROR));
     300             :         }
     301           0 :       int n = gzread (state.zf, state.buffer + pos, state.size - pos);
     302           0 :       if (n < 0)
     303             :         {
     304             :           int code;
     305           0 :           gzerror (state.zf, &code);
     306           0 :           gzclose (state.zf);
     307           0 :           return zlib_fail (&state, code);
     308             :         }
     309           0 :       if (n == 0)
     310             :         break;
     311           0 :       pos += n;
     312             :     }
     313             : 
     314           0 :   gzclose (state.zf);
     315           0 :   smaller_buffer (&state, pos);
     316             : #endif
     317             : 
     318          12 :   free (state.input_buffer);
     319             : 
     320          12 :   *state.whole = state.buffer;
     321          12 :   *whole_size = state.size;
     322             : 
     323          12 :   return DWFL_E_NOERROR;
     324             : }

Generated by: LCOV version 1.12