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 BZLIB
52 # define USE_INFLATE 1
53 # include <bzlib.h>
54 # define unzip __libdw_bunzip2
55 # define DWFL_E_ZLIB DWFL_E_BZLIB
56 # define MAGIC "BZh"
57 # define Z(what) BZ_##what
58 # define BZ_ERRNO BZ_IO_ERROR
59 # define z_stream bz_stream
60 # define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0)
61 # define do_inflate(z) BZ2_bzDecompress (z)
62 # define inflateEnd(z) BZ2_bzDecompressEnd (z)
63 #else
64 # define USE_INFLATE 0
65 # define crc32 loser_crc32
66 # include <zlib.h>
67 # define unzip __libdw_gunzip
68 # define MAGIC "\037\213"
69 # define Z(what) Z_##what
70 #endif
71
72 #define READ_SIZE (1 << 20)
73
74 struct unzip_state {
75 #if !USE_INFLATE
76 gzFile zf;
77 #endif
78 size_t mapped_size;
79 void **whole;
80 void *buffer;
81 size_t size;
82 void *input_buffer;
83 off_t input_pos;
84 };
85
86 static inline bool
bigger_buffer(struct unzip_state * state,size_t start)87 bigger_buffer (struct unzip_state *state, size_t start)
88 {
89 size_t more = state->size ? state->size * 2 : start;
90 char *b = realloc (state->buffer, more);
91 while (unlikely (b == NULL) && more >= state->size + 1024)
92 b = realloc (state->buffer, more -= 1024);
93 if (unlikely (b == NULL))
94 return false;
95 state->buffer = b;
96 state->size = more;
97 return true;
98 }
99
100 static inline void
smaller_buffer(struct unzip_state * state,size_t end)101 smaller_buffer (struct unzip_state *state, size_t end)
102 {
103 state->buffer =
104 realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
105 state->size = end;
106 }
107
108 static inline Dwfl_Error
fail(struct unzip_state * state,Dwfl_Error failure)109 fail (struct unzip_state *state, Dwfl_Error failure)
110 {
111 if (state->input_pos == (off_t) state->mapped_size)
112 *state->whole = state->input_buffer;
113 else
114 {
115 free (state->input_buffer);
116 *state->whole = NULL;
117 }
118 free (state->buffer);
119 return failure;
120 }
121
122 static inline Dwfl_Error
zlib_fail(struct unzip_state * state,int result)123 zlib_fail (struct unzip_state *state, int result)
124 {
125 switch (result)
126 {
127 case Z (MEM_ERROR):
128 return fail (state, DWFL_E_NOMEM);
129 case Z (ERRNO):
130 return fail (state, DWFL_E_ERRNO);
131 default:
132 return fail (state, DWFL_E_ZLIB);
133 }
134 }
135
136 #if !USE_INFLATE
137 static Dwfl_Error
open_stream(int fd,off_t start_offset,struct unzip_state * state)138 open_stream (int fd, off_t start_offset, struct unzip_state *state)
139 {
140 int d = dup (fd);
141 if (unlikely (d < 0))
142 return DWFL_E_BADELF;
143 if (start_offset != 0)
144 {
145 off_t off = lseek (d, start_offset, SEEK_SET);
146 if (off != start_offset)
147 {
148 close (d);
149 return DWFL_E_BADELF;
150 }
151 }
152 state->zf = gzdopen (d, "r");
153 if (unlikely (state->zf == NULL))
154 {
155 close (d);
156 return zlib_fail (state, Z (MEM_ERROR));
157 }
158
159 /* From here on, zlib will close D. */
160
161 return DWFL_E_NOERROR;
162 }
163 #endif
164
165 /* If this is not a compressed image, return DWFL_E_BADELF.
166 If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
167 Otherwise return an error for bad compressed data or I/O failure.
168 If we return an error after reading the first part of the file,
169 leave that portion malloc'd in *WHOLE, *WHOLE_SIZE. If *WHOLE
170 is not null on entry, we'll use it in lieu of repeating a read. */
171
172 Dwfl_Error internal_function
unzip(int fd,off_t start_offset,void * mapped,size_t _mapped_size,void ** _whole,size_t * whole_size)173 unzip (int fd, off_t start_offset,
174 void *mapped, size_t _mapped_size,
175 void **_whole, size_t *whole_size)
176 {
177 struct unzip_state state =
178 {
179 #if !USE_INFLATE
180 .zf = NULL,
181 #endif
182 .mapped_size = _mapped_size,
183 .whole = _whole,
184 .buffer = NULL,
185 .size = 0,
186 .input_buffer = NULL,
187 .input_pos = 0
188 };
189
190 if (mapped == NULL)
191 {
192 if (*state.whole == NULL)
193 {
194 state.input_buffer = malloc (READ_SIZE);
195 if (unlikely (state.input_buffer == NULL))
196 return DWFL_E_NOMEM;
197
198 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
199 if (unlikely (n < 0))
200 return zlib_fail (&state, Z (ERRNO));
201
202 state.input_pos = n;
203 mapped = state.input_buffer;
204 state.mapped_size = n;
205 }
206 else
207 {
208 state.input_buffer = *state.whole;
209 state.input_pos = state.mapped_size = *whole_size;
210 }
211 }
212
213 #define NOMAGIC(magic) \
214 (state.mapped_size <= sizeof magic || \
215 memcmp (mapped, magic, sizeof magic - 1))
216
217 /* First, look at the header. */
218 if (NOMAGIC (MAGIC)
219 #ifdef MAGIC2
220 && NOMAGIC (MAGIC2)
221 #endif
222 )
223 /* Not a compressed file. */
224 return DWFL_E_BADELF;
225
226 #if USE_INFLATE
227
228 /* This style actually only works with bzlib and liblzma.
229 The stupid zlib interface has nothing to grok the
230 gzip file headers except the slow gzFile interface. */
231
232 z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
233 int result = inflateInit (&z);
234 if (result != Z (OK))
235 {
236 inflateEnd (&z);
237 return zlib_fail (&state, result);
238 }
239
240 do
241 {
242 if (z.avail_in == 0 && state.input_buffer != NULL)
243 {
244 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
245 start_offset + state.input_pos);
246 if (unlikely (n < 0))
247 {
248 inflateEnd (&z);
249 return zlib_fail (&state, Z (ERRNO));
250 }
251 z.next_in = state.input_buffer;
252 z.avail_in = n;
253 state.input_pos += n;
254 }
255 if (z.avail_out == 0)
256 {
257 ptrdiff_t pos = (void *) z.next_out - state.buffer;
258 if (!bigger_buffer (&state, z.avail_in))
259 {
260 result = Z (MEM_ERROR);
261 break;
262 }
263 z.next_out = state.buffer + pos;
264 z.avail_out = state.size - pos;
265 }
266 }
267 while ((result = do_inflate (&z)) == Z (OK));
268
269 #ifdef BZLIB
270 uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
271 | z.total_out_lo32);
272 smaller_buffer (&state, total_out);
273 #else
274 smaller_buffer (&state, z.total_out);
275 #endif
276
277 inflateEnd (&z);
278
279 if (result != Z (STREAM_END))
280 return zlib_fail (&state, result);
281
282 #else /* gzip only. */
283
284 /* Let the decompression library read the file directly. */
285
286 Dwfl_Error result = open_stream (fd, start_offset, &state);
287
288 if (result == DWFL_E_NOERROR && gzdirect (state.zf))
289 {
290 gzclose (state.zf);
291 return fail (&state, DWFL_E_BADELF);
292 }
293
294 if (result != DWFL_E_NOERROR)
295 return fail (&state, result);
296
297 ptrdiff_t pos = 0;
298 while (1)
299 {
300 if (!bigger_buffer (&state, 1024))
301 {
302 gzclose (state.zf);
303 return zlib_fail (&state, Z (MEM_ERROR));
304 }
305 int n = gzread (state.zf, state.buffer + pos, state.size - pos);
306 if (n < 0)
307 {
308 int code;
309 gzerror (state.zf, &code);
310 gzclose (state.zf);
311 return zlib_fail (&state, code);
312 }
313 if (n == 0)
314 break;
315 pos += n;
316 }
317
318 gzclose (state.zf);
319 smaller_buffer (&state, pos);
320 #endif
321
322 free (state.input_buffer);
323
324 *state.whole = state.buffer;
325 *whole_size = state.size;
326
327 return DWFL_E_NOERROR;
328 }
329