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
bigger_buffer(struct unzip_state * state,size_t start)91 bigger_buffer (struct unzip_state *state, size_t start)
92 {
93 size_t more = state->size ? state->size * 2 : start;
94 char *b = realloc (state->buffer, more);
95 while (unlikely (b == NULL) && more >= state->size + 1024)
96 b = realloc (state->buffer, more -= 1024);
97 if (unlikely (b == NULL))
98 return false;
99 state->buffer = b;
100 state->size = more;
101 return true;
102 }
103
104 static inline void
smaller_buffer(struct unzip_state * state,size_t end)105 smaller_buffer (struct unzip_state *state, size_t end)
106 {
107 state->buffer =
108 realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
109 state->size = end;
110 }
111
112 static inline Dwfl_Error
fail(struct unzip_state * state,Dwfl_Error failure)113 fail (struct unzip_state *state, Dwfl_Error failure)
114 {
115 if (state->input_pos == (off_t) state->mapped_size)
116 *state->whole = state->input_buffer;
117 else
118 {
119 free (state->input_buffer);
120 *state->whole = NULL;
121 }
122 free (state->buffer);
123 return failure;
124 }
125
126 #ifndef ZSTD
127 static inline Dwfl_Error
zlib_fail(struct unzip_state * state,int result)128 zlib_fail (struct unzip_state *state, int result)
129 {
130 switch (result)
131 {
132 case Z (MEM_ERROR):
133 return fail (state, DWFL_E_NOMEM);
134 case Z (ERRNO):
135 return fail (state, DWFL_E_ERRNO);
136 default:
137 return fail (state, DWFL_E_ZLIB);
138 }
139 }
140 #endif
141
142 #if !USE_INFLATE
143 static Dwfl_Error
open_stream(int fd,off_t start_offset,struct unzip_state * state)144 open_stream (int fd, off_t start_offset, struct unzip_state *state)
145 {
146 int d = dup (fd);
147 if (unlikely (d < 0))
148 return DWFL_E_ERRNO;
149 if (start_offset != 0)
150 {
151 off_t off = lseek (d, start_offset, SEEK_SET);
152 if (off != start_offset)
153 {
154 close (d);
155 return DWFL_E_ERRNO;
156 }
157 }
158 state->zf = gzdopen (d, "r");
159 if (unlikely (state->zf == NULL))
160 {
161 close (d);
162 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
unzip(int fd,off_t start_offset,void * mapped,size_t _mapped_size,void ** _whole,size_t * whole_size)179 unzip (int fd, off_t start_offset,
180 void *mapped, size_t _mapped_size,
181 void **_whole, size_t *whole_size)
182 {
183 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 if (mapped == NULL)
197 {
198 if (*state.whole == NULL)
199 {
200 state.input_buffer = malloc (READ_SIZE);
201 if (unlikely (state.input_buffer == NULL))
202 return DWFL_E_NOMEM;
203
204 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
205 if (unlikely (n < 0))
206 return fail (&state, DWFL_E_ERRNO);
207
208 state.input_pos = n;
209 mapped = state.input_buffer;
210 state.mapped_size = n;
211 }
212 else
213 {
214 state.input_buffer = *state.whole;
215 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 if (NOMAGIC (MAGIC)
225 #ifdef MAGIC2
226 && NOMAGIC (MAGIC2)
227 #endif
228 )
229 /* Not a compressed file. */
230 return 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 void *next_in = mapped;
237 size_t avail_in = state.mapped_size;
238 void *next_out = NULL;
239 size_t avail_out = 0;
240 size_t total_out = 0;
241
242 size_t result;
243 ZSTD_DCtx *dctx = ZSTD_createDCtx();
244 if (dctx == NULL)
245 return fail (&state, DWFL_E_NOMEM);
246
247 do
248 {
249 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 return fail (&state, DWFL_E_ERRNO);
257 }
258 next_in = state.input_buffer;
259 avail_in = n;
260 state.input_pos += n;
261 }
262 if (avail_out == 0)
263 {
264 ptrdiff_t pos = (void *) next_out - state.buffer;
265 if (!bigger_buffer (&state, avail_in))
266 {
267 ZSTD_freeDCtx (dctx);
268 return fail (&state, DWFL_E_NOMEM);
269 }
270 next_out = state.buffer + pos;
271 avail_out = state.size - pos;
272 }
273
274 ZSTD_inBuffer input = { next_in, avail_in, 0 };
275 ZSTD_outBuffer output = { next_out, avail_out, 0 };
276 result = ZSTD_decompressStream (dctx, &output, &input);
277
278 if (! ZSTD_isError (result))
279 {
280 total_out += output.pos;
281 next_out += output.pos;
282 avail_out -= output.pos;
283 next_in += input.pos;
284 avail_in -= input.pos;
285 }
286
287 if (result == 0)
288 break;
289 }
290 while (avail_in > 0 && ! ZSTD_isError (result));
291
292 ZSTD_freeDCtx (dctx);
293
294 if (ZSTD_isError (result))
295 return fail (&state, DWFL_E_ZSTD);
296
297 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 z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
306 int result = inflateInit (&z);
307 if (result != Z (OK))
308 {
309 inflateEnd (&z);
310 return zlib_fail (&state, result);
311 }
312
313 do
314 {
315 if (z.avail_in == 0 && state.input_buffer != NULL)
316 {
317 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
318 start_offset + state.input_pos);
319 if (unlikely (n < 0))
320 {
321 inflateEnd (&z);
322 return zlib_fail (&state, Z (ERRNO));
323 }
324 z.next_in = state.input_buffer;
325 z.avail_in = n;
326 state.input_pos += n;
327 }
328 if (z.avail_out == 0)
329 {
330 ptrdiff_t pos = (void *) z.next_out - state.buffer;
331 if (!bigger_buffer (&state, z.avail_in))
332 {
333 result = Z (MEM_ERROR);
334 break;
335 }
336 z.next_out = state.buffer + pos;
337 z.avail_out = state.size - pos;
338 }
339 }
340 while ((result = do_inflate (&z)) == Z (OK));
341
342 #ifdef BZLIB
343 uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
344 | z.total_out_lo32);
345 smaller_buffer (&state, total_out);
346 #else
347 smaller_buffer (&state, z.total_out);
348 #endif
349
350 inflateEnd (&z);
351
352 if (result != Z (STREAM_END))
353 return zlib_fail (&state, result);
354
355 #else /* gzip only. */
356
357 /* Let the decompression library read the file directly. */
358
359 Dwfl_Error result = open_stream (fd, start_offset, &state);
360
361 if (result == DWFL_E_NOERROR && gzdirect (state.zf))
362 {
363 gzclose (state.zf);
364 /* Not a compressed stream after all. */
365 return fail (&state, DWFL_E_BADELF);
366 }
367
368 if (result != DWFL_E_NOERROR)
369 return fail (&state, result);
370
371 ptrdiff_t pos = 0;
372 while (1)
373 {
374 if (!bigger_buffer (&state, 1024))
375 {
376 gzclose (state.zf);
377 return zlib_fail (&state, Z (MEM_ERROR));
378 }
379 int n = gzread (state.zf, state.buffer + pos, state.size - pos);
380 if (n < 0)
381 {
382 int code;
383 gzerror (state.zf, &code);
384 gzclose (state.zf);
385 return zlib_fail (&state, code);
386 }
387 if (n == 0)
388 break;
389 pos += n;
390 }
391
392 gzclose (state.zf);
393 smaller_buffer (&state, pos);
394 #endif
395
396 free (state.input_buffer);
397
398 *state.whole = state.buffer;
399 *whole_size = state.size;
400
401 return DWFL_E_NOERROR;
402 }
403