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