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