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 : : }
|