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