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