Imported Upstream version 0.153
[platform/upstream/elfutils.git] / libdwfl / gzip.c
1 /* Decompression support for libdwfl: zlib (gzip) and/or bzlib (bzip2).
2    Copyright (C) 2009 Red Hat, Inc.
3    This file is part of Red Hat elfutils.
4
5    Red Hat elfutils is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by the
7    Free Software Foundation; version 2 of the License.
8
9    Red Hat elfutils is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with Red Hat elfutils; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18    In addition, as a special exception, Red Hat, Inc. gives You the
19    additional right to link the code of Red Hat elfutils with code licensed
20    under any Open Source Initiative certified open source license
21    (http://www.opensource.org/licenses/index.php) which requires the
22    distribution of source code with any binary distribution and to
23    distribute linked combinations of the two.  Non-GPL Code permitted under
24    this exception must only link to the code of Red Hat elfutils through
25    those well defined interfaces identified in the file named EXCEPTION
26    found in the source code files (the "Approved Interfaces").  The files
27    of Non-GPL Code may instantiate templates or use macros or inline
28    functions from the Approved Interfaces without causing the resulting
29    work to be covered by the GNU General Public License.  Only Red Hat,
30    Inc. may make changes or additions to the list of Approved Interfaces.
31    Red Hat's grant of this exception is conditioned upon your not adding
32    any new exceptions.  If you wish to add a new Approved Interface or
33    exception, please contact Red Hat.  You must obey the GNU General Public
34    License in all respects for all of the Red Hat elfutils code and other
35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
36    covered by this exception.  If you modify this file, you may extend this
37    exception to your version of the file, but you are not obligated to do
38    so.  If you do not wish to provide this exception without modification,
39    you must delete this exception statement from your version and license
40    this file solely under the GPL without exception.
41
42    Red Hat elfutils is an included package of the Open Invention Network.
43    An included package of the Open Invention Network is a package for which
44    Open Invention Network licensees cross-license their patents.  No patent
45    license is granted, either expressly or impliedly, by designation as an
46    included package.  Should you wish to participate in the Open Invention
47    Network licensing program, please visit www.openinventionnetwork.com
48    <http://www.openinventionnetwork.com>.  */
49
50 #include "libdwflP.h"
51 #include "system.h"
52
53 #include <unistd.h>
54
55 #ifdef LZMA
56 # define USE_INFLATE    1
57 # include <lzma.h>
58 # define unzip          __libdw_unlzma
59 # define DWFL_E_ZLIB    DWFL_E_LZMA
60 # define MAGIC          "\xFD" "7zXZ\0" /* XZ file format.  */
61 # define MAGIC2         "\x5d\0"        /* Raw LZMA format.  */
62 # define Z(what)        LZMA_##what
63 # define LZMA_ERRNO     LZMA_PROG_ERROR
64 # define z_stream       lzma_stream
65 # define inflateInit(z) lzma_auto_decoder (z, 1 << 30, 0)
66 # define do_inflate(z)  lzma_code (z, LZMA_RUN)
67 # define inflateEnd(z)  lzma_end (z)
68 #elif defined BZLIB
69 # define USE_INFLATE    1
70 # include <bzlib.h>
71 # define unzip          __libdw_bunzip2
72 # define DWFL_E_ZLIB    DWFL_E_BZLIB
73 # define MAGIC          "BZh"
74 # define Z(what)        BZ_##what
75 # define BZ_ERRNO       BZ_IO_ERROR
76 # define z_stream       bz_stream
77 # define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0)
78 # define do_inflate(z)  BZ2_bzDecompress (z)
79 # define inflateEnd(z)  BZ2_bzDecompressEnd (z)
80 #else
81 # define USE_INFLATE    0
82 # define crc32          loser_crc32
83 # include <zlib.h>
84 # define unzip          __libdw_gunzip
85 # define MAGIC          "\037\213"
86 # define Z(what)        Z_##what
87 #endif
88
89 #define READ_SIZE               (1 << 20)
90
91 /* If this is not a compressed image, return DWFL_E_BADELF.
92    If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR.
93    Otherwise return an error for bad compressed data or I/O failure.
94    If we return an error after reading the first part of the file,
95    leave that portion malloc'd in *WHOLE, *WHOLE_SIZE.  If *WHOLE
96    is not null on entry, we'll use it in lieu of repeating a read.  */
97
98 Dwfl_Error internal_function
99 unzip (int fd, off64_t start_offset,
100        void *mapped, size_t mapped_size,
101        void **whole, size_t *whole_size)
102 {
103   void *buffer = NULL;
104   size_t size = 0;
105   inline bool bigger_buffer (size_t start)
106   {
107     size_t more = size ? size * 2 : start;
108     char *b = realloc (buffer, more);
109     while (unlikely (b == NULL) && more >= size + 1024)
110       b = realloc (buffer, more -= 1024);
111     if (unlikely (b == NULL))
112       return false;
113     buffer = b;
114     size = more;
115     return true;
116   }
117   inline void smaller_buffer (size_t end)
118   {
119     buffer = realloc (buffer, end) ?: end == 0 ? NULL : buffer;
120     size = end;
121   }
122
123   void *input_buffer = NULL;
124   off_t input_pos = 0;
125
126   inline Dwfl_Error fail (Dwfl_Error failure)
127   {
128     if (input_pos == (off_t) mapped_size)
129       *whole = input_buffer;
130     else
131       {
132         free (input_buffer);
133         *whole = NULL;
134       }
135     free (buffer);
136     return failure;
137   }
138
139   inline Dwfl_Error zlib_fail (int result)
140   {
141     switch (result)
142       {
143       case Z (MEM_ERROR):
144         return fail (DWFL_E_NOMEM);
145       case Z (ERRNO):
146         return fail (DWFL_E_ERRNO);
147       default:
148         return fail (DWFL_E_ZLIB);
149       }
150   }
151
152   if (mapped == NULL)
153     {
154       if (*whole == NULL)
155         {
156           input_buffer = malloc (READ_SIZE);
157           if (unlikely (input_buffer == NULL))
158             return DWFL_E_NOMEM;
159
160           ssize_t n = pread_retry (fd, input_buffer, READ_SIZE, start_offset);
161           if (unlikely (n < 0))
162             return zlib_fail (Z (ERRNO));
163
164           input_pos = n;
165           mapped = input_buffer;
166           mapped_size = n;
167         }
168       else
169         {
170           input_buffer = *whole;
171           input_pos = mapped_size = *whole_size;
172         }
173     }
174
175 #define NOMAGIC(magic) \
176   (mapped_size <= sizeof magic || memcmp (mapped, magic, sizeof magic - 1))
177
178   /* First, look at the header.  */
179   if (NOMAGIC (MAGIC)
180 #ifdef MAGIC2
181       && NOMAGIC (MAGIC2)
182 #endif
183       )
184     /* Not a compressed file.  */
185     return DWFL_E_BADELF;
186
187 #if USE_INFLATE
188
189   /* This style actually only works with bzlib and liblzma.
190      The stupid zlib interface has nothing to grok the
191      gzip file headers except the slow gzFile interface.  */
192
193   z_stream z = { .next_in = mapped, .avail_in = mapped_size };
194   int result = inflateInit (&z);
195   if (result != Z (OK))
196     {
197       inflateEnd (&z);
198       return zlib_fail (result);
199     }
200
201   do
202     {
203       if (z.avail_in == 0 && input_buffer != NULL)
204         {
205           ssize_t n = pread_retry (fd, input_buffer, READ_SIZE,
206                                    start_offset + input_pos);
207           if (unlikely (n < 0))
208             {
209               inflateEnd (&z);
210               return zlib_fail (Z (ERRNO));
211             }
212           z.next_in = input_buffer;
213           z.avail_in = n;
214           input_pos += n;
215         }
216       if (z.avail_out == 0)
217         {
218           ptrdiff_t pos = (void *) z.next_out - buffer;
219           if (!bigger_buffer (z.avail_in))
220             {
221               result = Z (MEM_ERROR);
222               break;
223             }
224           z.next_out = buffer + pos;
225           z.avail_out = size - pos;
226         }
227     }
228   while ((result = do_inflate (&z)) == Z (OK));
229
230 #ifdef BZLIB
231   uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32)
232                         | z.total_out_lo32);
233   smaller_buffer (total_out);
234 #else
235   smaller_buffer (z.total_out);
236 #endif
237
238   inflateEnd (&z);
239
240   if (result != Z (STREAM_END))
241     return zlib_fail (result);
242
243 #else  /* gzip only.  */
244
245   /* Let the decompression library read the file directly.  */
246
247   gzFile zf;
248   Dwfl_Error open_stream (void)
249   {
250     int d = dup (fd);
251     if (unlikely (d < 0))
252       return DWFL_E_BADELF;
253     if (start_offset != 0)
254       {
255         off64_t off = lseek (d, start_offset, SEEK_SET);
256         if (off != start_offset)
257           {
258             close (d);
259             return DWFL_E_BADELF;
260           }
261       }
262     zf = gzdopen (d, "r");
263     if (unlikely (zf == NULL))
264       {
265         close (d);
266         return zlib_fail (Z (MEM_ERROR));
267       }
268
269     /* From here on, zlib will close D.  */
270
271     return DWFL_E_NOERROR;
272   }
273
274   Dwfl_Error result = open_stream ();
275
276   if (result == DWFL_E_NOERROR && gzdirect (zf))
277     {
278       gzclose (zf);
279       return fail (DWFL_E_BADELF);
280     }
281
282   if (result != DWFL_E_NOERROR)
283     return fail (result);
284
285   ptrdiff_t pos = 0;
286   while (1)
287     {
288       if (!bigger_buffer (1024))
289         {
290           gzclose (zf);
291           return zlib_fail (Z (MEM_ERROR));
292         }
293       int n = gzread (zf, buffer + pos, size - pos);
294       if (n < 0)
295         {
296           int code;
297           gzerror (zf, &code);
298           gzclose (zf);
299           return zlib_fail (code);
300         }
301       if (n == 0)
302         break;
303       pos += n;
304     }
305
306   gzclose (zf);
307   smaller_buffer (pos);
308 #endif
309
310   free (input_buffer);
311
312   *whole = buffer;
313   *whole_size = size;
314
315   return DWFL_E_NOERROR;
316 }