• 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 #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