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#ifdef LZMA 37# define USE_INFLATE 1 38# include <lzma.h> 39# define unzip __libdw_unlzma 40# define DWFL_E_ZLIB DWFL_E_LZMA 41# define MAGIC "\xFD" "7zXZ\0" /* XZ file format. */ 42# define MAGIC2 "\x5d\0" /* Raw LZMA format. */ 43# define Z(what) LZMA_##what 44# define LZMA_ERRNO LZMA_PROG_ERROR 45# define z_stream lzma_stream 46# define inflateInit(z) lzma_auto_decoder (z, 1 << 30, 0) 47# define do_inflate(z) lzma_code (z, LZMA_RUN) 48# define inflateEnd(z) lzma_end (z) 49#elif defined ZSTD 50# define USE_INFLATE 1 51# include <zstd.h> 52# define unzip __libdw_unzstd 53# define DWFL_E_ZLIB DWFL_E_ZSTD 54# define MAGIC "\x28\xb5\x2f\xfd" 55#elif defined BZLIB 56# define USE_INFLATE 1 57# include <bzlib.h> 58# define unzip __libdw_bunzip2 59# define DWFL_E_ZLIB DWFL_E_BZLIB 60# define MAGIC "BZh" 61# define Z(what) BZ_##what 62# define BZ_ERRNO BZ_IO_ERROR 63# define z_stream bz_stream 64# define inflateInit(z) BZ2_bzDecompressInit (z, 0, 0) 65# define do_inflate(z) BZ2_bzDecompress (z) 66# define inflateEnd(z) BZ2_bzDecompressEnd (z) 67#else 68# define USE_INFLATE 0 69# define crc32 loser_crc32 70# include <zlib.h> 71# define unzip __libdw_gunzip 72# define MAGIC "\037\213" 73# define Z(what) Z_##what 74#endif 75 76#define READ_SIZE (1 << 20) 77 78struct unzip_state { 79#if !USE_INFLATE 80 gzFile zf; 81#endif 82 size_t mapped_size; 83 void **whole; 84 void *buffer; 85 size_t size; 86 void *input_buffer; 87 off_t input_pos; 88}; 89 90static inline bool 91bigger_buffer (struct unzip_state *state, size_t start) 92{ 93 size_t more = state->size ? state->size * 2 : start; 94 char *b = realloc (state->buffer, more); 95 while (unlikely (b == NULL) && more >= state->size + 1024) 96 b = realloc (state->buffer, more -= 1024); 97 if (unlikely (b == NULL)) 98 return false; 99 state->buffer = b; 100 state->size = more; 101 return true; 102} 103 104static inline void 105smaller_buffer (struct unzip_state *state, size_t end) 106{ 107 state->buffer = 108 realloc (state->buffer, end) ?: end == 0 ? NULL : state->buffer; 109 state->size = end; 110} 111 112static inline Dwfl_Error 113fail (struct unzip_state *state, Dwfl_Error failure) 114{ 115 if (state->input_pos == (off_t) state->mapped_size) 116 *state->whole = state->input_buffer; 117 else 118 { 119 free (state->input_buffer); 120 *state->whole = NULL; 121 } 122 free (state->buffer); 123 return failure; 124} 125 126#ifndef ZSTD 127static inline Dwfl_Error 128zlib_fail (struct unzip_state *state, int result) 129{ 130 switch (result) 131 { 132 case Z (MEM_ERROR): 133 return fail (state, DWFL_E_NOMEM); 134 case Z (ERRNO): 135 return fail (state, DWFL_E_ERRNO); 136 default: 137 return fail (state, DWFL_E_ZLIB); 138 } 139} 140#endif 141 142#if !USE_INFLATE 143static Dwfl_Error 144open_stream (int fd, off_t start_offset, struct unzip_state *state) 145{ 146 int d = dup (fd); 147 if (unlikely (d < 0)) 148 return DWFL_E_ERRNO; 149 if (start_offset != 0) 150 { 151 off_t off = lseek (d, start_offset, SEEK_SET); 152 if (off != start_offset) 153 { 154 close (d); 155 return DWFL_E_ERRNO; 156 } 157 } 158 state->zf = gzdopen (d, "r"); 159 if (unlikely (state->zf == NULL)) 160 { 161 close (d); 162 return DWFL_E_NOMEM; 163 } 164 165 /* From here on, zlib will close D. */ 166 167 return DWFL_E_NOERROR; 168} 169#endif 170 171/* If this is not a compressed image, return DWFL_E_BADELF. 172 If we uncompressed it into *WHOLE, *WHOLE_SIZE, return DWFL_E_NOERROR. 173 Otherwise return an error for bad compressed data or I/O failure. 174 If we return an error after reading the first part of the file, 175 leave that portion malloc'd in *WHOLE, *WHOLE_SIZE. If *WHOLE 176 is not null on entry, we'll use it in lieu of repeating a read. */ 177 178Dwfl_Error internal_function 179unzip (int fd, off_t start_offset, 180 void *mapped, size_t _mapped_size, 181 void **_whole, size_t *whole_size) 182{ 183 struct unzip_state state = 184 { 185#if !USE_INFLATE 186 .zf = NULL, 187#endif 188 .mapped_size = _mapped_size, 189 .whole = _whole, 190 .buffer = NULL, 191 .size = 0, 192 .input_buffer = NULL, 193 .input_pos = 0 194 }; 195 196 if (mapped == NULL) 197 { 198 if (*state.whole == NULL) 199 { 200 state.input_buffer = malloc (READ_SIZE); 201 if (unlikely (state.input_buffer == NULL)) 202 return DWFL_E_NOMEM; 203 204 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, start_offset); 205 if (unlikely (n < 0)) 206 return fail (&state, DWFL_E_ERRNO); 207 208 state.input_pos = n; 209 mapped = state.input_buffer; 210 state.mapped_size = n; 211 } 212 else 213 { 214 state.input_buffer = *state.whole; 215 state.input_pos = state.mapped_size = *whole_size; 216 } 217 } 218 219#define NOMAGIC(magic) \ 220 (state.mapped_size <= sizeof magic || \ 221 memcmp (mapped, magic, sizeof magic - 1)) 222 223 /* First, look at the header. */ 224 if (NOMAGIC (MAGIC) 225#ifdef MAGIC2 226 && NOMAGIC (MAGIC2) 227#endif 228 ) 229 /* Not a compressed file. */ 230 return DWFL_E_BADELF; 231 232#ifdef ZSTD 233 /* special case for libzstd since it is slightly different from the 234 API provided by bzlib and liblzma. */ 235 236 void *next_in = mapped; 237 size_t avail_in = state.mapped_size; 238 void *next_out = NULL; 239 size_t avail_out = 0; 240 size_t total_out = 0; 241 242 size_t result; 243 ZSTD_DCtx *dctx = ZSTD_createDCtx(); 244 if (dctx == NULL) 245 return fail (&state, DWFL_E_NOMEM); 246 247 do 248 { 249 if (avail_in == 0 && state.input_buffer != NULL) 250 { 251 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, 252 start_offset + state.input_pos); 253 if (unlikely (n < 0)) 254 { 255 ZSTD_freeDCtx (dctx); 256 return fail (&state, DWFL_E_ERRNO); 257 } 258 next_in = state.input_buffer; 259 avail_in = n; 260 state.input_pos += n; 261 } 262 if (avail_out == 0) 263 { 264 ptrdiff_t pos = (void *) next_out - state.buffer; 265 if (!bigger_buffer (&state, avail_in)) 266 { 267 ZSTD_freeDCtx (dctx); 268 return fail (&state, DWFL_E_NOMEM); 269 } 270 next_out = state.buffer + pos; 271 avail_out = state.size - pos; 272 } 273 274 ZSTD_inBuffer input = { next_in, avail_in, 0 }; 275 ZSTD_outBuffer output = { next_out, avail_out, 0 }; 276 result = ZSTD_decompressStream (dctx, &output, &input); 277 278 if (! ZSTD_isError (result)) 279 { 280 total_out += output.pos; 281 next_out += output.pos; 282 avail_out -= output.pos; 283 next_in += input.pos; 284 avail_in -= input.pos; 285 } 286 287 if (result == 0) 288 break; 289 } 290 while (avail_in > 0 && ! ZSTD_isError (result)); 291 292 ZSTD_freeDCtx (dctx); 293 294 if (ZSTD_isError (result)) 295 return fail (&state, DWFL_E_ZSTD); 296 297 smaller_buffer (&state, total_out); 298 299#elif USE_INFLATE 300 301 /* This style actually only works with bzlib and liblzma. 302 The stupid zlib interface has nothing to grok the 303 gzip file headers except the slow gzFile interface. */ 304 305 z_stream z = { .next_in = mapped, .avail_in = state.mapped_size }; 306 int result = inflateInit (&z); 307 if (result != Z (OK)) 308 { 309 inflateEnd (&z); 310 return zlib_fail (&state, result); 311 } 312 313 do 314 { 315 if (z.avail_in == 0 && state.input_buffer != NULL) 316 { 317 ssize_t n = pread_retry (fd, state.input_buffer, READ_SIZE, 318 start_offset + state.input_pos); 319 if (unlikely (n < 0)) 320 { 321 inflateEnd (&z); 322 return zlib_fail (&state, Z (ERRNO)); 323 } 324 z.next_in = state.input_buffer; 325 z.avail_in = n; 326 state.input_pos += n; 327 } 328 if (z.avail_out == 0) 329 { 330 ptrdiff_t pos = (void *) z.next_out - state.buffer; 331 if (!bigger_buffer (&state, z.avail_in)) 332 { 333 result = Z (MEM_ERROR); 334 break; 335 } 336 z.next_out = state.buffer + pos; 337 z.avail_out = state.size - pos; 338 } 339 } 340 while ((result = do_inflate (&z)) == Z (OK)); 341 342#ifdef BZLIB 343 uint64_t total_out = (((uint64_t) z.total_out_hi32 << 32) 344 | z.total_out_lo32); 345 smaller_buffer (&state, total_out); 346#else 347 smaller_buffer (&state, z.total_out); 348#endif 349 350 inflateEnd (&z); 351 352 if (result != Z (STREAM_END)) 353 return zlib_fail (&state, result); 354 355#else /* gzip only. */ 356 357 /* Let the decompression library read the file directly. */ 358 359 Dwfl_Error result = open_stream (fd, start_offset, &state); 360 361 if (result == DWFL_E_NOERROR && gzdirect (state.zf)) 362 { 363 gzclose (state.zf); 364 /* Not a compressed stream after all. */ 365 return fail (&state, DWFL_E_BADELF); 366 } 367 368 if (result != DWFL_E_NOERROR) 369 return fail (&state, result); 370 371 ptrdiff_t pos = 0; 372 while (1) 373 { 374 if (!bigger_buffer (&state, 1024)) 375 { 376 gzclose (state.zf); 377 return zlib_fail (&state, Z (MEM_ERROR)); 378 } 379 int n = gzread (state.zf, state.buffer + pos, state.size - pos); 380 if (n < 0) 381 { 382 int code; 383 gzerror (state.zf, &code); 384 gzclose (state.zf); 385 return zlib_fail (&state, code); 386 } 387 if (n == 0) 388 break; 389 pos += n; 390 } 391 392 gzclose (state.zf); 393 smaller_buffer (&state, pos); 394#endif 395 396 free (state.input_buffer); 397 398 *state.whole = state.buffer; 399 *whole_size = state.size; 400 401 return DWFL_E_NOERROR; 402} 403