/src/elfutils/libdwfl/gzip.c
Line | Count | Source (jump to first uncovered line) |
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 | 0 | # define Z(what) Z_##what |
74 | | #endif |
75 | | |
76 | 0 | #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 | | bigger_buffer (struct unzip_state *state, size_t start) |
92 | 0 | { |
93 | 0 | size_t more = state->size ? state->size * 2 : start; |
94 | 0 | char *b = realloc (state->buffer, more); |
95 | 0 | while (unlikely (b == NULL) && more >= state->size + 1024) |
96 | 0 | b = realloc (state->buffer, more -= 1024); |
97 | 0 | if (unlikely (b == NULL)) |
98 | 0 | return false; |
99 | 0 | state->buffer = b; |
100 | 0 | state->size = more; |
101 | 0 | return true; |
102 | 0 | } |
103 | | |
104 | | static inline void |
105 | | smaller_buffer (struct unzip_state *state, size_t end) |
106 | 0 | { |
107 | 0 | state->buffer = |
108 | 0 | realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer; |
109 | 0 | state->size = end; |
110 | 0 | } |
111 | | |
112 | | static inline Dwfl_Error |
113 | | fail (struct unzip_state *state, Dwfl_Error failure) |
114 | 227 | { |
115 | 227 | if (state->input_pos == (off_t) state->mapped_size) |
116 | 0 | *state->whole = state->input_buffer; |
117 | 227 | else |
118 | 227 | { |
119 | 227 | free (state->input_buffer); |
120 | 227 | *state->whole = NULL; |
121 | 227 | } |
122 | 227 | free (state->buffer); |
123 | 227 | return failure; |
124 | 227 | } |
125 | | |
126 | | #ifndef ZSTD |
127 | | static inline Dwfl_Error |
128 | | zlib_fail (struct unzip_state *state, int result) |
129 | 0 | { |
130 | 0 | switch (result) |
131 | 0 | { |
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 | 0 | } |
139 | 0 | } |
140 | | #endif |
141 | | |
142 | | #if !USE_INFLATE |
143 | | static Dwfl_Error |
144 | | open_stream (int fd, off_t start_offset, struct unzip_state *state) |
145 | 0 | { |
146 | 0 | int d = dup (fd); |
147 | 0 | if (unlikely (d < 0)) |
148 | 0 | return DWFL_E_ERRNO; |
149 | 0 | if (start_offset != 0) |
150 | 0 | { |
151 | 0 | off_t off = lseek (d, start_offset, SEEK_SET); |
152 | 0 | if (off != start_offset) |
153 | 0 | { |
154 | 0 | close (d); |
155 | 0 | return DWFL_E_ERRNO; |
156 | 0 | } |
157 | 0 | } |
158 | 0 | state->zf = gzdopen (d, "r"); |
159 | 0 | if (unlikely (state->zf == NULL)) |
160 | 0 | { |
161 | 0 | close (d); |
162 | 0 | return DWFL_E_NOMEM; |
163 | 0 | } |
164 | | |
165 | | /* From here on, zlib will close D. */ |
166 | | |
167 | 0 | return DWFL_E_NOERROR; |
168 | 0 | } |
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 | | unzip (int fd, off_t start_offset, |
180 | | void *mapped, size_t _mapped_size, |
181 | | void **_whole, size_t *whole_size) |
182 | 227 | { |
183 | 227 | struct unzip_state state = |
184 | 227 | { |
185 | 227 | #if !USE_INFLATE |
186 | 227 | .zf = NULL, |
187 | 227 | #endif |
188 | 227 | .mapped_size = _mapped_size, |
189 | 227 | .whole = _whole, |
190 | 227 | .buffer = NULL, |
191 | 227 | .size = 0, |
192 | 227 | .input_buffer = NULL, |
193 | 227 | .input_pos = 0 |
194 | 227 | }; |
195 | | |
196 | 227 | if (mapped == NULL) |
197 | 0 | { |
198 | 0 | if (*state.whole == NULL) |
199 | 0 | { |
200 | 0 | state.input_buffer = malloc (READ_SIZE); |
201 | 0 | if (unlikely (state.input_buffer == NULL)) |
202 | 0 | 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 | 0 | } |
212 | 0 | else |
213 | 0 | { |
214 | 0 | state.input_buffer = *state.whole; |
215 | 0 | mapped = state.input_buffer; |
216 | 0 | state.input_pos = state.mapped_size = *whole_size; |
217 | 0 | } |
218 | 0 | } |
219 | | |
220 | 227 | #define NOMAGIC(magic) \ |
221 | 227 | (state.mapped_size <= sizeof magic || \ |
222 | 227 | memcmp (mapped, magic, sizeof magic - 1)) |
223 | | |
224 | | /* First, look at the header. */ |
225 | 227 | if (NOMAGIC (MAGIC) |
226 | | #ifdef MAGIC2 |
227 | | && NOMAGIC (MAGIC2) |
228 | | #endif |
229 | 227 | ) |
230 | | /* Not a compressed file. */ |
231 | 227 | 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 | | void *next_in = mapped; |
238 | | size_t avail_in = state.mapped_size; |
239 | | void *next_out = NULL; |
240 | | size_t avail_out = 0; |
241 | | size_t total_out = 0; |
242 | | |
243 | | size_t result; |
244 | | ZSTD_DCtx *dctx = ZSTD_createDCtx(); |
245 | | if (dctx == NULL) |
246 | | return fail (&state, DWFL_E_NOMEM); |
247 | | |
248 | | do |
249 | | { |
250 | | 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 | | return fail (&state, DWFL_E_ERRNO); |
258 | | } |
259 | | next_in = state.input_buffer; |
260 | | avail_in = n; |
261 | | state.input_pos += n; |
262 | | } |
263 | | if (avail_out == 0) |
264 | | { |
265 | | ptrdiff_t pos = (void *) next_out - state.buffer; |
266 | | if (!bigger_buffer (&state, avail_in)) |
267 | | { |
268 | | ZSTD_freeDCtx (dctx); |
269 | | return fail (&state, DWFL_E_NOMEM); |
270 | | } |
271 | | next_out = state.buffer + pos; |
272 | | avail_out = state.size - pos; |
273 | | } |
274 | | |
275 | | ZSTD_inBuffer input = { next_in, avail_in, 0 }; |
276 | | ZSTD_outBuffer output = { next_out, avail_out, 0 }; |
277 | | result = ZSTD_decompressStream (dctx, &output, &input); |
278 | | |
279 | | if (! ZSTD_isError (result)) |
280 | | { |
281 | | total_out += output.pos; |
282 | | next_out += output.pos; |
283 | | avail_out -= output.pos; |
284 | | next_in += input.pos; |
285 | | avail_in -= input.pos; |
286 | | } |
287 | | |
288 | | if (result == 0) |
289 | | break; |
290 | | } |
291 | | while (avail_in > 0 && ! ZSTD_isError (result)); |
292 | | |
293 | | ZSTD_freeDCtx (dctx); |
294 | | |
295 | | if (ZSTD_isError (result)) |
296 | | return fail (&state, DWFL_E_ZSTD); |
297 | | |
298 | | 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 | | z_stream z = { .next_in = mapped, .avail_in = state.mapped_size }; |
307 | | int result = inflateInit (&z); |
308 | | if (result != Z (OK)) |
309 | | { |
310 | | inflateEnd (&z); |
311 | | return zlib_fail (&state, result); |
312 | | } |
313 | | |
314 | | do |
315 | | { |
316 | | if (z.avail_in == 0 && state.input_buffer != NULL) |
317 | | { |
318 | | ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, |
319 | | start_offset + state.input_pos); |
320 | | if (unlikely (n < 0)) |
321 | | { |
322 | | inflateEnd (&z); |
323 | | return zlib_fail (&state, Z (ERRNO)); |
324 | | } |
325 | | z.next_in = state.input_buffer; |
326 | | z.avail_in = n; |
327 | | state.input_pos += n; |
328 | | } |
329 | | if (z.avail_out == 0) |
330 | | { |
331 | | ptrdiff_t pos = (void *) z.next_out - state.buffer; |
332 | | if (!bigger_buffer (&state, z.avail_in)) |
333 | | { |
334 | | result = Z (MEM_ERROR); |
335 | | break; |
336 | | } |
337 | | z.next_out = state.buffer + pos; |
338 | | z.avail_out = state.size - pos; |
339 | | } |
340 | | } |
341 | | while ((result = do_inflate (&z)) == Z (OK)); |
342 | | |
343 | | #ifdef BZLIB |
344 | | uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32) |
345 | | | z.total_out_lo32); |
346 | | smaller_buffer (&state, total_out); |
347 | | #else |
348 | | smaller_buffer (&state, z.total_out); |
349 | | #endif |
350 | | |
351 | | inflateEnd (&z); |
352 | | |
353 | | if (result != Z (STREAM_END)) |
354 | | 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 | 0 | { |
364 | 0 | gzclose (state.zf); |
365 | | /* Not a compressed stream after all. */ |
366 | 0 | return fail (&state, DWFL_E_BADELF); |
367 | 0 | } |
368 | | |
369 | 0 | if (result != DWFL_E_NOERROR) |
370 | 0 | return fail (&state, result); |
371 | | |
372 | 0 | ptrdiff_t pos = 0; |
373 | 0 | while (1) |
374 | 0 | { |
375 | 0 | if (!bigger_buffer (&state, 1024)) |
376 | 0 | { |
377 | 0 | gzclose (state.zf); |
378 | 0 | return zlib_fail (&state, Z (MEM_ERROR)); |
379 | 0 | } |
380 | 0 | int n = gzread (state.zf, state.buffer + pos, state.size - pos); |
381 | 0 | if (n < 0) |
382 | 0 | { |
383 | 0 | int code; |
384 | 0 | gzerror (state.zf, &code); |
385 | 0 | gzclose (state.zf); |
386 | 0 | return zlib_fail (&state, code); |
387 | 0 | } |
388 | 0 | if (n == 0) |
389 | 0 | break; |
390 | 0 | pos += n; |
391 | 0 | } |
392 | | |
393 | 0 | gzclose (state.zf); |
394 | 0 | smaller_buffer (&state, pos); |
395 | 0 | #endif |
396 | |
|
397 | 0 | free (state.input_buffer); |
398 | |
|
399 | 0 | *state.whole = state.buffer; |
400 | 0 | *whole_size = state.size; |
401 | |
|
402 | 0 | return DWFL_E_NOERROR; |
403 | 0 | } |