078e1da488c0d612a370ee29cf4820e479216f83
[platform/upstream/elfutils.git] / libdwfl / gzip.c
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 }