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 : #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
87 52 : bigger_buffer (struct unzip_state *state, size_t start)
88 : {
89 52 : size_t more = state->size ? state->size * 2 : start;
90 52 : char *b = realloc (state->buffer, more);
91 52 : while (unlikely (b == NULL) && more >= state->size + 1024)
92 0 : b = realloc (state->buffer, more -= 1024);
93 52 : if (unlikely (b == NULL))
94 : return false;
95 52 : state->buffer = b;
96 52 : state->size = more;
97 : return true;
98 : }
99 :
100 : static inline void
101 : smaller_buffer (struct unzip_state *state, size_t end)
102 : {
103 14 : state->buffer =
104 14 : realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
105 14 : state->size = end;
106 : }
107 :
108 : static inline Dwfl_Error
109 0 : fail (struct unzip_state *state, Dwfl_Error failure)
110 : {
111 0 : if (state->input_pos == (off_t) state->mapped_size)
112 0 : *state->whole = state->input_buffer;
113 : else
114 : {
115 0 : free (state->input_buffer);
116 0 : *state->whole = NULL;
117 : }
118 0 : free (state->buffer);
119 0 : return failure;
120 : }
121 :
122 : static inline Dwfl_Error
123 0 : zlib_fail (struct unzip_state *state, int result)
124 : {
125 0 : switch (result)
126 : {
127 0 : case Z (MEM_ERROR):
128 0 : return fail (state, DWFL_E_NOMEM);
129 0 : case Z (ERRNO):
130 0 : return fail (state, DWFL_E_ERRNO);
131 0 : default:
132 0 : return fail (state, DWFL_E_ZLIB);
133 : }
134 : }
135 :
136 : #if !USE_INFLATE
137 : static Dwfl_Error
138 0 : open_stream (int fd, off_t start_offset, struct unzip_state *state)
139 : {
140 0 : int d = dup (fd);
141 0 : if (unlikely (d < 0))
142 : return DWFL_E_BADELF;
143 0 : if (start_offset != 0)
144 : {
145 0 : off_t off = lseek (d, start_offset, SEEK_SET);
146 0 : if (off != start_offset)
147 : {
148 0 : close (d);
149 0 : return DWFL_E_BADELF;
150 : }
151 : }
152 0 : state->zf = gzdopen (d, "r");
153 0 : if (unlikely (state->zf == NULL))
154 : {
155 0 : close (d);
156 0 : 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
173 24 : unzip (int fd, off_t start_offset,
174 : void *mapped, size_t _mapped_size,
175 : void **_whole, size_t *whole_size)
176 : {
177 24 : 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 24 : if (mapped == NULL)
191 : {
192 0 : if (*state.whole == NULL)
193 : {
194 0 : state.input_buffer = malloc (READ_SIZE);
195 0 : if (unlikely (state.input_buffer == NULL))
196 : return DWFL_E_NOMEM;
197 :
198 0 : ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
199 0 : if (unlikely (n < 0))
200 0 : return zlib_fail (&state, Z (ERRNO));
201 :
202 0 : state.input_pos = n;
203 0 : mapped = state.input_buffer;
204 0 : state.mapped_size = n;
205 : }
206 : else
207 : {
208 0 : state.input_buffer = *state.whole;
209 0 : 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 24 : if (NOMAGIC (MAGIC)
219 : #ifdef MAGIC2
220 0 : && 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 14 : z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
233 14 : int result = inflateInit (&z);
234 14 : if (result != Z (OK))
235 : {
236 0 : inflateEnd (&z);
237 0 : return zlib_fail (&state, result);
238 : }
239 :
240 : do
241 : {
242 52 : if (z.avail_in == 0 && state.input_buffer != NULL)
243 : {
244 0 : ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
245 0 : start_offset + state.input_pos);
246 0 : if (unlikely (n < 0))
247 : {
248 0 : inflateEnd (&z);
249 0 : return zlib_fail (&state, Z (ERRNO));
250 : }
251 0 : z.next_in = state.input_buffer;
252 0 : z.avail_in = n;
253 0 : state.input_pos += n;
254 : }
255 52 : if (z.avail_out == 0)
256 : {
257 52 : ptrdiff_t pos = (void *) z.next_out - state.buffer;
258 52 : if (!bigger_buffer (&state, z.avail_in))
259 : {
260 : result = Z (MEM_ERROR);
261 : break;
262 : }
263 52 : z.next_out = state.buffer + pos;
264 52 : z.avail_out = state.size - pos;
265 : }
266 : }
267 52 : while ((result = do_inflate (&z)) == Z (OK));
268 :
269 : #ifdef BZLIB
270 4 : uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
271 2 : | z.total_out_lo32);
272 4 : smaller_buffer (&state, total_out);
273 : #else
274 24 : smaller_buffer (&state, z.total_out);
275 : #endif
276 :
277 14 : inflateEnd (&z);
278 :
279 14 : if (result != Z (STREAM_END))
280 0 : return zlib_fail (&state, result);
281 :
282 : #else /* gzip only. */
283 :
284 : /* Let the decompression library read the file directly. */
285 :
286 0 : Dwfl_Error result = open_stream (fd, start_offset, &state);
287 :
288 0 : if (result == DWFL_E_NOERROR && gzdirect (state.zf))
289 : {
290 0 : gzclose (state.zf);
291 0 : return fail (&state, DWFL_E_BADELF);
292 : }
293 :
294 0 : if (result != DWFL_E_NOERROR)
295 0 : return fail (&state, result);
296 :
297 : ptrdiff_t pos = 0;
298 : while (1)
299 0 : {
300 0 : if (!bigger_buffer (&state, 1024))
301 : {
302 0 : gzclose (state.zf);
303 0 : return zlib_fail (&state, Z (MEM_ERROR));
304 : }
305 0 : int n = gzread (state.zf, state.buffer + pos, state.size - pos);
306 0 : if (n < 0)
307 : {
308 : int code;
309 0 : gzerror (state.zf, &code);
310 0 : gzclose (state.zf);
311 0 : return zlib_fail (&state, code);
312 : }
313 0 : if (n == 0)
314 : break;
315 0 : pos += n;
316 : }
317 :
318 0 : gzclose (state.zf);
319 0 : smaller_buffer (&state, pos);
320 : #endif
321 :
322 14 : free (state.input_buffer);
323 :
324 14 : *state.whole = state.buffer;
325 14 : *whole_size = state.size;
326 :
327 14 : return DWFL_E_NOERROR;
328 : }
|