[e16e8f2] | 1 | /* |
---|
| 2 | * unzip.c |
---|
| 3 | * |
---|
| 4 | * This is a collection of several routines from gzip-1.0.3 |
---|
| 5 | * adapted for Linux. |
---|
| 6 | * |
---|
| 7 | * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 |
---|
| 8 | * puts by Nick Holloway 1993, better puts by Martin Mares 1995 |
---|
| 9 | * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 |
---|
| 10 | * |
---|
| 11 | * Adapted for MEMDISK by H. Peter Anvin, April 2003 |
---|
| 12 | */ |
---|
| 13 | |
---|
| 14 | #include <stdint.h> |
---|
| 15 | #include "memdisk.h" |
---|
| 16 | #include "conio.h" |
---|
| 17 | |
---|
| 18 | #undef DEBUG /* Means something different for this file */ |
---|
| 19 | |
---|
| 20 | /* |
---|
| 21 | * gzip declarations |
---|
| 22 | */ |
---|
| 23 | |
---|
| 24 | #define OF(args) args |
---|
| 25 | #define STATIC static |
---|
| 26 | |
---|
| 27 | #define memzero(s, n) memset ((s), 0, (n)) |
---|
| 28 | |
---|
| 29 | typedef uint8_t uch; |
---|
| 30 | typedef uint16_t ush; |
---|
| 31 | typedef uint32_t ulg; |
---|
| 32 | |
---|
| 33 | #define WSIZE 0x8000 /* Window size must be at least 32k, */ |
---|
| 34 | /* and a power of two */ |
---|
| 35 | |
---|
| 36 | static uch *inbuf; /* input pointer */ |
---|
| 37 | static uch window[WSIZE]; /* sliding output window buffer */ |
---|
| 38 | |
---|
| 39 | static unsigned insize; /* total input bytes read */ |
---|
| 40 | static unsigned inbytes; /* valid bytes in inbuf */ |
---|
| 41 | static unsigned outcnt; /* bytes in output buffer */ |
---|
| 42 | |
---|
| 43 | /* gzip flag byte */ |
---|
| 44 | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ |
---|
| 45 | #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ |
---|
| 46 | #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ |
---|
| 47 | #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ |
---|
| 48 | #define COMMENT 0x10 /* bit 4 set: file comment present */ |
---|
| 49 | #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ |
---|
| 50 | #define RESERVED 0xC0 /* bit 6,7: reserved */ |
---|
| 51 | |
---|
| 52 | /* Diagnostic functions */ |
---|
| 53 | #ifdef DEBUG |
---|
| 54 | # define Assert(cond,msg) {if(!(cond)) error(msg);} |
---|
| 55 | # define Trace(x) fprintf x |
---|
| 56 | # define Tracev(x) {if (verbose) fprintf x ;} |
---|
| 57 | # define Tracevv(x) {if (verbose>1) fprintf x ;} |
---|
| 58 | # define Tracec(c,x) {if (verbose && (c)) fprintf x ;} |
---|
| 59 | # define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} |
---|
| 60 | #else |
---|
| 61 | # define Assert(cond,msg) |
---|
| 62 | # define Trace(x) |
---|
| 63 | # define Tracev(x) |
---|
| 64 | # define Tracevv(x) |
---|
| 65 | # define Tracec(c,x) |
---|
| 66 | # define Tracecv(c,x) |
---|
| 67 | #endif |
---|
| 68 | |
---|
| 69 | static int fill_inbuf(void); |
---|
| 70 | static void flush_window(void); |
---|
| 71 | static void error(char *m); |
---|
| 72 | static void gzip_mark(void **); |
---|
| 73 | static void gzip_release(void **); |
---|
| 74 | |
---|
| 75 | static ulg crc_32_tab[256]; |
---|
| 76 | |
---|
| 77 | /* Get byte from input buffer */ |
---|
| 78 | static inline uch get_byte(void) |
---|
| 79 | { |
---|
| 80 | if (inbytes) { |
---|
| 81 | uch b = *inbuf++; |
---|
| 82 | inbytes--; |
---|
| 83 | return b; |
---|
| 84 | } else { |
---|
| 85 | return fill_inbuf(); /* Input buffer underrun */ |
---|
| 86 | } |
---|
| 87 | } |
---|
| 88 | |
---|
| 89 | /* Unget byte from input buffer */ |
---|
| 90 | static inline void unget_byte(void) |
---|
| 91 | { |
---|
| 92 | inbytes++; |
---|
| 93 | inbuf--; |
---|
| 94 | } |
---|
| 95 | |
---|
| 96 | static ulg bytes_out = 0; /* Number of bytes output */ |
---|
| 97 | static uch *output_data; /* Output data pointer */ |
---|
| 98 | static ulg output_size; /* Number of output bytes expected */ |
---|
| 99 | |
---|
| 100 | static void *malloc(int size); |
---|
| 101 | static void free(void *where); |
---|
| 102 | |
---|
| 103 | static ulg free_mem_ptr, free_mem_end_ptr; |
---|
| 104 | |
---|
| 105 | #include "inflate.c" |
---|
| 106 | |
---|
| 107 | static void *malloc(int size) |
---|
| 108 | { |
---|
| 109 | void *p; |
---|
| 110 | |
---|
| 111 | if (size < 0) |
---|
| 112 | error("malloc error"); |
---|
| 113 | |
---|
| 114 | free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ |
---|
| 115 | |
---|
| 116 | p = (void *)free_mem_ptr; |
---|
| 117 | free_mem_ptr += size; |
---|
| 118 | |
---|
| 119 | if (free_mem_ptr >= free_mem_end_ptr) |
---|
| 120 | error("out of memory"); |
---|
| 121 | |
---|
| 122 | return p; |
---|
| 123 | } |
---|
| 124 | |
---|
| 125 | static void free(void *where) |
---|
| 126 | { |
---|
| 127 | /* Don't care */ |
---|
| 128 | (void)where; |
---|
| 129 | } |
---|
| 130 | |
---|
| 131 | static void gzip_mark(void **ptr) |
---|
| 132 | { |
---|
| 133 | *ptr = (void *)free_mem_ptr; |
---|
| 134 | } |
---|
| 135 | |
---|
| 136 | static void gzip_release(void **ptr) |
---|
| 137 | { |
---|
| 138 | free_mem_ptr = (long)*ptr; |
---|
| 139 | } |
---|
| 140 | |
---|
| 141 | /* =========================================================================== |
---|
| 142 | * Fill the input buffer. This is called only when the buffer is empty |
---|
| 143 | * and at least one byte is really needed. |
---|
| 144 | */ |
---|
| 145 | static int fill_inbuf(void) |
---|
| 146 | { |
---|
| 147 | /* This should never happen. We have already pointed the algorithm |
---|
| 148 | to all the data we have. */ |
---|
| 149 | die("failed\nDecompression error: ran out of input data\n"); |
---|
| 150 | } |
---|
| 151 | |
---|
| 152 | /* =========================================================================== |
---|
| 153 | * Write the output window window[0..outcnt-1] and update crc and bytes_out. |
---|
| 154 | * (Used for the decompressed data only.) |
---|
| 155 | */ |
---|
| 156 | static void flush_window(void) |
---|
| 157 | { |
---|
| 158 | ulg c = crc; /* temporary variable */ |
---|
| 159 | unsigned n; |
---|
| 160 | uch *in, *out, ch; |
---|
| 161 | |
---|
| 162 | if (bytes_out + outcnt > output_size) |
---|
| 163 | error("output buffer overrun"); |
---|
| 164 | |
---|
| 165 | in = window; |
---|
| 166 | out = output_data; |
---|
| 167 | for (n = 0; n < outcnt; n++) { |
---|
| 168 | ch = *out++ = *in++; |
---|
| 169 | c = crc_32_tab[(c ^ ch) & 0xff] ^ (c >> 8); |
---|
| 170 | } |
---|
| 171 | crc = c; |
---|
| 172 | output_data = out; |
---|
| 173 | bytes_out += (ulg) outcnt; |
---|
| 174 | outcnt = 0; |
---|
| 175 | } |
---|
| 176 | |
---|
| 177 | static void error(char *x) |
---|
| 178 | { |
---|
| 179 | die("failed\nDecompression error: %s\n", x); |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | /* GZIP header */ |
---|
| 183 | struct gzip_header { |
---|
| 184 | uint16_t magic; |
---|
| 185 | uint8_t method; |
---|
| 186 | uint8_t flags; |
---|
| 187 | uint32_t timestamp; |
---|
| 188 | uint8_t extra_flags; |
---|
| 189 | uint8_t os_type; |
---|
| 190 | } __attribute__ ((packed)); |
---|
| 191 | /* (followed by optional and variable length "extra", "original name", |
---|
| 192 | and "comment" fields) */ |
---|
| 193 | |
---|
| 194 | struct gzip_trailer { |
---|
| 195 | uint32_t crc; |
---|
| 196 | uint32_t dbytes; |
---|
| 197 | } __attribute__ ((packed)); |
---|
| 198 | |
---|
| 199 | /* PKZIP header. See |
---|
| 200 | * <http://www.pkware.com/products/enterprise/white_papers/appnote.html>. |
---|
| 201 | */ |
---|
| 202 | struct pkzip_header { |
---|
| 203 | uint32_t magic; |
---|
| 204 | uint16_t version; |
---|
| 205 | uint16_t flags; |
---|
| 206 | uint16_t method; |
---|
| 207 | uint16_t modified_time; |
---|
| 208 | uint16_t modified_date; |
---|
| 209 | uint32_t crc; |
---|
| 210 | uint32_t zbytes; |
---|
| 211 | uint32_t dbytes; |
---|
| 212 | uint16_t filename_len; |
---|
| 213 | uint16_t extra_len; |
---|
| 214 | } __attribute__ ((packed)); |
---|
| 215 | /* (followed by optional and variable length "filename" and "extra" |
---|
| 216 | fields) */ |
---|
| 217 | |
---|
| 218 | /* gzip flag byte */ |
---|
| 219 | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ |
---|
| 220 | #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ |
---|
| 221 | #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ |
---|
| 222 | #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ |
---|
| 223 | #define COMMENT 0x10 /* bit 4 set: file comment present */ |
---|
| 224 | #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ |
---|
| 225 | #define RESERVED 0xC0 /* bit 6,7: reserved */ |
---|
| 226 | |
---|
| 227 | /* pkzip flag byte */ |
---|
| 228 | #define PK_ENCRYPTED 0x01 /* bit 0 set: file is encrypted */ |
---|
| 229 | #define PK_DATADESC 0x08 /* bit 3 set: file has trailing "data |
---|
| 230 | descriptor" */ |
---|
| 231 | #define PK_UNSUPPORTED 0xFFF0 /* All other bits must be zero */ |
---|
| 232 | |
---|
| 233 | /* Return 0 if (indata, size) points to a ZIP file, and fill in |
---|
| 234 | compressed data size, uncompressed data size, CRC, and offset of |
---|
| 235 | data. |
---|
| 236 | |
---|
| 237 | If indata is not a ZIP file, return -1. */ |
---|
| 238 | int check_zip(void *indata, uint32_t size, uint32_t * zbytes_p, |
---|
| 239 | uint32_t * dbytes_p, uint32_t * orig_crc, uint32_t * offset_p) |
---|
| 240 | { |
---|
| 241 | struct gzip_header *gzh = (struct gzip_header *)indata; |
---|
| 242 | struct pkzip_header *pkzh = (struct pkzip_header *)indata; |
---|
| 243 | uint32_t offset; |
---|
| 244 | |
---|
| 245 | if (gzh->magic == 0x8b1f) { |
---|
| 246 | struct gzip_trailer *gzt = indata + size - sizeof(struct gzip_trailer); |
---|
| 247 | /* We only support method #8, DEFLATED */ |
---|
| 248 | if (gzh->method != 8) { |
---|
| 249 | error("gzip file uses invalid method"); |
---|
| 250 | return -1; |
---|
| 251 | } |
---|
| 252 | if (gzh->flags & ENCRYPTED) { |
---|
| 253 | error("gzip file is encrypted; not supported"); |
---|
| 254 | return -1; |
---|
| 255 | } |
---|
| 256 | if (gzh->flags & CONTINUATION) { |
---|
| 257 | error("gzip file is a continuation file; not supported"); |
---|
| 258 | return -1; |
---|
| 259 | } |
---|
| 260 | if (gzh->flags & RESERVED) { |
---|
| 261 | error("gzip file has unsupported flags"); |
---|
| 262 | return -1; |
---|
| 263 | } |
---|
| 264 | offset = sizeof(*gzh); |
---|
| 265 | if (gzh->flags & EXTRA_FIELD) { |
---|
| 266 | /* Skip extra field */ |
---|
| 267 | unsigned len = *(unsigned *)(indata + offset); |
---|
| 268 | offset += 2 + len; |
---|
| 269 | } |
---|
| 270 | if (gzh->flags & ORIG_NAME) { |
---|
| 271 | /* Discard the old name */ |
---|
| 272 | uint8_t *p = indata; |
---|
| 273 | while (p[offset] != 0 && offset < size) { |
---|
| 274 | offset++; |
---|
| 275 | } |
---|
| 276 | offset++; |
---|
| 277 | } |
---|
| 278 | |
---|
| 279 | if (gzh->flags & COMMENT) { |
---|
| 280 | /* Discard the comment */ |
---|
| 281 | uint8_t *p = indata; |
---|
| 282 | while (p[offset] != 0 && offset < size) { |
---|
| 283 | offset++; |
---|
| 284 | } |
---|
| 285 | offset++; |
---|
| 286 | } |
---|
| 287 | |
---|
| 288 | if (offset > size) { |
---|
| 289 | error("gzip file corrupt"); |
---|
| 290 | return -1; |
---|
| 291 | } |
---|
| 292 | *zbytes_p = size - offset - sizeof(struct gzip_trailer); |
---|
| 293 | *dbytes_p = gzt->dbytes; |
---|
| 294 | *orig_crc = gzt->crc; |
---|
| 295 | *offset_p = offset; |
---|
| 296 | return 0; |
---|
| 297 | } else if (pkzh->magic == 0x04034b50UL) { |
---|
| 298 | /* Magic number matches pkzip file. */ |
---|
| 299 | |
---|
| 300 | offset = sizeof(*pkzh); |
---|
| 301 | if (pkzh->flags & PK_ENCRYPTED) { |
---|
| 302 | error("pkzip file is encrypted; not supported"); |
---|
| 303 | return -1; |
---|
| 304 | } |
---|
| 305 | if (pkzh->flags & PK_DATADESC) { |
---|
| 306 | error("pkzip file uses data_descriptor field; not supported"); |
---|
| 307 | return -1; |
---|
| 308 | } |
---|
| 309 | if (pkzh->flags & PK_UNSUPPORTED) { |
---|
| 310 | error("pkzip file has unsupported flags"); |
---|
| 311 | return -1; |
---|
| 312 | } |
---|
| 313 | |
---|
| 314 | /* We only support method #8, DEFLATED */ |
---|
| 315 | if (pkzh->method != 8) { |
---|
| 316 | error("pkzip file uses invalid method"); |
---|
| 317 | return -1; |
---|
| 318 | } |
---|
| 319 | /* skip header */ |
---|
| 320 | offset = sizeof(*pkzh); |
---|
| 321 | /* skip filename */ |
---|
| 322 | offset += pkzh->filename_len; |
---|
| 323 | /* skip extra field */ |
---|
| 324 | offset += pkzh->extra_len; |
---|
| 325 | |
---|
| 326 | if (offset + pkzh->zbytes > size) { |
---|
| 327 | error("pkzip file corrupt"); |
---|
| 328 | return -1; |
---|
| 329 | } |
---|
| 330 | |
---|
| 331 | *zbytes_p = pkzh->zbytes; |
---|
| 332 | *dbytes_p = pkzh->dbytes; |
---|
| 333 | *orig_crc = pkzh->crc; |
---|
| 334 | *offset_p = offset; |
---|
| 335 | return 0; |
---|
| 336 | } else { |
---|
| 337 | /* Magic number does not match. */ |
---|
| 338 | return -1; |
---|
| 339 | } |
---|
| 340 | |
---|
| 341 | error("Internal error in check_zip"); |
---|
| 342 | return -1; |
---|
| 343 | } |
---|
| 344 | |
---|
| 345 | /* |
---|
| 346 | * Decompress the image, trying to flush the end of it as close |
---|
| 347 | * to end_mem as possible. Return a pointer to the data block, |
---|
| 348 | * and change datalen. |
---|
| 349 | */ |
---|
| 350 | extern void _end; |
---|
| 351 | |
---|
| 352 | static char heap[65536]; |
---|
| 353 | |
---|
| 354 | void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes, |
---|
| 355 | uint32_t orig_crc, void *target) |
---|
| 356 | { |
---|
| 357 | /* Set up the heap; it is simply a chunk of bss memory */ |
---|
| 358 | free_mem_ptr = (size_t)heap; |
---|
| 359 | free_mem_end_ptr = (size_t)heap + sizeof heap; |
---|
| 360 | |
---|
| 361 | /* Set up input buffer */ |
---|
| 362 | inbuf = indata; |
---|
| 363 | /* Sometimes inflate() looks beyond the end of the compressed data, |
---|
| 364 | but it always backs up before it is done. So we give it 4 bytes |
---|
| 365 | of slack. */ |
---|
| 366 | insize = inbytes = zbytes + 4; |
---|
| 367 | |
---|
| 368 | /* Set up output buffer */ |
---|
| 369 | outcnt = 0; |
---|
| 370 | output_data = target; |
---|
| 371 | output_size = dbytes; |
---|
| 372 | bytes_out = 0; |
---|
| 373 | |
---|
| 374 | makecrc(); |
---|
| 375 | gunzip(); |
---|
| 376 | |
---|
| 377 | /* Verify that gunzip() consumed the entire input. */ |
---|
| 378 | if (inbytes != 4) |
---|
| 379 | error("compressed data length error"); |
---|
| 380 | |
---|
| 381 | /* Check the uncompressed data length and CRC. */ |
---|
| 382 | if (bytes_out != dbytes) |
---|
| 383 | error("uncompressed data length error"); |
---|
| 384 | |
---|
| 385 | if (orig_crc != CRC_VALUE) |
---|
| 386 | error("crc error"); |
---|
| 387 | |
---|
| 388 | puts("ok\n"); |
---|
| 389 | |
---|
| 390 | return target; |
---|
| 391 | } |
---|