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