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