b7dde5d423080a620c9b38c08b31a85e48829e48
[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 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
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 }