Branch data 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 : : /* 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
78 : 0 : unzip (int fd, off64_t start_offset,
79 : : void *mapped, size_t mapped_size,
80 : : void **whole, size_t *whole_size)
81 : : {
82 : 0 : void *buffer = NULL;
83 : 0 : size_t size = 0;
84 : 0 : inline bool bigger_buffer (size_t start)
85 : : {
86 [ # # ]: 0 : size_t more = size ? size * 2 : start;
87 : 0 : char *b = realloc (buffer, more);
88 [ # # ][ # # ]: 0 : while (unlikely (b == NULL) && more >= size + 1024)
89 : 0 : b = realloc (buffer, more -= 1024);
90 [ # # ]: 0 : if (unlikely (b == NULL))
91 : : return false;
92 : 0 : buffer = b;
93 : 0 : size = more;
94 : 0 : return true;
95 : : }
96 : 0 : inline void smaller_buffer (size_t end)
97 : : {
98 [ # # ][ # # ]: 0 : buffer = realloc (buffer, end) ?: end == 0 ? NULL : buffer;
99 : 0 : size = end;
100 : 0 : }
101 : :
102 : 0 : void *input_buffer = NULL;
103 : 0 : off_t input_pos = 0;
104 : :
105 : 0 : inline Dwfl_Error fail (Dwfl_Error failure)
106 : : {
107 [ # # ]: 0 : if (input_pos == (off_t) mapped_size)
108 : 0 : *whole = input_buffer;
109 : : else
110 : : {
111 : 0 : free (input_buffer);
112 : 0 : *whole = NULL;
113 : : }
114 : 0 : free (buffer);
115 : 0 : return failure;
116 : : }
117 : :
118 : 0 : inline Dwfl_Error zlib_fail (int result)
119 : : {
120 [ # # # ]: 0 : switch (result)
121 : : {
122 : : case Z (MEM_ERROR):
123 : 0 : return fail (DWFL_E_NOMEM);
124 : : case Z (ERRNO):
125 : 0 : return fail (DWFL_E_ERRNO);
126 : : default:
127 : 0 : return fail (DWFL_E_ZLIB);
128 : : }
129 : : }
130 : :
131 [ # # ]: 0 : if (mapped == NULL)
132 : : {
133 [ # # ]: 0 : if (*whole == NULL)
134 : : {
135 : 0 : input_buffer = malloc (READ_SIZE);
136 [ # # ]: 0 : if (unlikely (input_buffer == NULL))
137 : : return DWFL_E_NOMEM;
138 : :
139 : 0 : ssize_t n = pread_retry (fd, input_buffer, READ_SIZE, start_offset);
140 [ # # ]: 0 : if (unlikely (n < 0))
141 : 0 : return zlib_fail (Z (ERRNO));
142 : :
143 : 0 : input_pos = n;
144 : 0 : mapped = input_buffer;
145 : 0 : mapped_size = n;
146 : : }
147 : : else
148 : : {
149 : 0 : input_buffer = *whole;
150 : 0 : 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 [ # # ][ # # ]: 0 : if (NOMAGIC (MAGIC)
159 : : #ifdef MAGIC2
160 [ # # ][ # # ]: 0 : && 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 : 0 : z_stream z = { .next_in = mapped, .avail_in = mapped_size };
173 : 0 : int result = inflateInit (&z);
174 [ # # ]: 0 : if (result != Z (OK))
175 : : {
176 : 0 : inflateEnd (&z);
177 : 0 : return zlib_fail (result);
178 : : }
179 : :
180 : : do
181 : : {
182 [ # # ][ # # ]: 0 : if (z.avail_in == 0 && input_buffer != NULL)
183 : : {
184 : 0 : ssize_t n = pread_retry (fd, input_buffer, READ_SIZE,
185 : : start_offset + input_pos);
186 [ # # ]: 0 : if (unlikely (n < 0))
187 : : {
188 : 0 : inflateEnd (&z);
189 : 0 : return zlib_fail (Z (ERRNO));
190 : : }
191 : 0 : z.next_in = input_buffer;
192 : 0 : z.avail_in = n;
193 : 0 : input_pos += n;
194 : : }
195 [ # # ]: 0 : if (z.avail_out == 0)
196 : : {
197 : 0 : ptrdiff_t pos = (void *) z.next_out - buffer;
198 [ # # ]: 0 : if (!bigger_buffer (z.avail_in))
199 : : {
200 : : result = Z (MEM_ERROR);
201 : : break;
202 : : }
203 : 0 : z.next_out = buffer + pos;
204 : 0 : z.avail_out = size - pos;
205 : : }
206 : : }
207 [ # # ]: 0 : while ((result = do_inflate (&z)) == Z (OK));
208 : :
209 : : #ifdef BZLIB
210 : 0 : uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
211 : 0 : | z.total_out_lo32);
212 : 0 : smaller_buffer (total_out);
213 : : #else
214 : 0 : smaller_buffer (z.total_out);
215 : : #endif
216 : :
217 : 0 : inflateEnd (&z);
218 : :
219 [ # # ]: 0 : if (result != Z (STREAM_END))
220 : 0 : return zlib_fail (result);
221 : :
222 : : #else /* gzip only. */
223 : :
224 : : /* Let the decompression library read the file directly. */
225 : :
226 : : gzFile zf;
227 : 0 : Dwfl_Error open_stream (void)
228 : : {
229 : 0 : int d = dup (fd);
230 [ # # ]: 0 : if (unlikely (d < 0))
231 : : return DWFL_E_BADELF;
232 [ # # ]: 0 : if (start_offset != 0)
233 : : {
234 : 0 : off64_t off = lseek (d, start_offset, SEEK_SET);
235 [ # # ]: 0 : if (off != start_offset)
236 : : {
237 : 0 : close (d);
238 : 0 : return DWFL_E_BADELF;
239 : : }
240 : : }
241 : 0 : zf = gzdopen (d, "r");
242 [ # # ]: 0 : if (unlikely (zf == NULL))
243 : : {
244 : 0 : close (d);
245 : 0 : 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 : 0 : Dwfl_Error result = open_stream ();
254 : :
255 [ # # ][ # # ]: 0 : if (result == DWFL_E_NOERROR && gzdirect (zf))
256 : : {
257 : 0 : gzclose (zf);
258 : 0 : return fail (DWFL_E_BADELF);
259 : : }
260 : :
261 [ # # ]: 0 : if (result != DWFL_E_NOERROR)
262 : 0 : return fail (result);
263 : :
264 : : ptrdiff_t pos = 0;
265 : : while (1)
266 : : {
267 [ # # ]: 0 : if (!bigger_buffer (1024))
268 : : {
269 : 0 : gzclose (zf);
270 : 0 : return zlib_fail (Z (MEM_ERROR));
271 : : }
272 : 0 : int n = gzread (zf, buffer + pos, size - pos);
273 [ # # ]: 0 : if (n < 0)
274 : : {
275 : : int code;
276 : 0 : gzerror (zf, &code);
277 : 0 : gzclose (zf);
278 : 0 : return zlib_fail (code);
279 : : }
280 [ # # ]: 0 : if (n == 0)
281 : : break;
282 : 0 : pos += n;
283 : 0 : }
284 : :
285 : 0 : gzclose (zf);
286 : 0 : smaller_buffer (pos);
287 : : #endif
288 : :
289 : 0 : free (input_buffer);
290 : :
291 : 0 : *whole = buffer;
292 : 0 : *whole_size = size;
293 : :
294 : 0 : return DWFL_E_NOERROR;
295 : : }
|