392 lines
9.7 KiB
C
392 lines
9.7 KiB
C
/*
|
|
* 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 <stdint.h>
|
|
#include "memdisk.h"
|
|
#include "conio.h"
|
|
|
|
#undef DEBUG /* Means something different for this file */
|
|
|
|
/*
|
|
* 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 **);
|
|
|
|
static 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. */
|
|
die("failed\nDecompression error: ran out of input data\n");
|
|
}
|
|
|
|
/* ===========================================================================
|
|
* 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)
|
|
{
|
|
die("failed\nDecompression error: %s\n", x);
|
|
}
|
|
|
|
/* 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
|
|
* <http://www.pkware.com/products/enterprise/white_papers/appnote.html>.
|
|
*/
|
|
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;
|
|
|
|
static char heap[65536];
|
|
|
|
void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes,
|
|
uint32_t orig_crc, void *target)
|
|
{
|
|
/* Set up the heap; it is simply a chunk of bss memory */
|
|
free_mem_ptr = (size_t)heap;
|
|
free_mem_end_ptr = (size_t)heap + sizeof heap;
|
|
|
|
/* 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;
|
|
}
|