From 87ac7028e01cdc4f504ea558a6ae3d086ed1bf2b Mon Sep 17 00:00:00 2001 From: Glenn L McGrath Date: Wed, 2 Jan 2002 13:52:26 +0000 Subject: unzip applet by Laurence Anderson ---------------------------------------------------------------------- --- archival/Makefile | 1 + archival/config.in | 1 + archival/libunarchive/Makefile | 8 ++- archival/libunarchive/decompress_unzip.c | 48 +++++++------- archival/libunarchive/get_header_zip.c | 110 +++++++++++++++++++++++++++++++ archival/libunarchive/unarchive.c | 3 +- archival/libunarchive/unzip.c | 48 +++++++------- archival/unzip.c | 94 ++++++++++++++++++++++++++ 8 files changed, 260 insertions(+), 53 deletions(-) create mode 100644 archival/libunarchive/get_header_zip.c create mode 100644 archival/unzip.c (limited to 'archival') diff --git a/archival/Makefile b/archival/Makefile index 8787589ff..35ba02f3b 100644 --- a/archival/Makefile +++ b/archival/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_GUNZIP) += gunzip.o obj-$(CONFIG_GZIP) += gzip.o obj-$(CONFIG_RPM2CPIO) += rpm2cpio.o obj-$(CONFIG_TAR) += tar.o +obj-$(CONFIG_UNZIP) += unzip.o # Hand off to toplevel Rules.mak diff --git a/archival/config.in b/archival/config.in index 76a192e13..7b5644f01 100644 --- a/archival/config.in +++ b/archival/config.in @@ -20,4 +20,5 @@ if [ "$CONFIG_TAR" = "y" ] ; then bool ' Enable -X and --exclude options (exclude files)' CONFIG_FEATURE_TAR_EXCLUDE bool ' Enable -z option (currently only for extracting)' CONFIG_FEATURE_TAR_GZIP fi +bool 'unzip' CONFIG_UNZIP endmenu diff --git a/archival/libunarchive/Makefile b/archival/libunarchive/Makefile index 0c7219dcd..a8409a432 100644 --- a/archival/libunarchive/Makefile +++ b/archival/libunarchive/Makefile @@ -20,7 +20,7 @@ TOPDIR :=../.. L_TARGET := libunarchive.a -obj-y := unarchive.o seek_sub_file.o +obj-y := unarchive.o seek_sub_file.o obj-n := obj- := @@ -41,13 +41,17 @@ ifeq ($(CONFIG_CPIO),y) endif ifeq ($(CONFIG_RPM2CPIO),y) - obj-y += get_header_cpio.o + obj-y += get_header_cpio.o endif ifeq ($(CONFIG_TAR),y) obj-y += get_header_tar.o endif +ifeq ($(CONFIG_UNZIP),y) + obj-y += get_header_zip.o +endif + # Hand off to toplevel Rules.mak include $(TOPDIR)/Rules.mak diff --git a/archival/libunarchive/decompress_unzip.c b/archival/libunarchive/decompress_unzip.c index 6c28d181d..8075fd717 100644 --- a/archival/libunarchive/decompress_unzip.c +++ b/archival/libunarchive/decompress_unzip.c @@ -80,7 +80,7 @@ static const int ERROR = 1; /* * window size--must be a power of two, and - * at least 32K for zip's deflate method + * at least 32K for zip's deflate method */ static const int WSIZE = 0x8000; @@ -846,7 +846,7 @@ static int inflate_block(int *e) * * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr */ -static int inflate(void) +extern int inflate(FILE *in, FILE *out) { int e; /* last block flag */ int r; /* result code */ @@ -857,6 +857,13 @@ static int inflate(void) bk = 0; bb = 0; + in_file = in; + out_file = out; + + /* Allocate all global buffers (for DYN_ALLOC option) */ + window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); + bytes_out = 0L; + /* Create the crc table */ make_crc_table(); @@ -881,13 +888,15 @@ static int inflate(void) /* flush out window */ flush_window(); + free(window); + free(crc_table); /* return success */ return 0; } /* =========================================================================== - * Unzip in to out. This routine works on both gzip and pkzip files. + * Unzip in to out. This routine works on gzip files only. * * IN assertions: the buffer inbuf contains already the beginning of * the compressed data, from offsets inptr to insize-1 included. @@ -901,9 +910,6 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) typedef void (*sig_type) (int); unsigned short i; - in_file = l_in_file; - out_file = l_out_file; - if (signal(SIGINT, SIG_IGN) != SIG_IGN) { (void) signal(SIGINT, (sig_type) abort_gzip); } @@ -918,53 +924,48 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) } #endif - /* Allocate all global buffers (for DYN_ALLOC option) */ - window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); - outcnt = 0; - bytes_out = 0L; - /* Magic header for gzip files, 1F 8B = \037\213 */ - if ((fgetc(in_file) != 0x1F) || (fgetc(in_file) != 0x8b)) { + if ((fgetc(l_in_file) != 0x1F) || (fgetc(l_in_file) != 0x8b)) { error_msg("Invalid gzip magic"); return EXIT_FAILURE; } /* Check the compression method */ - if (fgetc(in_file) != 8) { + if (fgetc(l_in_file) != 8) { error_msg("Unknown compression method"); return(-1); } - flags = (unsigned char) fgetc(in_file); + flags = (unsigned char) fgetc(l_in_file); /* Ignore time stamp(4), extra flags(1), OS type(1) */ for (i = 0; i < 6; i++) { - fgetc(in_file); + fgetc(l_in_file); } if (flags & 0x04) { /* bit 2 set: extra field present */ - const unsigned short extra = fgetc(in_file) + (fgetc(in_file) << 8); + const unsigned short extra = fgetc(l_in_file) + (fgetc(l_in_file) << 8); for (i = 0; i < extra; i++) { - fgetc(in_file); + fgetc(l_in_file); } } /* Discard original name if any */ if (flags & 0x08) { /* bit 3 set: original file name present */ - while (fgetc(in_file) != 0); /* null */ + while (fgetc(l_in_file) != 0); /* null */ } /* Discard file comment if any */ if (flags & 0x10) { /* bit 4 set: file comment present */ - while (fgetc(in_file) != 0); /* null */ + while (fgetc(l_in_file) != 0); /* null */ } /* Decompress */ - if (inflate() != 0) { + if (inflate(l_in_file, l_out_file) != 0) { error_msg("invalid compressed data--format violated"); } @@ -972,7 +973,7 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) * crc32 (see algorithm.doc) * uncompressed input size modulo 2^32 */ - fread(buf, 1, 8, in_file); + fread(buf, 1, 8, l_in_file); /* Validate decompression - crc */ if ((unsigned int)((buf[0] | (buf[1] << 8)) |((buf[2] | (buf[3] << 8)) << 16)) != (crc ^ 0xffffffffL)) { @@ -983,14 +984,11 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) error_msg("invalid compressed data--length error"); } - free(window); - free(crc_table); - return 0; } /* - * This needs access to global variables wondow and crc_table, so its not in its own file. + * This needs access to global variables window and crc_table, so its not in its own file. */ extern void gz_close(int gunzip_pid) { diff --git a/archival/libunarchive/get_header_zip.c b/archival/libunarchive/get_header_zip.c new file mode 100644 index 000000000..84f2a54f2 --- /dev/null +++ b/archival/libunarchive/get_header_zip.c @@ -0,0 +1,110 @@ +/* vi: set sw=4 ts=4: */ +/* + * get_header_zip for busybox + * + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "unarchive.h" +#include "libbb.h" + +#define ZIP_FILEHEADER_MAGIC 0x04034b50 +#define ZIP_CDS_MAGIC 0x02014b50 +#define ZIP_CDS_END_MAGIC 0x06054b50 +#define ZIP_DD_MAGIC 0x8074b50 + +file_header_t *get_header_zip(FILE *zip_stream) +{ + struct { + short version __attribute__ ((packed)); + short flags __attribute__ ((packed)); + short method __attribute__ ((packed)); + short modtime __attribute__ ((packed)); + short moddate __attribute__ ((packed)); + int crc32 __attribute__ ((packed)); + int cmpsize __attribute__ ((packed)); + int ucmpsize __attribute__ ((packed)); + short filename_len __attribute__ ((packed)); + short extra_len __attribute__ ((packed)); + } zip_header; + file_header_t *zip_entry = NULL; + int magic; + static int dd_ahead = 0; // If this is true, the we didn't know how long the last extraced file was + + fread (&magic, 4, 1, zip_stream); + archive_offset += 4; + + if (feof(zip_stream)) return(NULL); +checkmagic: + switch (magic) { + case ZIP_FILEHEADER_MAGIC: + zip_entry = xcalloc(1, sizeof(file_header_t)); + fread (&zip_header, sizeof(zip_header), 1, zip_stream); + archive_offset += sizeof(zip_header); + if (!(zip_header.method == 8 || zip_header.method == 0)) { printf("Unsupported compression method %d\n", zip_header.method); return(NULL); } + zip_entry->name = calloc(zip_header.filename_len + 1, sizeof(char)); + fread (zip_entry->name, sizeof(char), zip_header.filename_len, zip_stream); + archive_offset += zip_header.filename_len; + seek_sub_file(zip_stream, zip_header.extra_len); + zip_entry->size = zip_header.cmpsize; + if (zip_header.method == 8) zip_entry->extract_func = &inflate; + zip_entry->mode = S_IFREG | 0777; + // Time/Date? + if (*(zip_entry->name + strlen(zip_entry->name) - 1) == '/') { // Files that end in a / are directories + zip_entry->mode ^= S_IFREG; + zip_entry->mode |= S_IFDIR; + *(zip_entry->name + strlen(zip_entry->name) - 1) = '\0'; // Remove trailing / so unarchive doesn't get confused + } + //printf("cmpsize: %d, ucmpsize: %d, method: %d\n", zip_header.cmpsize, zip_header.ucmpsize, zip_header.method); + if (zip_header.flags & 0x8) { // crc32, and sizes are in the data description _after_ the file + if (zip_header.cmpsize == 0) dd_ahead = 1; // As we don't know how long this file it is difficult to skip! but it is compressed, so normally its ok + if (zip_header.ucmpsize != 0) dd_ahead = 2; // Humm... we would otherwise skip this twice - not good! + } + break; + case ZIP_CDS_MAGIC: /* FALLTHRU */ + case ZIP_CDS_END_MAGIC: + return(NULL); + break; + case ZIP_DD_MAGIC: { + int cmpsize; + seek_sub_file(zip_stream, 4); // Skip crc32 + fread(&cmpsize, 4, 1, zip_stream); + archive_offset += 4; + if (dd_ahead == 1) archive_offset += cmpsize; + seek_sub_file(zip_stream, 4); // Skip uncompressed size + dd_ahead = 0; + return (get_header_zip(zip_stream)); + break; } + default: + if (!dd_ahead) error_msg("Invalid magic (%#x): Trying to skip junk", magic); + dd_ahead = 0; + while (!feof(zip_stream)) { + int tmpmagic; + tmpmagic = fgetc(zip_stream); + archive_offset++; + magic = ((magic >> 8) & 0x00ffffff) | ((tmpmagic << 24) & 0xff000000); + if (magic == ZIP_FILEHEADER_MAGIC || magic == ZIP_CDS_MAGIC || magic == ZIP_CDS_END_MAGIC) goto checkmagic; + } + error_msg("End of archive reached: Bad archive"); + return(NULL); + } + //if (archive_offset != ftell(zip_stream)) printf("Archive offset out of sync (%d,%d)\n", (int) archive_offset, (int) ftell(zip_stream)); + return(zip_entry); +} diff --git a/archival/libunarchive/unarchive.c b/archival/libunarchive/unarchive.c index ff9b5876f..41be963ef 100644 --- a/archival/libunarchive/unarchive.c +++ b/archival/libunarchive/unarchive.c @@ -120,7 +120,8 @@ char *extract_archive(FILE *src_stream, FILE *out_stream, const file_header_t *f return NULL; } archive_offset += file_entry->size; - copy_file_chunk(src_stream, dst_stream, file_entry->size); + if (file_entry->extract_func) file_entry->extract_func(src_stream, dst_stream); + else copy_file_chunk(src_stream, dst_stream, file_entry->size); fclose(dst_stream); } break; diff --git a/archival/libunarchive/unzip.c b/archival/libunarchive/unzip.c index 6c28d181d..8075fd717 100644 --- a/archival/libunarchive/unzip.c +++ b/archival/libunarchive/unzip.c @@ -80,7 +80,7 @@ static const int ERROR = 1; /* * window size--must be a power of two, and - * at least 32K for zip's deflate method + * at least 32K for zip's deflate method */ static const int WSIZE = 0x8000; @@ -846,7 +846,7 @@ static int inflate_block(int *e) * * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr */ -static int inflate(void) +extern int inflate(FILE *in, FILE *out) { int e; /* last block flag */ int r; /* result code */ @@ -857,6 +857,13 @@ static int inflate(void) bk = 0; bb = 0; + in_file = in; + out_file = out; + + /* Allocate all global buffers (for DYN_ALLOC option) */ + window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); + bytes_out = 0L; + /* Create the crc table */ make_crc_table(); @@ -881,13 +888,15 @@ static int inflate(void) /* flush out window */ flush_window(); + free(window); + free(crc_table); /* return success */ return 0; } /* =========================================================================== - * Unzip in to out. This routine works on both gzip and pkzip files. + * Unzip in to out. This routine works on gzip files only. * * IN assertions: the buffer inbuf contains already the beginning of * the compressed data, from offsets inptr to insize-1 included. @@ -901,9 +910,6 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) typedef void (*sig_type) (int); unsigned short i; - in_file = l_in_file; - out_file = l_out_file; - if (signal(SIGINT, SIG_IGN) != SIG_IGN) { (void) signal(SIGINT, (sig_type) abort_gzip); } @@ -918,53 +924,48 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) } #endif - /* Allocate all global buffers (for DYN_ALLOC option) */ - window = xmalloc((size_t)(((2L*WSIZE)+1L)*sizeof(unsigned char))); - outcnt = 0; - bytes_out = 0L; - /* Magic header for gzip files, 1F 8B = \037\213 */ - if ((fgetc(in_file) != 0x1F) || (fgetc(in_file) != 0x8b)) { + if ((fgetc(l_in_file) != 0x1F) || (fgetc(l_in_file) != 0x8b)) { error_msg("Invalid gzip magic"); return EXIT_FAILURE; } /* Check the compression method */ - if (fgetc(in_file) != 8) { + if (fgetc(l_in_file) != 8) { error_msg("Unknown compression method"); return(-1); } - flags = (unsigned char) fgetc(in_file); + flags = (unsigned char) fgetc(l_in_file); /* Ignore time stamp(4), extra flags(1), OS type(1) */ for (i = 0; i < 6; i++) { - fgetc(in_file); + fgetc(l_in_file); } if (flags & 0x04) { /* bit 2 set: extra field present */ - const unsigned short extra = fgetc(in_file) + (fgetc(in_file) << 8); + const unsigned short extra = fgetc(l_in_file) + (fgetc(l_in_file) << 8); for (i = 0; i < extra; i++) { - fgetc(in_file); + fgetc(l_in_file); } } /* Discard original name if any */ if (flags & 0x08) { /* bit 3 set: original file name present */ - while (fgetc(in_file) != 0); /* null */ + while (fgetc(l_in_file) != 0); /* null */ } /* Discard file comment if any */ if (flags & 0x10) { /* bit 4 set: file comment present */ - while (fgetc(in_file) != 0); /* null */ + while (fgetc(l_in_file) != 0); /* null */ } /* Decompress */ - if (inflate() != 0) { + if (inflate(l_in_file, l_out_file) != 0) { error_msg("invalid compressed data--format violated"); } @@ -972,7 +973,7 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) * crc32 (see algorithm.doc) * uncompressed input size modulo 2^32 */ - fread(buf, 1, 8, in_file); + fread(buf, 1, 8, l_in_file); /* Validate decompression - crc */ if ((unsigned int)((buf[0] | (buf[1] << 8)) |((buf[2] | (buf[3] << 8)) << 16)) != (crc ^ 0xffffffffL)) { @@ -983,14 +984,11 @@ extern int unzip(FILE *l_in_file, FILE *l_out_file) error_msg("invalid compressed data--length error"); } - free(window); - free(crc_table); - return 0; } /* - * This needs access to global variables wondow and crc_table, so its not in its own file. + * This needs access to global variables window and crc_table, so its not in its own file. */ extern void gz_close(int gunzip_pid) { diff --git a/archival/unzip.c b/archival/unzip.c new file mode 100644 index 000000000..ae0d7c1dc --- /dev/null +++ b/archival/unzip.c @@ -0,0 +1,94 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini unzip implementation for busybox + * + * Copyright (C) 2001 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 + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include "unarchive.h" +#include "busybox.h" + +extern int unzip_main(int argc, char **argv) +{ + FILE *src_stream; + int extract_function = extract_all_to_fs | extract_create_leading_dirs; + char **extract_names = NULL; + char **exclude_names = NULL; + int opt = 0; + int num_of_entries = 0; + int exclude = 0; + char *outdir = "./"; + FILE *msgout = stdout; + + while ((opt = getopt(argc, argv, "lnopqxd:")) != -1) { + switch (opt) { + case 'l': + extract_function |= extract_verbose_list; + extract_function ^= extract_all_to_fs; + break; + case 'n': + break; + case 'o': + extract_function |= extract_unconditional; + break; + case 'p': + extract_function |= extract_to_stdout; + extract_function ^= extract_all_to_fs; + /* FALLTHROUGH */ + case 'q': + msgout = xfopen("/dev/null", "w"); + break; + case 'd': + outdir = xstrdup(optarg); + strcat(outdir, "/"); + break; + case 'x': + exclude = 1; + break; + } + } + + if (optind == argc) { + show_usage(); + } + + if (*argv[optind] == '-') src_stream = stdin; + else src_stream = xfopen(argv[optind++], "r"); + + while (optind < argc) { + if (exclude) { + exclude_names = xrealloc(exclude_names, sizeof(char *) * (num_of_entries + 2)); + exclude_names[num_of_entries] = xstrdup(argv[optind]); + } else { + extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); + extract_names[num_of_entries] = xstrdup(argv[optind]); + } + num_of_entries++; + if (exclude) exclude_names[num_of_entries] = NULL; + else extract_names[num_of_entries] = NULL; + optind++; + } + + unarchive(src_stream, msgout, &get_header_zip, extract_function, outdir, extract_names, exclude_names); + return EXIT_SUCCESS; +} -- cgit v1.2.3