LCOV - code coverage report
Current view: top level - libdwfl - gzip.c (source / functions) Hit Total Coverage
Test: elfutils-0.191 Lines: 75 145 51.7 %
Date: 2024-03-29 01:01:25 Functions: 7 9 77.8 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 28 83 33.7 %

           Branch data     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                 :            : #ifdef LZMA
      37                 :            : # define USE_INFLATE    1
      38                 :            : # include <lzma.h>
      39                 :            : # define unzip          __libdw_unlzma
      40                 :            : # define DWFL_E_ZLIB    DWFL_E_LZMA
      41                 :            : # define MAGIC          "\xFD" "7zXZ\0" /* XZ file format.  */
      42                 :            : # define MAGIC2         "\x5d\0"      /* Raw LZMA format.  */
      43                 :            : # define Z(what)        LZMA_##what
      44                 :            : # define LZMA_ERRNO     LZMA_PROG_ERROR
      45                 :            : # define z_stream       lzma_stream
      46                 :            : # define inflateInit(z) lzma_auto_decoder (z, 1 << 30, 0)
      47                 :            : # define do_inflate(z)  lzma_code (z, LZMA_RUN)
      48                 :            : # define inflateEnd(z)  lzma_end (z)
      49                 :            : #elif defined ZSTD
      50                 :            : # define USE_INFLATE    1
      51                 :            : # include <zstd.h>
      52                 :            : # define unzip          __libdw_unzstd
      53                 :            : # define DWFL_E_ZLIB    DWFL_E_ZSTD
      54                 :            : # define MAGIC          "\x28\xb5\x2f\xfd"
      55                 :            : #elif defined BZLIB
      56                 :            : # define USE_INFLATE    1
      57                 :            : # include <bzlib.h>
      58                 :            : # define unzip          __libdw_bunzip2
      59                 :            : # define DWFL_E_ZLIB    DWFL_E_BZLIB
      60                 :            : # define MAGIC          "BZh"
      61                 :            : # define Z(what)        BZ_##what
      62                 :            : # define BZ_ERRNO       BZ_IO_ERROR
      63                 :            : # define z_stream       bz_stream
      64                 :            : # define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0)
      65                 :            : # define do_inflate(z)  BZ2_bzDecompress (z)
      66                 :            : # define inflateEnd(z)  BZ2_bzDecompressEnd (z)
      67                 :            : #else
      68                 :            : # define USE_INFLATE    0
      69                 :            : # define crc32          loser_crc32
      70                 :            : # include <zlib.h>
      71                 :            : # define unzip          __libdw_gunzip
      72                 :            : # define MAGIC          "\037\213"
      73                 :            : # define Z(what)        Z_##what
      74                 :            : #endif
      75                 :            : 
      76                 :            : #define READ_SIZE               (1 << 20)
      77                 :            : 
      78                 :            : struct unzip_state {
      79                 :            : #if !USE_INFLATE
      80                 :            :   gzFile zf;
      81                 :            : #endif
      82                 :            :   size_t mapped_size;
      83                 :            :   void **whole;
      84                 :            :   void *buffer;
      85                 :            :   size_t size;
      86                 :            :   void *input_buffer;
      87                 :            :   off_t input_pos;
      88                 :            : };
      89                 :            : 
      90                 :            : static inline bool
      91                 :        428 : bigger_buffer (struct unzip_state *state, size_t start)
      92                 :            : {
      93         [ +  + ]:        428 :   size_t more = state->size ? state->size * 2 : start;
      94                 :        428 :   char *b = realloc (state->buffer, more);
      95   [ -  +  -  - ]:        428 :   while (unlikely (b == NULL) && more >= state->size + 1024)
      96                 :          0 :     b = realloc (state->buffer, more -= 1024);
      97         [ +  - ]:        428 :   if (unlikely (b == NULL))
      98                 :            :     return false;
      99                 :        428 :   state->buffer = b;
     100                 :        428 :   state->size = more;
     101                 :        428 :   return true;
     102                 :            : }
     103                 :            : 
     104                 :            : static inline void
     105                 :        110 : smaller_buffer (struct unzip_state *state, size_t end)
     106                 :            : {
     107                 :        220 :   state->buffer =
     108   [ -  +  -  - ]:        110 :       realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
     109                 :        110 :   state->size = end;
     110                 :        110 : }
     111                 :            : 
     112                 :            : static inline Dwfl_Error
     113                 :         36 : fail (struct unzip_state *state, Dwfl_Error failure)
     114                 :            : {
     115         [ -  + ]:         36 :   if (state->input_pos == (off_t) state->mapped_size)
     116                 :          0 :     *state->whole = state->input_buffer;
     117                 :            :   else
     118                 :            :     {
     119                 :         36 :       free (state->input_buffer);
     120                 :         36 :       *state->whole = NULL;
     121                 :            :     }
     122                 :         36 :   free (state->buffer);
     123                 :         36 :   return failure;
     124                 :            : }
     125                 :            : 
     126                 :            : #ifndef ZSTD
     127                 :            : static inline Dwfl_Error
     128                 :          0 : zlib_fail (struct unzip_state *state, int result)
     129                 :            : {
     130      [ #  #  # ]:          0 :   switch (result)
     131                 :            :     {
     132                 :          0 :     case Z (MEM_ERROR):
     133                 :          0 :       return fail (state, DWFL_E_NOMEM);
     134                 :          0 :     case Z (ERRNO):
     135                 :          0 :       return fail (state, DWFL_E_ERRNO);
     136                 :          0 :     default:
     137                 :          0 :       return fail (state, DWFL_E_ZLIB);
     138                 :            :     }
     139                 :            : }
     140                 :            : #endif
     141                 :            : 
     142                 :            : #if !USE_INFLATE
     143                 :            : static Dwfl_Error
     144                 :          0 : open_stream (int fd, off_t start_offset, struct unzip_state *state)
     145                 :            : {
     146                 :          0 :     int d = dup (fd);
     147         [ #  # ]:          0 :     if (unlikely (d < 0))
     148                 :            :       return DWFL_E_ERRNO;
     149         [ #  # ]:          0 :     if (start_offset != 0)
     150                 :            :       {
     151                 :          0 :         off_t off = lseek (d, start_offset, SEEK_SET);
     152         [ #  # ]:          0 :         if (off != start_offset)
     153                 :            :           {
     154                 :          0 :             close (d);
     155                 :          0 :             return DWFL_E_ERRNO;
     156                 :            :           }
     157                 :            :       }
     158                 :          0 :     state->zf = gzdopen (d, "r");
     159         [ #  # ]:          0 :     if (unlikely (state->zf == NULL))
     160                 :            :       {
     161                 :          0 :         close (d);
     162                 :          0 :         return DWFL_E_NOMEM;
     163                 :            :       }
     164                 :            : 
     165                 :            :     /* From here on, zlib will close D.  */
     166                 :            : 
     167                 :            :     return DWFL_E_NOERROR;
     168                 :            : }
     169                 :            : #endif
     170                 :            : 
     171                 :            : /* If this is not a compressed image, return DWFL_E_BADELF.
     172                 :            :    If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
     173                 :            :    Otherwise return an error for bad compressed data or I/O failure.
     174                 :            :    If we return an error after reading the first part of the file,
     175                 :            :    leave that portion malloc'd in *WHOLE, *WHOLE_SIZE.  If *WHOLE
     176                 :            :    is not null on entry, we'll use it in lieu of repeating a read.  */
     177                 :            : 
     178                 :            : Dwfl_Error internal_function
     179                 :        146 : unzip (int fd, off_t start_offset,
     180                 :            :        void *mapped, size_t _mapped_size,
     181                 :            :        void **_whole, size_t *whole_size)
     182                 :            : {
     183                 :        146 :   struct unzip_state state =
     184                 :            :     {
     185                 :            : #if !USE_INFLATE
     186                 :            :       .zf = NULL,
     187                 :            : #endif
     188                 :            :       .mapped_size = _mapped_size,
     189                 :            :       .whole = _whole,
     190                 :            :       .buffer = NULL,
     191                 :            :       .size = 0,
     192                 :            :       .input_buffer = NULL,
     193                 :            :       .input_pos = 0
     194                 :            :     };
     195                 :            : 
     196         [ -  + ]:        146 :   if (mapped == NULL)
     197                 :            :     {
     198         [ #  # ]:          0 :       if (*state.whole == NULL)
     199                 :            :         {
     200                 :          0 :           state.input_buffer = malloc (READ_SIZE);
     201         [ #  # ]:          0 :           if (unlikely (state.input_buffer == NULL))
     202                 :            :             return DWFL_E_NOMEM;
     203                 :            : 
     204                 :          0 :           ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
     205         [ #  # ]:          0 :           if (unlikely (n < 0))
     206                 :          0 :             return fail (&state, DWFL_E_ERRNO);
     207                 :            : 
     208                 :          0 :           state.input_pos = n;
     209                 :          0 :           mapped = state.input_buffer;
     210                 :          0 :           state.mapped_size = n;
     211                 :            :         }
     212                 :            :       else
     213                 :            :         {
     214                 :          0 :           state.input_buffer = *state.whole;
     215                 :          0 :           state.input_pos = state.mapped_size = *whole_size;
     216                 :            :         }
     217                 :            :     }
     218                 :            : 
     219                 :            : #define NOMAGIC(magic) \
     220                 :            :   (state.mapped_size <= sizeof magic || \
     221                 :            :    memcmp (mapped, magic, sizeof magic - 1))
     222                 :            : 
     223                 :            :   /* First, look at the header.  */
     224   [ +  -  +  + ]:        146 :   if (NOMAGIC (MAGIC)
     225                 :            : #ifdef MAGIC2
     226   [ +  -  +  - ]:          4 :       && NOMAGIC (MAGIC2)
     227                 :            : #endif
     228                 :            :       )
     229                 :            :     /* Not a compressed file.  */
     230                 :         36 :     return fail (&state, DWFL_E_BADELF);
     231                 :            : 
     232                 :            : #ifdef ZSTD
     233                 :            :   /* special case for libzstd since it is slightly different from the
     234                 :            :      API provided by bzlib and liblzma.  */
     235                 :            : 
     236                 :          4 :   void *next_in = mapped;
     237                 :          4 :   size_t avail_in = state.mapped_size;
     238                 :          4 :   void *next_out = NULL;
     239                 :          4 :   size_t avail_out = 0;
     240                 :          4 :   size_t total_out = 0;
     241                 :            : 
     242                 :          4 :   size_t result;
     243                 :          4 :   ZSTD_DCtx *dctx = ZSTD_createDCtx();
     244         [ -  + ]:          4 :   if (dctx == NULL)
     245                 :          0 :     return fail (&state, DWFL_E_NOMEM);
     246                 :            : 
     247                 :         12 :   do
     248                 :            :     {
     249                 :         12 :       if (avail_in == 0 && state.input_buffer != NULL)
     250                 :            :         {
     251                 :            :           ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
     252                 :            :                                    start_offset + state.input_pos);
     253                 :            :           if (unlikely (n < 0))
     254                 :            :             {
     255                 :            :               ZSTD_freeDCtx (dctx);
     256                 :          0 :               return fail (&state, DWFL_E_ERRNO);
     257                 :            :             }
     258                 :            :           next_in = state.input_buffer;
     259                 :            :           avail_in = n;
     260                 :            :           state.input_pos += n;
     261                 :            :         }
     262         [ +  - ]:         12 :       if (avail_out == 0)
     263                 :            :         {
     264                 :         12 :           ptrdiff_t pos = (void *) next_out - state.buffer;
     265         [ -  + ]:         12 :           if (!bigger_buffer (&state, avail_in))
     266                 :            :             {
     267                 :          0 :               ZSTD_freeDCtx (dctx);
     268                 :          0 :               return fail (&state, DWFL_E_NOMEM);
     269                 :            :             }
     270                 :         12 :           next_out = state.buffer + pos;
     271                 :         12 :           avail_out = state.size - pos;
     272                 :            :         }
     273                 :            : 
     274                 :         12 :       ZSTD_inBuffer input = { next_in, avail_in, 0 };
     275                 :         12 :       ZSTD_outBuffer output = { next_out, avail_out, 0 };
     276                 :         12 :       result = ZSTD_decompressStream (dctx, &output, &input);
     277                 :            : 
     278         [ +  - ]:         12 :       if (! ZSTD_isError (result))
     279                 :            :         {
     280                 :         12 :           total_out += output.pos;
     281                 :         12 :           next_out += output.pos;
     282                 :         12 :           avail_out -= output.pos;
     283                 :         12 :           next_in += input.pos;
     284                 :         12 :           avail_in -= input.pos;
     285                 :            :         }
     286                 :            : 
     287         [ +  + ]:         12 :       if (result == 0)
     288                 :            :         break;
     289                 :            :     }
     290   [ +  -  +  - ]:          8 :   while (avail_in > 0 && ! ZSTD_isError (result));
     291                 :            : 
     292                 :          4 :   ZSTD_freeDCtx (dctx);
     293                 :            : 
     294         [ -  + ]:          4 :   if (ZSTD_isError (result))
     295                 :          0 :     return fail (&state, DWFL_E_ZSTD);
     296                 :            : 
     297                 :          4 :   smaller_buffer (&state, total_out);
     298                 :            : 
     299                 :            : #elif USE_INFLATE
     300                 :            : 
     301                 :            :   /* This style actually only works with bzlib and liblzma.
     302                 :            :      The stupid zlib interface has nothing to grok the
     303                 :            :      gzip file headers except the slow gzFile interface.  */
     304                 :            : 
     305                 :        106 :   z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
     306                 :        106 :   int result = inflateInit (&z);
     307         [ -  + ]:        106 :   if (result != Z (OK))
     308                 :            :     {
     309                 :          0 :       inflateEnd (&z);
     310                 :          0 :       return zlib_fail (&state, result);
     311                 :            :     }
     312                 :            : 
     313                 :        416 :   do
     314                 :            :     {
     315   [ -  +  -  - ]:        416 :       if (z.avail_in == 0 && state.input_buffer != NULL)
     316                 :            :         {
     317                 :          0 :           ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
     318                 :          0 :                                    start_offset + state.input_pos);
     319         [ #  # ]:          0 :           if (unlikely (n < 0))
     320                 :            :             {
     321                 :          0 :               inflateEnd (&z);
     322                 :          0 :               return zlib_fail (&state, Z (ERRNO));
     323                 :            :             }
     324                 :          0 :           z.next_in = state.input_buffer;
     325                 :          0 :           z.avail_in = n;
     326                 :          0 :           state.input_pos += n;
     327                 :            :         }
     328         [ +  - ]:        416 :       if (z.avail_out == 0)
     329                 :            :         {
     330                 :        416 :           ptrdiff_t pos = (void *) z.next_out - state.buffer;
     331         [ +  - ]:        416 :           if (!bigger_buffer (&state, z.avail_in))
     332                 :            :             {
     333                 :            :               result = Z (MEM_ERROR);
     334                 :            :               break;
     335                 :            :             }
     336                 :        416 :           z.next_out = state.buffer + pos;
     337                 :        416 :           z.avail_out = state.size - pos;
     338                 :            :         }
     339                 :            :     }
     340         [ +  + ]:        416 :   while ((result = do_inflate (&z)) == Z (OK));
     341                 :            : 
     342                 :            : #ifdef BZLIB
     343                 :          4 :   uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
     344                 :          4 :                         | z.total_out_lo32);
     345                 :          4 :   smaller_buffer (&state, total_out);
     346                 :            : #else
     347                 :        102 :   smaller_buffer (&state, z.total_out);
     348                 :            : #endif
     349                 :            : 
     350                 :        106 :   inflateEnd (&z);
     351                 :            : 
     352         [ -  + ]:        106 :   if (result != Z (STREAM_END))
     353                 :          0 :     return zlib_fail (&state, result);
     354                 :            : 
     355                 :            : #else  /* gzip only.  */
     356                 :            : 
     357                 :            :   /* Let the decompression library read the file directly.  */
     358                 :            : 
     359                 :          0 :   Dwfl_Error result = open_stream (fd, start_offset, &state);
     360                 :            : 
     361   [ #  #  #  # ]:          0 :   if (result == DWFL_E_NOERROR && gzdirect (state.zf))
     362                 :            :     {
     363                 :          0 :       gzclose (state.zf);
     364                 :            :       /* Not a compressed stream after all.  */
     365                 :          0 :       return fail (&state, DWFL_E_BADELF);
     366                 :            :     }
     367                 :            : 
     368                 :          0 :   if (result != DWFL_E_NOERROR)
     369                 :          0 :     return fail (&state, result);
     370                 :            : 
     371                 :            :   ptrdiff_t pos = 0;
     372                 :          0 :   while (1)
     373                 :          0 :     {
     374         [ #  # ]:          0 :       if (!bigger_buffer (&state, 1024))
     375                 :            :         {
     376                 :          0 :           gzclose (state.zf);
     377                 :          0 :           return zlib_fail (&state, Z (MEM_ERROR));
     378                 :            :         }
     379                 :          0 :       int n = gzread (state.zf, state.buffer + pos, state.size - pos);
     380         [ #  # ]:          0 :       if (n < 0)
     381                 :            :         {
     382                 :          0 :           int code;
     383                 :          0 :           gzerror (state.zf, &code);
     384                 :          0 :           gzclose (state.zf);
     385                 :          0 :           return zlib_fail (&state, code);
     386                 :            :         }
     387         [ #  # ]:          0 :       if (n == 0)
     388                 :            :         break;
     389                 :          0 :       pos += n;
     390                 :            :     }
     391                 :            : 
     392                 :          0 :   gzclose (state.zf);
     393                 :          0 :   smaller_buffer (&state, pos);
     394                 :            : #endif
     395                 :            : 
     396                 :        110 :   free (state.input_buffer);
     397                 :            : 
     398                 :        110 :   *state.whole = state.buffer;
     399                 :        110 :   *whole_size = state.size;
     400                 :            : 
     401                 :        110 :   return DWFL_E_NOERROR;
     402                 :            : }

Generated by: LCOV version 1.16