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