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 : 354 : bigger_buffer (struct unzip_state *state, size_t start)
92 : : {
93 [ + + ]: 354 : size_t more = state->size ? state->size * 2 : start;
94 : 354 : char *b = realloc (state->buffer, more);
95 [ - + - - ]: 354 : while (unlikely (b == NULL) && more >= state->size + 1024)
96 : 0 : b = realloc (state->buffer, more -= 1024);
97 [ + - ]: 354 : if (unlikely (b == NULL))
98 : : return false;
99 : 354 : state->buffer = b;
100 : 354 : state->size = more;
101 : 354 : return true;
102 : : }
103 : :
104 : : static inline void
105 : 92 : smaller_buffer (struct unzip_state *state, size_t end)
106 : : {
107 : 184 : state->buffer =
108 [ - + - - ]: 92 : realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
109 : 92 : state->size = end;
110 : 92 : }
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 : 128 : unzip (int fd, off_t start_offset,
180 : : void *mapped, size_t _mapped_size,
181 : : void **_whole, size_t *whole_size)
182 : : {
183 : 128 : 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 [ - + ]: 128 : 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 : mapped = state.input_buffer;
216 : 0 : state.input_pos = state.mapped_size = *whole_size;
217 : : }
218 : : }
219 : :
220 : : #define NOMAGIC(magic) \
221 : : (state.mapped_size <= sizeof magic || \
222 : : memcmp (mapped, magic, sizeof magic - 1))
223 : :
224 : : /* First, look at the header. */
225 [ + - + + ]: 128 : if (NOMAGIC (MAGIC)
226 : : #ifdef MAGIC2
227 [ + - + - ]: 4 : && NOMAGIC (MAGIC2)
228 : : #endif
229 : : )
230 : : /* Not a compressed file. */
231 : 36 : return fail (&state, DWFL_E_BADELF);
232 : :
233 : : #ifdef ZSTD
234 : : /* special case for libzstd since it is slightly different from the
235 : : API provided by bzlib and liblzma. */
236 : :
237 : 4 : void *next_in = mapped;
238 : 4 : size_t avail_in = state.mapped_size;
239 : 4 : void *next_out = NULL;
240 : 4 : size_t avail_out = 0;
241 : 4 : size_t total_out = 0;
242 : :
243 : 4 : size_t result;
244 : 4 : ZSTD_DCtx *dctx = ZSTD_createDCtx();
245 [ - + ]: 4 : if (dctx == NULL)
246 : 0 : return fail (&state, DWFL_E_NOMEM);
247 : :
248 : 12 : do
249 : : {
250 : 12 : if (avail_in == 0 && state.input_buffer != NULL)
251 : : {
252 : : ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
253 : : start_offset + state.input_pos);
254 : : if (unlikely (n < 0))
255 : : {
256 : : ZSTD_freeDCtx (dctx);
257 : 0 : return fail (&state, DWFL_E_ERRNO);
258 : : }
259 : : next_in = state.input_buffer;
260 : : avail_in = n;
261 : : state.input_pos += n;
262 : : }
263 [ + - ]: 12 : if (avail_out == 0)
264 : : {
265 : 12 : ptrdiff_t pos = (void *) next_out - state.buffer;
266 [ - + ]: 12 : if (!bigger_buffer (&state, avail_in))
267 : : {
268 : 0 : ZSTD_freeDCtx (dctx);
269 : 0 : return fail (&state, DWFL_E_NOMEM);
270 : : }
271 : 12 : next_out = state.buffer + pos;
272 : 12 : avail_out = state.size - pos;
273 : : }
274 : :
275 : 12 : ZSTD_inBuffer input = { next_in, avail_in, 0 };
276 : 12 : ZSTD_outBuffer output = { next_out, avail_out, 0 };
277 : 12 : result = ZSTD_decompressStream (dctx, &output, &input);
278 : :
279 [ + - ]: 12 : if (! ZSTD_isError (result))
280 : : {
281 : 12 : total_out += output.pos;
282 : 12 : next_out += output.pos;
283 : 12 : avail_out -= output.pos;
284 : 12 : next_in += input.pos;
285 : 12 : avail_in -= input.pos;
286 : : }
287 : :
288 [ + + ]: 12 : if (result == 0)
289 : : break;
290 : : }
291 [ + - + - ]: 8 : while (avail_in > 0 && ! ZSTD_isError (result));
292 : :
293 : 4 : ZSTD_freeDCtx (dctx);
294 : :
295 [ - + ]: 4 : if (ZSTD_isError (result))
296 : 0 : return fail (&state, DWFL_E_ZSTD);
297 : :
298 : 4 : smaller_buffer (&state, total_out);
299 : :
300 : : #elif USE_INFLATE
301 : :
302 : : /* This style actually only works with bzlib and liblzma.
303 : : The stupid zlib interface has nothing to grok the
304 : : gzip file headers except the slow gzFile interface. */
305 : :
306 : 88 : z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
307 : 88 : int result = inflateInit (&z);
308 [ - + ]: 88 : if (result != Z (OK))
309 : : {
310 : 0 : inflateEnd (&z);
311 : 0 : return zlib_fail (&state, result);
312 : : }
313 : :
314 : 342 : do
315 : : {
316 [ - + - - ]: 342 : if (z.avail_in == 0 && state.input_buffer != NULL)
317 : : {
318 : 0 : ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
319 : 0 : start_offset + state.input_pos);
320 [ # # ]: 0 : if (unlikely (n < 0))
321 : : {
322 : 0 : inflateEnd (&z);
323 : 0 : return zlib_fail (&state, Z (ERRNO));
324 : : }
325 : 0 : z.next_in = state.input_buffer;
326 : 0 : z.avail_in = n;
327 : 0 : state.input_pos += n;
328 : : }
329 [ + - ]: 342 : if (z.avail_out == 0)
330 : : {
331 : 342 : ptrdiff_t pos = (void *) z.next_out - state.buffer;
332 [ + - ]: 342 : if (!bigger_buffer (&state, z.avail_in))
333 : : {
334 : : result = Z (MEM_ERROR);
335 : : break;
336 : : }
337 : 342 : z.next_out = state.buffer + pos;
338 : 342 : z.avail_out = state.size - pos;
339 : : }
340 : : }
341 [ + + ]: 342 : while ((result = do_inflate (&z)) == Z (OK));
342 : :
343 : : #ifdef BZLIB
344 : 4 : uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
345 : 4 : | z.total_out_lo32);
346 : 4 : smaller_buffer (&state, total_out);
347 : : #else
348 : 84 : smaller_buffer (&state, z.total_out);
349 : : #endif
350 : :
351 : 88 : inflateEnd (&z);
352 : :
353 [ - + ]: 88 : if (result != Z (STREAM_END))
354 : 0 : return zlib_fail (&state, result);
355 : :
356 : : #else /* gzip only. */
357 : :
358 : : /* Let the decompression library read the file directly. */
359 : :
360 : 0 : Dwfl_Error result = open_stream (fd, start_offset, &state);
361 : :
362 [ # # # # ]: 0 : if (result == DWFL_E_NOERROR && gzdirect (state.zf))
363 : : {
364 : 0 : gzclose (state.zf);
365 : : /* Not a compressed stream after all. */
366 : 0 : return fail (&state, DWFL_E_BADELF);
367 : : }
368 : :
369 : 0 : if (result != DWFL_E_NOERROR)
370 : 0 : return fail (&state, result);
371 : :
372 : : ptrdiff_t pos = 0;
373 : 0 : while (1)
374 : 0 : {
375 [ # # ]: 0 : if (!bigger_buffer (&state, 1024))
376 : : {
377 : 0 : gzclose (state.zf);
378 : 0 : return zlib_fail (&state, Z (MEM_ERROR));
379 : : }
380 : 0 : int n = gzread (state.zf, state.buffer + pos, state.size - pos);
381 [ # # ]: 0 : if (n < 0)
382 : : {
383 : 0 : int code;
384 : 0 : gzerror (state.zf, &code);
385 : 0 : gzclose (state.zf);
386 : 0 : return zlib_fail (&state, code);
387 : : }
388 [ # # ]: 0 : if (n == 0)
389 : : break;
390 : 0 : pos += n;
391 : : }
392 : :
393 : 0 : gzclose (state.zf);
394 : 0 : smaller_buffer (&state, pos);
395 : : #endif
396 : :
397 : 92 : free (state.input_buffer);
398 : :
399 : 92 : *state.whole = state.buffer;
400 : 92 : *whole_size = state.size;
401 : :
402 : 92 : return DWFL_E_NOERROR;
403 : : }
|