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

Generated by: LCOV version 1.13