/* * unzip.c * * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. * * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 * puts by Nick Holloway 1993, better puts by Martin Mares 1995 * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 * * Adapted for MEMDISK by H. Peter Anvin, April 2003 */ #include #include "memdisk.h" #include "conio.h" /* * gzip declarations */ #define OF(args) args #define STATIC static #define memzero(s, n) memset ((s), 0, (n)) typedef uint8_t uch; typedef uint16_t ush; typedef uint32_t ulg; #define WSIZE 0x8000 /* Window size must be at least 32k, */ /* and a power of two */ static uch *inbuf; /* input pointer */ static uch window[WSIZE]; /* sliding output window buffer */ static unsigned insize; /* total input bytes read */ static unsigned inbytes; /* valid bytes in inbuf */ static unsigned outcnt; /* bytes in output buffer */ /* gzip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ #define RESERVED 0xC0 /* bit 6,7: reserved */ /* Diagnostic functions */ #ifdef DEBUG # define Assert(cond,msg) {if(!(cond)) error(msg);} # define Trace(x) fprintf x # define Tracev(x) {if (verbose) fprintf x ;} # define Tracevv(x) {if (verbose>1) fprintf x ;} # define Tracec(c,x) {if (verbose && (c)) fprintf x ;} # define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} #else # define Assert(cond,msg) # define Trace(x) # define Tracev(x) # define Tracevv(x) # define Tracec(c,x) # define Tracecv(c,x) #endif static int fill_inbuf(void); static void flush_window(void); static void error(char *m); static void gzip_mark(void **); static void gzip_release(void **); extern ulg crc_32_tab[256]; /* Get byte from input buffer */ static inline uch get_byte(void) { if ( inbytes ) { uch b = *inbuf++; inbytes--; return b; } else { return fill_inbuf(); /* Input buffer underrun */ } } /* Unget byte from input buffer */ static inline void unget_byte(void) { inbytes++; inbuf--; } static ulg bytes_out = 0; /* Number of bytes output */ static uch *output_data; /* Output data pointer */ static ulg output_size; /* Number of output bytes expected */ static void *malloc(int size); static void free(void *where); static ulg free_mem_ptr, free_mem_end_ptr; #include "inflate.c" static void *malloc(int size) { void *p; if (size < 0) error("malloc error"); free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ p = (void *)free_mem_ptr; free_mem_ptr += size; if (free_mem_ptr >= free_mem_end_ptr) error("out of memory"); return p; } static void free(void *where) { /* Don't care */ (void)where; } static void gzip_mark(void **ptr) { *ptr = (void *) free_mem_ptr; } static void gzip_release(void **ptr) { free_mem_ptr = (long) *ptr; } /* =========================================================================== * Fill the input buffer. This is called only when the buffer is empty * and at least one byte is really needed. */ static int fill_inbuf(void) { /* This should never happen. We have already pointed the algorithm to all the data we have. */ printf("failed\nDecompression error: ran out of input data\n"); die(); } /* =========================================================================== * Write the output window window[0..outcnt-1] and update crc and bytes_out. * (Used for the decompressed data only.) */ static void flush_window(void) { ulg c = crc; /* temporary variable */ unsigned n; uch *in, *out, ch; if ( bytes_out+outcnt > output_size ) error("output buffer overrun"); in = window; out = output_data; for (n = 0; n < outcnt; n++) { ch = *out++ = *in++; c = crc_32_tab[(c ^ ch) & 0xff] ^ (c >> 8); } crc = c; output_data = out; bytes_out += (ulg)outcnt; outcnt = 0; } static void error(char *x) { printf("failed\nDecompression error: %s\n", x); die(); } /* GZIP header */ struct gzip_header { uint16_t magic; uint8_t method; uint8_t flags; uint32_t timestamp; uint8_t extra_flags; uint8_t os_type; } __attribute__ ((packed)); /* (followed by optional and variable length "extra", "original name", and "comment" fields) */ struct gzip_trailer { uint32_t crc; uint32_t dbytes; } __attribute__ ((packed)); /* PKZIP header. See * . */ struct pkzip_header { uint32_t magic; uint16_t version; uint16_t flags; uint16_t method; uint16_t modified_time; uint16_t modified_date; uint32_t crc; uint32_t zbytes; uint32_t dbytes; uint16_t filename_len; uint16_t extra_len; } __attribute__ ((packed)); /* (followed by optional and variable length "filename" and "extra" fields) */ /* gzip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ #define RESERVED 0xC0 /* bit 6,7: reserved */ /* pkzip flag byte */ #define PK_ENCRYPTED 0x01 /* bit 0 set: file is encrypted */ #define PK_DATADESC 0x08 /* bit 3 set: file has trailing "data descriptor" */ #define PK_UNSUPPORTED 0xFFF0 /* All other bits must be zero */ /* Return 0 if (indata, size) points to a ZIP file, and fill in compressed data size, uncompressed data size, CRC, and offset of data. If indata is not a ZIP file, return -1. */ int check_zip(void *indata, uint32_t size, uint32_t *zbytes_p, uint32_t *dbytes_p, uint32_t *orig_crc, uint32_t *offset_p) { struct gzip_header *gzh = (struct gzip_header *)indata; struct pkzip_header *pkzh = (struct pkzip_header *)indata; uint32_t offset; if (gzh->magic == 0x8b1f) { struct gzip_trailer *gzt = indata + size - sizeof (struct gzip_trailer); /* We only support method #8, DEFLATED */ if (gzh->method != 8) { error("gzip file uses invalid method"); return -1; } if (gzh->flags & ENCRYPTED) { error("gzip file is encrypted; not supported"); return -1; } if (gzh->flags & CONTINUATION) { error("gzip file is a continuation file; not supported"); return -1; } if (gzh->flags & RESERVED) { error("gzip file has unsupported flags"); return -1; } offset = sizeof (*gzh); if (gzh->flags & EXTRA_FIELD) { /* Skip extra field */ unsigned len = *(unsigned *)(indata + offset); offset += 2 + len; } if (gzh->flags & ORIG_NAME) { /* Discard the old name */ uint8_t *p = indata; while (p[offset] != 0 && offset < size) { offset++; } offset++; } if (gzh->flags & COMMENT) { /* Discard the comment */ uint8_t *p = indata; while (p[offset] != 0 && offset < size) { offset++; } offset++; } if (offset > size) { error ("gzip file corrupt"); return -1; } *zbytes_p = size - offset - sizeof (struct gzip_trailer); *dbytes_p = gzt->dbytes; *orig_crc = gzt->crc; *offset_p = offset; return 0; } else if (pkzh->magic == 0x04034b50UL) { /* Magic number matches pkzip file. */ offset = sizeof (*pkzh); if (pkzh->flags & PK_ENCRYPTED) { error("pkzip file is encrypted; not supported"); return -1; } if (pkzh->flags & PK_DATADESC) { error("pkzip file uses data_descriptor field; not supported"); return -1; } if (pkzh->flags & PK_UNSUPPORTED) { error("pkzip file has unsupported flags"); return -1; } /* We only support method #8, DEFLATED */ if (pkzh->method != 8) { error("pkzip file uses invalid method"); return -1; } /* skip header */ offset = sizeof (*pkzh); /* skip filename */ offset += pkzh->filename_len; /* skip extra field */ offset += pkzh->extra_len; if (offset + pkzh->zbytes > size) { error ("pkzip file corrupt"); return -1; } *zbytes_p = pkzh->zbytes; *dbytes_p = pkzh->dbytes; *orig_crc = pkzh->crc; *offset_p = offset; return 0; } else { /* Magic number does not match. */ return -1; } error ("Internal error in check_zip"); return -1; } /* * Decompress the image, trying to flush the end of it as close * to end_mem as possible. Return a pointer to the data block, * and change datalen. */ extern void _end; void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes, uint32_t orig_crc, void *target) { /* Set up the heap; it's the 64K after the bounce buffer */ free_mem_ptr = (ulg)sys_bounce + 0x10000; free_mem_end_ptr = free_mem_ptr + 0x10000; /* Set up input buffer */ inbuf = indata; /* Sometimes inflate() looks beyond the end of the compressed data, but it always backs up before it is done. So we give it 4 bytes of slack. */ insize = inbytes = zbytes + 4; /* Set up output buffer */ outcnt = 0; output_data = target; output_size = dbytes; bytes_out = 0; makecrc(); gunzip(); /* Verify that gunzip() consumed the entire input. */ if (inbytes != 4) error("compressed data length error"); /* Check the uncompressed data length and CRC. */ if ( bytes_out != dbytes ) error("uncompressed data length error"); if (orig_crc != CRC_VALUE) error("crc error"); puts("ok\n"); return target; }