* General cleanup to better adhere to the style guide and make use of standard
* busybox functions by Glenn McGrath <bug1@optushome.com.au>
*
+ * read_gz interface + associated hacking by Laurence Anderson
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
} huft_t;
static int gunzip_src_fd;
-static int gunzip_dst_fd;
unsigned int gunzip_bytes_out; /* number of output bytes */
static unsigned int gunzip_outbuf_count; /* bytes in output buffer */
-/* This is used to sanify any unused bits from the bitbuffer
- * so they arent skipped when reading trailers (trailing headers) */
-unsigned char gunzip_in_buffer_count;
-unsigned char *gunzip_in_buffer;
-
/* gunzip_window size--must be a power of two, and
* at least 32K for zip's deflate method */
static const int gunzip_wsize = 0x8000;
-
static unsigned char *gunzip_window;
+
static unsigned int *gunzip_crc_table;
unsigned int gunzip_crc;
#define BMAX 16 /* maximum bit length of any code (16 for explode) */
#define N_MAX 288 /* maximum number of codes in any set */
-static unsigned int gunzip_hufts; /* track memory usage */
+/* bitbuffer */
static unsigned int gunzip_bb; /* bit buffer */
static unsigned char gunzip_bk; /* bits in bit buffer */
+/* These control the size of the bytebuffer */
+#define BYTEBUFFER_MAX 0x8000
+static unsigned char *bytebuffer = NULL;
+static unsigned int bytebuffer_offset = 0;
+static unsigned int bytebuffer_size = 0;
+
static const unsigned short mask_bits[] = {
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
+static void fill_bytebuffer(void)
+{
+ if (bytebuffer_offset >= bytebuffer_size) {
+ /* Leave the first 4 bytes empty so we can always unwind the bitbuffer
+ * to the front of the bytebuffer, leave 4 bytes free at end of tail
+ * so we can easily top up buffer in check_trailer_gzip() */
+ bytebuffer_size = 4 + xread(gunzip_src_fd, &bytebuffer[4], BYTEBUFFER_MAX - 8);
+ bytebuffer_offset = 4;
+ }
+}
+
static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current, const unsigned int required)
{
while (*current < required) {
- bitbuffer |= ((unsigned int) xread_char(gunzip_src_fd)) << *current;
+ fill_bytebuffer();
+ bitbuffer |= ((unsigned int) bytebuffer[bytebuffer_offset]) << *current;
+ bytebuffer_offset++;
*current += 8;
}
-
return(bitbuffer);
}
-static void abort_gzip(void)
-{
- error_msg("gzip aborted\n");
- exit(-1);
-}
-
static void make_gunzip_crc_table(void)
{
const unsigned int poly = 0xedb88320; /* polynomial exclusive-or pattern */
/* allocate and link in new table */
q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t));
- gunzip_hufts += z + 1; /* track memory usage */
*t = q + 1; /* link to list for huft_free() */
*(t = &(q->v.t)) = NULL;
u[h] = ++q; /* table starts after link */
return y != 0 && g != 1;
}
-/* ===========================================================================
- * Write the output gunzip_window gunzip_window[0..gunzip_outbuf_count-1] and update crc and gunzip_bytes_out.
- * (Used for the decompressed data only.)
- */
-static void flush_gunzip_window(void)
-{
- int n;
-
- for (n = 0; n < gunzip_outbuf_count; n++) {
- gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
- }
-
- if (write(gunzip_dst_fd, gunzip_window, gunzip_outbuf_count) != gunzip_outbuf_count) {
- error_msg_and_die("Couldnt write");
- }
- gunzip_bytes_out += gunzip_outbuf_count;
- gunzip_outbuf_count = 0;
-}
-
/*
* inflate (decompress) the codes in a deflated (compressed) block.
* Return an error code or zero if it all goes ok.
* tl, td: literal/length and distance decoder tables
* bl, bd: number of bits decoded by tl[] and td[]
*/
-static int inflate_codes(huft_t * tl, huft_t * td, const unsigned int bl, const unsigned int bd)
+static int inflate_codes(huft_t * my_tl, huft_t * my_td, const unsigned int my_bl, const unsigned int my_bd, int setup)
{
- unsigned int e; /* table entry flag/number of extra bits */
- unsigned int n, d; /* length and index for copy */
- unsigned int w; /* current gunzip_window position */
- huft_t *t; /* pointer to table entry */
- unsigned int ml, md; /* masks for bl and bd bits */
- unsigned int b; /* bit buffer */
- unsigned int k; /* number of bits in bit buffer */
-
- /* make local copies of globals */
- b = gunzip_bb; /* initialize bit buffer */
- k = gunzip_bk;
- w = gunzip_outbuf_count; /* initialize gunzip_window position */
-
- /* inflate the coded data */
- ml = mask_bits[bl]; /* precompute masks for speed */
- md = mask_bits[bd];
+ static unsigned int e; /* table entry flag/number of extra bits */
+ static unsigned int n, d; /* length and index for copy */
+ static unsigned int w; /* current gunzip_window position */
+ static huft_t *t; /* pointer to table entry */
+ static unsigned int ml, md; /* masks for bl and bd bits */
+ static unsigned int b; /* bit buffer */
+ static unsigned int k; /* number of bits in bit buffer */
+ static huft_t *tl, *td;
+ static unsigned int bl, bd;
+ static int resumeCopy = 0;
+
+ if (setup) { // 1st time we are called, copy in variables
+ tl = my_tl;
+ td = my_td;
+ bl = my_bl;
+ bd = my_bd;
+ /* make local copies of globals */
+ b = gunzip_bb; /* initialize bit buffer */
+ k = gunzip_bk;
+ w = gunzip_outbuf_count; /* initialize gunzip_window position */
+
+ /* inflate the coded data */
+ ml = mask_bits[bl]; /* precompute masks for speed */
+ md = mask_bits[bd];
+ return 0; // Don't actually do anything the first time
+ }
+
+ if (resumeCopy) goto do_copy;
+
while (1) { /* do until end of block */
b = fill_bitbuffer(b, &k, bl);
if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
do {
if (e == 99) {
- return 1;
+ error_msg_and_die("inflate_codes error 1");;
}
b >>= t->b;
k -= t->b;
gunzip_window[w++] = (unsigned char) t->v.n;
if (w == gunzip_wsize) {
gunzip_outbuf_count = (w);
- flush_gunzip_window();
+ //flush_gunzip_window();
w = 0;
+ return 1; // We have a block to read
}
} else { /* it's an EOB or a length */
if ((e = (t = td + ((unsigned) b & md))->e) > 16)
do {
if (e == 99)
- return 1;
+ error_msg_and_die("inflate_codes error 2");;
b >>= t->b;
k -= t->b;
e -= 16;
k -= e;
/* do the copy */
- do {
+do_copy: do {
n -= (e =
(e =
gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
}
if (w == gunzip_wsize) {
gunzip_outbuf_count = (w);
- flush_gunzip_window();
+ if (n) resumeCopy = 1;
+ else resumeCopy = 0;
+ //flush_gunzip_window();
w = 0;
+ return 1;
}
-
} while (n);
+ resumeCopy = 0;
}
}
gunzip_bb = b; /* restore global bit buffer */
gunzip_bk = k;
+ /* normally just after call to inflate_codes, but save code by putting it here */
+ /* free the decoding tables, return */
+ huft_free(tl);
+ huft_free(td);
+
/* done */
return 0;
}
+static int inflate_stored(int my_n, int my_b_stored, int my_k_stored, int setup)
+{
+ static int n, b_stored, k_stored, w;
+ if (setup) {
+ n = my_n;
+ b_stored = my_b_stored;
+ k_stored = my_k_stored;
+ w = gunzip_outbuf_count; /* initialize gunzip_window position */
+ return 0; // Don't do anything first time
+ }
+
+ /* read and output the compressed data */
+ while (n--) {
+ b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
+ gunzip_window[w++] = (unsigned char) b_stored;
+ if (w == (unsigned int) gunzip_wsize) {
+ gunzip_outbuf_count = (w);
+ //flush_gunzip_window();
+ w = 0;
+ b_stored >>= 8;
+ k_stored -= 8;
+ return 1; // We have a block
+ }
+ b_stored >>= 8;
+ k_stored -= 8;
+ }
+
+ /* restore the globals from the locals */
+ gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
+ gunzip_bb = b_stored; /* restore global bit buffer */
+ gunzip_bk = k_stored;
+ return 0; // Finished
+}
+
/*
* decompress an inflated block
* e: last block flag
*
* GLOBAL VARIABLES: bb, kk,
*/
+ // Return values: -1 = inflate_stored, -2 = inflate_codes
static int inflate_block(int *e)
{
unsigned t; /* block type */
case 0: /* Inflate stored */
{
unsigned int n; /* number of bytes in block */
- unsigned int w; /* current gunzip_window position */
unsigned int b_stored; /* bit buffer */
unsigned int k_stored; /* number of bits in bit buffer */
/* make local copies of globals */
b_stored = gunzip_bb; /* initialize bit buffer */
k_stored = gunzip_bk;
- w = gunzip_outbuf_count; /* initialize gunzip_window position */
/* go to byte boundary */
n = k_stored & 7;
b_stored >>= 16;
k_stored -= 16;
- /* read and output the compressed data */
- while (n--) {
- b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
- gunzip_window[w++] = (unsigned char) b_stored;
- if (w == (unsigned int) gunzip_wsize) {
- gunzip_outbuf_count = (w);
- flush_gunzip_window();
- w = 0;
- }
- b_stored >>= 8;
- k_stored -= 8;
- }
-
- /* restore the globals from the locals */
- gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
- gunzip_bb = b_stored; /* restore global bit buffer */
- gunzip_bk = k_stored;
- return 0;
+ inflate_stored(n, b_stored, k_stored, 1); // Setup inflate_stored
+ return -1;
}
case 1: /* Inflate fixed
* decompress an inflated type 1 (fixed Huffman codes) block. We should
}
/* decompress until an end-of-block code */
- if (inflate_codes(tl, td, bl, bd)) {
- return 1;
- }
-
- /* free the decoding tables, return */
- huft_free(tl);
- huft_free(td);
- return 0;
+ inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes
+
+ /* huft_free code moved into inflate_codes */
+
+ return -2;
}
case 2: /* Inflate dynamic */
{
}
/* decompress until an end-of-block code */
- if (inflate_codes(tl, td, bl, bd)) {
- return 1;
- }
+ inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes
- /* free the decoding tables, return */
- huft_free(tl);
- huft_free(td);
- return 0;
+ /* huft_free code moved into inflate_codes */
+
+ return -2;
}
default:
/* bad block type */
- error_msg("bad block type %d\n", t);
- return 2;
+ error_msg_and_die("bad block type %d\n", t);
+ }
+}
+
+static void calculate_gunzip_crc(void)
+{
+ int n;
+ for (n = 0; n < gunzip_outbuf_count; n++) {
+ gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
+ }
+ gunzip_bytes_out += gunzip_outbuf_count;
+}
+
+static int inflate_get_next_window(void)
+{
+ static int needAnotherBlock = 1;
+ static int method = -1; // Method == -1 for stored, -2 for codes
+ static int e = 0;
+
+ gunzip_outbuf_count = 0;
+
+ while(1) {
+ int ret;
+
+ if (needAnotherBlock) {
+ if(e) {
+ calculate_gunzip_crc();
+ return 0;
+ } // Last block
+ method = inflate_block(&e);
+ needAnotherBlock = 0;
+ }
+
+ switch (method) {
+ case -1: ret = inflate_stored(0,0,0,0);
+ break;
+ case -2: ret = inflate_codes(0,0,0,0,0);
+ break;
+ default: error_msg_and_die("inflate error %d", method);
+ }
+
+ if (ret == 1) {
+ calculate_gunzip_crc();
+ return 1; // More data left
+ } else needAnotherBlock = 1; // End of that block
}
+ /* Doesnt get here */
}
/*
- * decompress an inflated entry
+ * User functions
*
- * GLOBAL VARIABLES: gunzip_outbuf_count, bk, gunzip_bb, hufts, inptr
+ * read_gz, GZ_gzReadOpen, GZ_gzReadClose, inflate
*/
-extern int inflate(int in, int out)
+
+extern ssize_t read_gz(int fd, void *buf, size_t count)
+{
+ static int morebytes = 0, finished = 0;
+
+ if (morebytes) {
+ int bytesRead = morebytes > count ? count : morebytes;
+ memcpy(buf, gunzip_window + (gunzip_outbuf_count - morebytes), bytesRead);
+ morebytes -= bytesRead;
+ return bytesRead;
+ } else if (finished) {
+ return 0;
+ } else if (count >= 0x8000) { // We can decompress direcly to the buffer, 32k at a time
+ // Could decompress to larger buffer, but it must be a power of 2, and calculating that is probably more expensive than the benefit
+ unsigned char *old_gunzip_window = gunzip_window; // Save old window
+ gunzip_window = buf;
+ if (inflate_get_next_window() == 0) finished = 1;
+ gunzip_window = old_gunzip_window; // Restore old window
+ return gunzip_outbuf_count;
+ } else { // Oh well, need to split up the gunzip_window
+ int bytesRead;
+ if (inflate_get_next_window() == 0) finished = 1;
+ morebytes = gunzip_outbuf_count;
+ bytesRead = morebytes > count ? count : morebytes;
+ memcpy(buf, gunzip_window, bytesRead);
+ morebytes -= bytesRead;
+ return bytesRead;
+ }
+
+}
+
+extern void GZ_gzReadOpen(int fd, void *unused, int nUnused)
{
typedef void (*sig_type) (int);
- int e; /* last block flag */
- int r; /* result code */
- unsigned h = 0; /* maximum struct huft's malloc'ed */
/* Allocate all global buffers (for DYN_ALLOC option) */
- gunzip_window = xmalloc(0x8000);
+ gunzip_window = xmalloc(gunzip_wsize);
gunzip_outbuf_count = 0;
gunzip_bytes_out = 0;
+ gunzip_src_fd = fd;
- gunzip_src_fd = in;
- gunzip_dst_fd = out;
-
- gunzip_in_buffer = malloc(8);
-
- if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
- (void) signal(SIGINT, (sig_type) abort_gzip);
- }
-#ifdef SIGHUP
- if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
- (void) signal(SIGHUP, (sig_type) abort_gzip);
- }
-#endif
+ /* Input buffer */
+ bytebuffer = xmalloc(BYTEBUFFER_MAX);
/* initialize gunzip_window, bit buffer */
gunzip_bk = 0;
/* Create the crc table */
make_gunzip_crc_table();
+}
- /* decompress until the last block */
- do {
- gunzip_hufts = 0;
- r = inflate_block(&e);
- if (r != 0) {
- error_msg_and_die("inflate error %d", r);
- return r;
- }
- if (gunzip_hufts > h) {
- h = gunzip_hufts;
- }
- } while (!e);
-
- /* write any buffered uncompressed data */
- flush_gunzip_window();
- free(gunzip_window);
-
+extern void GZ_gzReadClose(void)
+{
/* Cleanup */
+ free(gunzip_window);
free(gunzip_crc_table);
/* Store unused bytes in a global buffer so calling applets can access it */
- gunzip_in_buffer_count = 0;
if (gunzip_bk >= 8) {
/* Undo too much lookahead. The next read will be byte aligned
* so we can discard unused bits in the last meaningful byte. */
- gunzip_in_buffer[gunzip_in_buffer_count] = gunzip_bb & 0xff;
- gunzip_in_buffer_count++;
+ bytebuffer_offset--;
+ bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff;
gunzip_bb >>= 8;
gunzip_bk -= 8;
}
+}
+
+/*extern int inflate(int in, int out) // Useful for testing read_gz
+{
+ char buf[8192];
+ ssize_t nread, nwrote;
+
+ GZ_gzReadOpen(in, 0, 0);
+ while(1) { // Robbed from copyfd.c
+ nread = read_gz(in, buf, sizeof(buf));
+ if (nread == 0) break; // no data to write
+ else if (nread == -1) {
+ perror_msg("read");
+ return -1;
+ }
+ nwrote = full_write(out, buf, nread);
+ if (nwrote == -1) {
+ perror_msg("write");
+ return -1;
+ }
+ }
+ GZ_gzReadClose();
+ return 0;
+}*/
- /* return success */
+extern int inflate(int in, int out)
+{
+ ssize_t nwrote;
+ GZ_gzReadOpen(in, 0, 0);
+ while(1) {
+ int ret = inflate_get_next_window();
+ nwrote = full_write(out, gunzip_window, gunzip_outbuf_count);
+ if (nwrote == -1) {
+ perror_msg("write");
+ return -1;
+ }
+ if (ret == 0) break;
+ }
+ GZ_gzReadClose();
return 0;
}
+
+extern void check_trailer_gzip(int src_fd)
+{
+ unsigned int stored_crc = 0;
+ unsigned char count;
+
+ /* top up the input buffer with the rest of the trailer */
+ count = bytebuffer_size - bytebuffer_offset;
+ if (count < 8) {
+ xread_all(src_fd, &bytebuffer[bytebuffer_size], 8 - count);
+ bytebuffer_size += 8 - count;
+ }
+ for (count = 0; count != 4; count++) {
+ stored_crc |= (bytebuffer[bytebuffer_offset] << (count * 8));
+ bytebuffer_offset++;
+ }
+
+ /* Validate decompression - crc */
+ if (stored_crc != (gunzip_crc ^ 0xffffffffL)) {
+ error_msg_and_die("crc error");
+ }
+
+ /* Validate decompression - size */
+ if (gunzip_bytes_out !=
+ (bytebuffer[bytebuffer_offset] | (bytebuffer[bytebuffer_offset+1] << 8) |
+ (bytebuffer[bytebuffer_offset+2] << 16) | (bytebuffer[bytebuffer_offset+3] << 24))) {
+ error_msg_and_die("Incorrect length, but crc is correct");
+ }
+
+}