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