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