• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ZSTD
52 # define USE_INFLATE	1
53 # include <zstd.h>
54 # define unzip		__libdw_unzstd
55 # define DWFL_E_ZLIB	DWFL_E_ZSTD
56 # define MAGIC		"\x28\xb5\x2f\xfd"
57 #elif defined BZLIB
58 # define USE_INFLATE	1
59 # include <bzlib.h>
60 # define unzip		__libdw_bunzip2
61 # define DWFL_E_ZLIB	DWFL_E_BZLIB
62 # define MAGIC		"BZh"
63 # define Z(what)	BZ_##what
64 # define BZ_ERRNO	BZ_IO_ERROR
65 # define z_stream	bz_stream
66 # define inflateInit(z)	BZ2_bzDecompressInit (z, 0, 0)
67 # define do_inflate(z)	BZ2_bzDecompress (z)
68 # define inflateEnd(z)	BZ2_bzDecompressEnd (z)
69 #else
70 # define USE_INFLATE	0
71 # define crc32		loser_crc32
72 # include <zlib.h>
73 # define unzip		__libdw_gunzip
74 # define MAGIC		"\037\213"
75 # define Z(what)	Z_##what
76 #endif
77 
78 #define READ_SIZE		(1 << 20)
79 
80 struct unzip_state {
81 #if !USE_INFLATE
82   gzFile zf;
83 #endif
84   size_t mapped_size;
85   void **whole;
86   void *buffer;
87   size_t size;
88   void *input_buffer;
89   off_t input_pos;
90 };
91 
92 static inline bool
bigger_buffer(struct unzip_state * state,size_t start)93 bigger_buffer (struct unzip_state *state, size_t start)
94 {
95   size_t more = state->size ? state->size * 2 : start;
96   char *b = realloc (state->buffer, more);
97   while (unlikely (b == NULL) && more >= state->size + 1024)
98     b = realloc (state->buffer, more -= 1024);
99   if (unlikely (b == NULL))
100     return false;
101   state->buffer = b;
102   state->size = more;
103   return true;
104 }
105 
106 static inline void
smaller_buffer(struct unzip_state * state,size_t end)107 smaller_buffer (struct unzip_state *state, size_t end)
108 {
109   state->buffer =
110       realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer;
111   state->size = end;
112 }
113 
114 static inline Dwfl_Error
fail(struct unzip_state * state,Dwfl_Error failure)115 fail (struct unzip_state *state, Dwfl_Error failure)
116 {
117   if (state->input_pos == (off_t) state->mapped_size)
118     *state->whole = state->input_buffer;
119   else
120     {
121       free (state->input_buffer);
122       *state->whole = NULL;
123     }
124   free (state->buffer);
125   return failure;
126 }
127 
128 #ifndef ZSTD
129 static inline Dwfl_Error
zlib_fail(struct unzip_state * state,int result)130 zlib_fail (struct unzip_state *state, int result)
131 {
132   switch (result)
133     {
134     case Z (MEM_ERROR):
135       return fail (state, DWFL_E_NOMEM);
136     case Z (ERRNO):
137       return fail (state, DWFL_E_ERRNO);
138     default:
139       return fail (state, DWFL_E_ZLIB);
140     }
141 }
142 #endif
143 
144 #if !USE_INFLATE
145 static Dwfl_Error
open_stream(int fd,off_t start_offset,struct unzip_state * state)146 open_stream (int fd, off_t start_offset, struct unzip_state *state)
147 {
148     int d = dup (fd);
149     if (unlikely (d < 0))
150       return DWFL_E_ERRNO;
151     if (start_offset != 0)
152       {
153 	off_t off = lseek (d, start_offset, SEEK_SET);
154 	if (off != start_offset)
155 	  {
156 	    close (d);
157 	    return DWFL_E_ERRNO;
158 	  }
159       }
160     state->zf = gzdopen (d, "r");
161     if (unlikely (state->zf == NULL))
162       {
163 	close (d);
164 	return DWFL_E_NOMEM;
165       }
166 
167     /* From here on, zlib will close D.  */
168 
169     return DWFL_E_NOERROR;
170 }
171 #endif
172 
173 /* If this is not a compressed image, return DWFL_E_BADELF.
174    If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
175    Otherwise return an error for bad compressed data or I/O failure.
176    If we return an error after reading the first part of the file,
177    leave that portion malloc'd in *WHOLE, *WHOLE_SIZE.  If *WHOLE
178    is not null on entry, we'll use it in lieu of repeating a read.  */
179 
180 Dwfl_Error internal_function
unzip(int fd,off_t start_offset,void * mapped,size_t _mapped_size,void ** _whole,size_t * whole_size)181 unzip (int fd, off_t start_offset,
182        void *mapped, size_t _mapped_size,
183        void **_whole, size_t *whole_size)
184 {
185   struct unzip_state state =
186     {
187 #if !USE_INFLATE
188       .zf = NULL,
189 #endif
190       .mapped_size = _mapped_size,
191       .whole = _whole,
192       .buffer = NULL,
193       .size = 0,
194       .input_buffer = NULL,
195       .input_pos = 0
196     };
197 
198   if (mapped == NULL)
199     {
200       if (*state.whole == NULL)
201 	{
202 	  state.input_buffer = malloc (READ_SIZE);
203 	  if (unlikely (state.input_buffer == NULL))
204 	    return DWFL_E_NOMEM;
205 
206 	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset);
207 	  if (unlikely (n < 0))
208 	    return fail (&state, DWFL_E_ERRNO);
209 
210 	  state.input_pos = n;
211 	  mapped = state.input_buffer;
212 	  state.mapped_size = n;
213 	}
214       else
215 	{
216 	  state.input_buffer = *state.whole;
217 	  state.input_pos = state.mapped_size = *whole_size;
218 	}
219     }
220 
221 #define NOMAGIC(magic) \
222   (state.mapped_size <= sizeof magic || \
223    memcmp (mapped, magic, sizeof magic - 1))
224 
225   /* First, look at the header.  */
226   if (NOMAGIC (MAGIC)
227 #ifdef MAGIC2
228       && NOMAGIC (MAGIC2)
229 #endif
230       )
231     /* Not a compressed file.  */
232     return DWFL_E_BADELF;
233 
234 #ifdef ZSTD
235   /* special case for libzstd since it is slightly different from the
236      API provided by bzlib and liblzma.  */
237 
238   void *next_in = mapped;
239   size_t avail_in = state.mapped_size;
240   void *next_out = NULL;
241   size_t avail_out = 0;
242   size_t total_out = 0;
243 
244   size_t result;
245   ZSTD_DCtx *dctx = ZSTD_createDCtx();
246   if (dctx == NULL)
247     return fail (&state, DWFL_E_NOMEM);
248 
249   do
250     {
251       if (avail_in == 0 && state.input_buffer != NULL)
252 	{
253 	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
254 				   start_offset + state.input_pos);
255 	  if (unlikely (n < 0))
256 	    {
257 	      ZSTD_freeDCtx (dctx);
258 	      return fail (&state, DWFL_E_ERRNO);
259 	    }
260 	  next_in = state.input_buffer;
261 	  avail_in = n;
262 	  state.input_pos += n;
263 	}
264       if (avail_out == 0)
265 	{
266 	  ptrdiff_t pos = (void *) next_out - state.buffer;
267 	  if (!bigger_buffer (&state, avail_in))
268 	    {
269 	      ZSTD_freeDCtx (dctx);
270 	      return fail (&state, DWFL_E_NOMEM);
271 	    }
272 	  next_out = state.buffer + pos;
273 	  avail_out = state.size - pos;
274 	}
275 
276       ZSTD_inBuffer input = { next_in, avail_in, 0 };
277       ZSTD_outBuffer output = { next_out, avail_out, 0 };
278       result = ZSTD_decompressStream (dctx, &output, &input);
279 
280       if (! ZSTD_isError (result))
281 	{
282 	  total_out += output.pos;
283 	  next_out += output.pos;
284 	  avail_out -= output.pos;
285 	  next_in += input.pos;
286 	  avail_in -= input.pos;
287 	}
288 
289       if (result == 0)
290 	break;
291     }
292   while (avail_in > 0 && ! ZSTD_isError (result));
293 
294   ZSTD_freeDCtx (dctx);
295 
296   if (ZSTD_isError (result))
297     return fail (&state, DWFL_E_ZSTD);
298 
299   smaller_buffer (&state, total_out);
300 
301 #elif USE_INFLATE
302 
303   /* This style actually only works with bzlib and liblzma.
304      The stupid zlib interface has nothing to grok the
305      gzip file headers except the slow gzFile interface.  */
306 
307   z_stream z = { .next_in = mapped, .avail_in = state.mapped_size };
308   int result = inflateInit (&z);
309   if (result != Z (OK))
310     {
311       inflateEnd (&z);
312       return zlib_fail (&state, result);
313     }
314 
315   do
316     {
317       if (z.avail_in == 0 && state.input_buffer != NULL)
318 	{
319 	  ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE,
320 				   start_offset + state.input_pos);
321 	  if (unlikely (n < 0))
322 	    {
323 	      inflateEnd (&z);
324 	      return zlib_fail (&state, Z (ERRNO));
325 	    }
326 	  z.next_in = state.input_buffer;
327 	  z.avail_in = n;
328 	  state.input_pos += n;
329 	}
330       if (z.avail_out == 0)
331 	{
332 	  ptrdiff_t pos = (void *) z.next_out - state.buffer;
333 	  if (!bigger_buffer (&state, z.avail_in))
334 	    {
335 	      result = Z (MEM_ERROR);
336 	      break;
337 	    }
338 	  z.next_out = state.buffer + pos;
339 	  z.avail_out = state.size - pos;
340 	}
341     }
342   while ((result = do_inflate (&z)) == Z (OK));
343 
344 #ifdef BZLIB
345   uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
346 			| z.total_out_lo32);
347   smaller_buffer (&state, total_out);
348 #else
349   smaller_buffer (&state, z.total_out);
350 #endif
351 
352   inflateEnd (&z);
353 
354   if (result != Z (STREAM_END))
355     return zlib_fail (&state, result);
356 
357 #else  /* gzip only.  */
358 
359   /* Let the decompression library read the file directly.  */
360 
361   Dwfl_Error result = open_stream (fd, start_offset, &state);
362 
363   if (result == DWFL_E_NOERROR && gzdirect (state.zf))
364     {
365       gzclose (state.zf);
366       /* Not a compressed stream after all.  */
367       return fail (&state, DWFL_E_BADELF);
368     }
369 
370   if (result != DWFL_E_NOERROR)
371     return fail (&state, result);
372 
373   ptrdiff_t pos = 0;
374   while (1)
375     {
376       if (!bigger_buffer (&state, 1024))
377 	{
378 	  gzclose (state.zf);
379 	  return zlib_fail (&state, Z (MEM_ERROR));
380 	}
381       int n = gzread (state.zf, state.buffer + pos, state.size - pos);
382       if (n < 0)
383 	{
384 	  int code;
385 	  gzerror (state.zf, &code);
386 	  gzclose (state.zf);
387 	  return zlib_fail (&state, code);
388 	}
389       if (n == 0)
390 	break;
391       pos += n;
392     }
393 
394   gzclose (state.zf);
395   smaller_buffer (&state, pos);
396 #endif
397 
398   free (state.input_buffer);
399 
400   *state.whole = state.buffer;
401   *whole_size = state.size;
402 
403   return DWFL_E_NOERROR;
404 }
405