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