Home | History | Annotate | Download | only in libdwfl
      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 bigger_buffer (struct unzip_state *state, size_t start)
     88 {
     89   size_t more = state->size ? state->size * 2 : start;
     90   char *b = realloc (state->buffer, more);
     91   while (unlikely (b == NULL) && more >= state->size + 1024)
     92     b = realloc (state->buffer, more -= 1024);
     93   if (unlikely (b == NULL))
     94     return false;
     95   state->buffer = b;
     96   state->size = more;
     97   return true;
     98 }
     99 
    100 static inline void
    101 smaller_buffer (struct unzip_state *state, size_t end)
    102 {
    103   state->buffer =
    104       realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
    105   state->size = end;
    106 }
    107 
    108 static inline Dwfl_Error
    109 fail (struct unzip_state *state, Dwfl_Error failure)
    110 {
    111   if (state->input_pos == (off_t) state->mapped_size)
    112     *state->whole = state->input_buffer;
    113   else
    114     {
    115       free (state->input_buffer);
    116       *state->whole = NULL;
    117     }
    118   free (state->buffer);
    119   return failure;
    120 }
    121 
    122 static inline Dwfl_Error
    123 zlib_fail (struct unzip_state *state, int result)
    124 {
    125   switch (result)
    126     {
    127     case Z (MEM_ERROR):
    128       return fail (state, DWFL_E_NOMEM);
    129     case Z (ERRNO):
    130       return fail (state, DWFL_E_ERRNO);
    131     default:
    132       return fail (state, DWFL_E_ZLIB);
    133     }
    134 }
    135 
    136 #if !USE_INFLATE
    137 static Dwfl_Error
    138 open_stream (int fd, off_t start_offset, struct unzip_state *state)
    139 {
    140     int d = dup (fd);
    141     if (unlikely (d < 0))
    142       return DWFL_E_BADELF;
    143     if (start_offset != 0)
    144       {
    145 	off_t off = lseek (d, start_offset, SEEK_SET);
    146 	if (off != start_offset)
    147 	  {
    148 	    close (d);
    149 	    return DWFL_E_BADELF;
    150 	  }
    151       }
    152     state->zf = gzdopen (d, "r");
    153     if (unlikely (state->zf == NULL))
    154       {
    155 	close (d);
    156 	return zlib_fail (state, Z (MEM_ERROR));
    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 unzip (int fd, off_t start_offset,
    174        void *mapped, size_t _mapped_size,
    175        void **_whole, size_t *whole_size)
    176 {
    177   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   if (mapped == NULL)
    191     {
    192       if (*state.whole == NULL)
    193 	{
    194 	  state.input_buffer = malloc (READ_SIZE);
    195 	  if (unlikely (state.input_buffer == NULL))
    196 	    return DWFL_E_NOMEM;
    197 
    198 	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
    199 	  if (unlikely (n < 0))
    200 	    return zlib_fail (&state, Z (ERRNO));
    201 
    202 	  state.input_pos = n;
    203 	  mapped = state.input_buffer;
    204 	  state.mapped_size = n;
    205 	}
    206       else
    207 	{
    208 	  state.input_buffer = *state.whole;
    209 	  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   if (NOMAGIC (MAGIC)
    219 #ifdef MAGIC2
    220       && 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   z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
    233   int result = inflateInit (&z);
    234   if (result != Z (OK))
    235     {
    236       inflateEnd (&z);
    237       return zlib_fail (&state, result);
    238     }
    239 
    240   do
    241     {
    242       if (z.avail_in == 0 && state.input_buffer != NULL)
    243 	{
    244 	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
    245 				   start_offset + state.input_pos);
    246 	  if (unlikely (n < 0))
    247 	    {
    248 	      inflateEnd (&z);
    249 	      return zlib_fail (&state, Z (ERRNO));
    250 	    }
    251 	  z.next_in = state.input_buffer;
    252 	  z.avail_in = n;
    253 	  state.input_pos += n;
    254 	}
    255       if (z.avail_out == 0)
    256 	{
    257 	  ptrdiff_t pos = (void *) z.next_out - state.buffer;
    258 	  if (!bigger_buffer (&state, z.avail_in))
    259 	    {
    260 	      result = Z (MEM_ERROR);
    261 	      break;
    262 	    }
    263 	  z.next_out = state.buffer + pos;
    264 	  z.avail_out = state.size - pos;
    265 	}
    266     }
    267   while ((result = do_inflate (&z)) == Z (OK));
    268 
    269 #ifdef BZLIB
    270   uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
    271 			| z.total_out_lo32);
    272   smaller_buffer (&state, total_out);
    273 #else
    274   smaller_buffer (&state, z.total_out);
    275 #endif
    276 
    277   inflateEnd (&z);
    278 
    279   if (result != Z (STREAM_END))
    280     return zlib_fail (&state, result);
    281 
    282 #else  /* gzip only.  */
    283 
    284   /* Let the decompression library read the file directly.  */
    285 
    286   Dwfl_Error result = open_stream (fd, start_offset, &state);
    287 
    288   if (result == DWFL_E_NOERROR && gzdirect (state.zf))
    289     {
    290       gzclose (state.zf);
    291       return fail (&state, DWFL_E_BADELF);
    292     }
    293 
    294   if (result != DWFL_E_NOERROR)
    295     return fail (&state, result);
    296 
    297   ptrdiff_t pos = 0;
    298   while (1)
    299     {
    300       if (!bigger_buffer (&state, 1024))
    301 	{
    302 	  gzclose (state.zf);
    303 	  return zlib_fail (&state, Z (MEM_ERROR));
    304 	}
    305       int n = gzread (state.zf, state.buffer + pos, state.size - pos);
    306       if (n < 0)
    307 	{
    308 	  int code;
    309 	  gzerror (state.zf, &code);
    310 	  gzclose (state.zf);
    311 	  return zlib_fail (&state, code);
    312 	}
    313       if (n == 0)
    314 	break;
    315       pos += n;
    316     }
    317 
    318   gzclose (state.zf);
    319   smaller_buffer (&state, pos);
    320 #endif
    321 
    322   free (state.input_buffer);
    323 
    324   *state.whole = state.buffer;
    325   *whole_size = state.size;
    326 
    327   return DWFL_E_NOERROR;
    328 }
    329