diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2002-09-25 02:47:48 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2002-09-25 02:47:48 +0000 |
commit | 7ca04f328e22fcbee4659d73f9a72dfdf1dd6a23 (patch) | |
tree | f38c7ef4317eea28c6abdb0adbbb286fe041711e /archival | |
parent | ecfa290cfd4953598e6d91989bd66ac16e135f84 (diff) | |
download | busybox-7ca04f328e22fcbee4659d73f9a72dfdf1dd6a23.tar.gz |
New common unarchive code.
Diffstat (limited to 'archival')
31 files changed, 1969 insertions, 1386 deletions
diff --git a/archival/ar.c b/archival/ar.c index ac8c41f96..b5fa7956e 100644 --- a/archival/ar.c +++ b/archival/ar.c @@ -21,41 +21,100 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * There is no signle standard to adhere to so ar may not portable + * between different systems + * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html */ +#include <sys/types.h> #include <fcntl.h> +#include <fnmatch.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> +#include <utime.h> +#include <unistd.h> #include <getopt.h> #include "unarchive.h" #include "busybox.h" +static void header_verbose_list_ar(const file_header_t *file_header) +{ + const char *mode = mode_string(file_header->mode); + char *mtime; + + mtime = ctime(&file_header->mtime); + mtime[16] = ' '; + memmove(&mtime[17], &mtime[20], 4); + mtime[21] = '\0'; + printf("%s %d/%d%7d %s %s\n", &mode[1], file_header->uid, file_header->gid, (int) file_header->size, &mtime[4], file_header->name); +} + +#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO +/* This is simpler than data_extract_all */ +static void data_extract_regular_file(archive_handle_t *archive_handle) +{ + file_header_t *file_header; + int dst_fd; + + file_header = archive_handle->file_header; + dst_fd = xopen(file_header->name, O_WRONLY | O_CREAT); + copy_file_chunk_fd(archive_handle->src_fd, dst_fd, file_header->size); + close(dst_fd); + + chmod(file_header->name, file_header->mode); + chown(file_header->name, file_header->uid, file_header->gid); + + if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) { + struct utimbuf t; + t.actime = t.modtime = file_header->mtime; + utime(file_header->name, &t); + } + + return; +} +#endif + extern int ar_main(int argc, char **argv) { - FILE *src_stream = NULL; - char **extract_names = NULL; - char ar_magic[8]; - int extract_function = extract_unconditional; + archive_handle_t *archive_handle; int opt; - int num_of_entries = 0; - extern off_t archive_offset; - while ((opt = getopt(argc, argv, "ovtpx")) != -1) { +#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO + archive_handle = init_handle(); +#else + char magic[8]; + + archive_handle = xcalloc(1, sizeof(archive_handle_t)); + archive_handle->filter = filter_accept_all; + archive_handle->action_data = data_skip; + archive_handle->action_header = header_skip; + archive_handle->file_header =xmalloc(sizeof(file_header_t)); +#endif + + while ((opt = getopt(argc, argv, "covtpxX")) != -1) { switch (opt) { - case 'o': - extract_function |= extract_preserve_date; + case 'p': /* print */ + archive_handle->action_data = data_extract_to_stdout; break; - case 'v': - extract_function |= extract_verbose_list; + case 't': /* list contents */ + archive_handle->action_header = header_list; break; - case 't': - extract_function |= extract_list; + case 'X': + archive_handle->action_header = header_verbose_list_ar; + case 'x': /* extract */ +#if defined CONFIG_TAR | defined CONFIG_DPKG_DEB | defined CONFIG_CPIO + archive_handle->action_data = data_extract_all; +#else + archive_handle->action_data = data_extract_regular_file; +#endif break; - case 'p': - extract_function |= extract_to_stdout; + /* Modifiers */ + case 'o': /* preserve original dates */ + archive_handle->flags |= ARCHIVE_PRESERVE_DATE; break; - case 'x': - extract_function |= extract_all_to_fs; + case 'v': /* verbose */ + archive_handle->action_header = header_verbose_list_ar; break; default: show_usage(); @@ -67,24 +126,26 @@ extern int ar_main(int argc, char **argv) show_usage(); } - src_stream = xfopen(argv[optind++], "r"); + archive_handle->src_fd = xopen(argv[optind++], O_RDONLY); - /* check ar magic */ - fread(ar_magic, 1, 8, src_stream); - archive_offset = 8; - if (strncmp(ar_magic,"!<arch>",7) != 0) { - error_msg_and_die("invalid magic"); - } - - /* Create a list of files to extract */ + /* TODO: This is the same as in tar, seperate function ? */ while (optind < argc) { - extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); - extract_names[num_of_entries] = xstrdup(argv[optind]); - num_of_entries++; - extract_names[num_of_entries] = NULL; + archive_handle->filter = filter_accept_list; + archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]); optind++; } - unarchive(src_stream, stdout, &get_header_ar, extract_function, "./", extract_names, NULL); +#if defined CONFIG_DPKG_DEB + unpack_ar_archive(archive_handle); +#else + xread_all(archive_handle->src_fd, magic, 7); + if (strncmp(magic, "!<arch>", 7) != 0) { + error_msg_and_die("Invalid ar magic"); + } + archive_handle->offset += 7; + + while (get_header_ar(archive_handle) == EXIT_SUCCESS); +#endif + return EXIT_SUCCESS; } diff --git a/archival/config.in b/archival/config.in index 3de671514..dd97909f9 100644 --- a/archival/config.in +++ b/archival/config.in @@ -7,10 +7,16 @@ mainmenu_option next_comment comment 'Archival Utilities' bool 'ar' CONFIG_AR +if [ "$CONFIG_AR" = "y" ] ; then + bool ' Enable support for long filenames (not need for debs)' CONFIG_FEATURE_AR_LONG_FILENAMES +fi bool 'bunzip2' CONFIG_BUNZIP2 bool 'cpio' CONFIG_CPIO bool 'dpkg' CONFIG_DPKG bool 'dpkg_deb' CONFIG_DPKG_DEB +if [ "$CONFIG_DPKG_DEB" = "y" ] ; then + bool ' -x support only' CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY +fi bool 'gunzip' CONFIG_GUNZIP bool 'gzip' CONFIG_GZIP bool 'rpm2cpio' CONFIG_RPM2CPIO diff --git a/archival/cpio.c b/archival/cpio.c index baaa72ea1..761517516 100644 --- a/archival/cpio.c +++ b/archival/cpio.c @@ -31,66 +31,210 @@ #include "unarchive.h" #include "busybox.h" +typedef struct hardlinks_s { + file_header_t *entry; + int inode; + struct hardlinks_s *next; +} hardlinks_t; + extern int cpio_main(int argc, char **argv) { - FILE *src_stream = stdin; - char **extract_names = NULL; - int extract_function = 0; - int num_of_entries = 0; - int opt = 0; - mode_t oldmask = 0; + archive_handle_t *archive_handle; + int opt; + + /* Initialise */ + archive_handle = init_handle(); + archive_handle->src_fd = fileno(stdin); + archive_handle->action_header = header_list; while ((opt = getopt(argc, argv, "idmuvtF:")) != -1) { switch (opt) { - case 'i': // extract - extract_function |= extract_all_to_fs; + case 'i': /* extract */ + archive_handle->action_data = data_extract_all; break; - case 'd': // create _leading_ directories - extract_function |= extract_create_leading_dirs; - oldmask = umask(077); /* Make make_directory act like GNU cpio */ + case 'd': /* create _leading_ directories */ + archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS; break; - case 'm': // preserve modification time - extract_function |= extract_preserve_date; + case 'm': /* preserve modification time */ + archive_handle->flags |= ARCHIVE_PRESERVE_DATE; break; - case 'v': // verbosly list files - extract_function |= extract_verbose_list; + case 'v': /* verbosly list files */ + archive_handle->action_header = header_verbose_list; break; - case 'u': // unconditional - extract_function |= extract_unconditional; + case 'u': /* unconditional */ + archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL; break; - case 't': // list files - extract_function |= extract_list; + case 't': /* list files */ + archive_handle->action_header = header_list; break; case 'F': - src_stream = xfopen(optarg, "r"); + archive_handle->src_fd = xopen(optarg, O_RDONLY); break; default: show_usage(); } } - if ((extract_function & extract_all_to_fs) && (extract_function & extract_list)) { - extract_function ^= extract_all_to_fs; /* If specify t, don't extract*/ + while (optind < argc) { + archive_handle->filter = filter_accept_list; + archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]); + optind++; } - if ((extract_function & extract_all_to_fs) && (extract_function & extract_verbose_list)) { - /* The meaning of v changes on extract */ - extract_function ^= extract_verbose_list; - extract_function |= extract_list; + while (1) { + static hardlinks_t *saved_hardlinks = NULL; + static unsigned short pending_hardlinks = 0; + file_header_t *file_header = archive_handle->file_header; + char cpio_header[110]; + int namesize; + char dummy[16]; + int major, minor, nlink, inode; + char extract_flag; + + if (pending_hardlinks) { /* Deal with any pending hardlinks */ + hardlinks_t *tmp; + hardlinks_t *oldtmp; + + tmp = saved_hardlinks; + oldtmp = NULL; + + while (tmp) { + error_msg_and_die("need to fix this\n"); + if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */ + file_header = tmp->entry; + if (oldtmp) { + oldtmp->next = tmp->next; /* Remove item from linked list */ + } else { + saved_hardlinks = tmp->next; + } + free(tmp); + continue; + } + oldtmp = tmp; + tmp = tmp->next; + } + pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */ } - while (optind < argc) { - extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); - extract_names[num_of_entries] = xstrdup(argv[optind]); - num_of_entries++; - extract_names[num_of_entries] = NULL; - optind++; + /* There can be padding before archive header */ + archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 4); + + if (xread_all_eof(archive_handle->src_fd, cpio_header, 110) == 0) { + return(EXIT_FAILURE); + } + archive_handle->offset += 110; + + if (strncmp(&cpio_header[0], "07070", 5) != 0) { + printf("cpio header is %x-%x-%x-%x-%x\n", + cpio_header[0], + cpio_header[1], + cpio_header[2], + cpio_header[3], + cpio_header[4]); + error_msg_and_die("Unsupported cpio format"); + } + + if ((cpio_header[5] != '1') && (cpio_header[5] != '2')) { + error_msg_and_die("Unsupported cpio format, use newc or crc"); + } + + sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c", + dummy, &inode, (unsigned int*)&file_header->mode, + (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid, + &nlink, &file_header->mtime, &file_header->size, + dummy, &major, &minor, &namesize, dummy); + + file_header->name = (char *) xmalloc(namesize + 1); + xread(archive_handle->src_fd, file_header->name, namesize); /* Read in filename */ + file_header->name[namesize] = '\0'; + archive_handle->offset += namesize; + + /* Update offset amount and skip padding before file contents */ + archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 4); + + if (strcmp(file_header->name, "TRAILER!!!") == 0) { + printf("%d blocks\n", (int) (archive_handle->offset % 512 ? (archive_handle->offset / 512) + 1 : archive_handle->offset / 512)); /* Always round up */ + if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */ + hardlinks_t *tmp = saved_hardlinks; + hardlinks_t *oldtmp = NULL; + while (tmp) { + error_msg("%s not created: cannot resolve hardlink", tmp->entry->name); + oldtmp = tmp; + tmp = tmp->next; + free (oldtmp->entry->name); + free (oldtmp->entry); + free (oldtmp); + } + saved_hardlinks = NULL; + pending_hardlinks = 0; } + return(EXIT_FAILURE); + } + + if (S_ISLNK(file_header->mode)) { + file_header->link_name = (char *) xmalloc(file_header->size + 1); + xread(archive_handle->src_fd, file_header->link_name, file_header->size); + file_header->link_name[file_header->size] = '\0'; + archive_handle->offset += file_header->size; + file_header->size = 0; /* Stop possible seeks in future */ + } + if (nlink > 1 && !S_ISDIR(file_header->mode)) { + if (file_header->size == 0) { /* Put file on a linked list for later */ + hardlinks_t *new = xmalloc(sizeof(hardlinks_t)); + new->next = saved_hardlinks; + new->inode = inode; + new->entry = file_header; + saved_hardlinks = new; + continue; + } else { /* Found the file with data in */ + hardlinks_t *tmp = saved_hardlinks; + pending_hardlinks = 1; + while (tmp) { + if (tmp->inode == inode) { + tmp->entry->link_name = xstrdup(file_header->name); + nlink--; + } + tmp = tmp->next; + } + if (nlink > 1) { + error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?"); + } + } + } + file_header->device = (major << 8) | minor; + + extract_flag = FALSE; + if (archive_handle->filter(archive_handle->accept, archive_handle->reject, file_header->name) == EXIT_SUCCESS) { + struct stat statbuf; - unarchive(src_stream, stdout, &get_header_cpio, extract_function, "./", extract_names, NULL); - if (oldmask) { - umask(oldmask); /* Restore umask if we changed it */ + extract_flag = TRUE; + + /* Check if the file already exists */ + if (lstat (file_header->name, &statbuf) == 0) { + if ((archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) || (statbuf.st_mtime < file_header->mtime)) { + /* Remove file if flag set or its older than the file to be extracted */ + if (unlink(file_header->name) == -1) { + perror_msg_and_die("Couldnt remove old file"); + } + } else { + if (! archive_handle->flags & ARCHIVE_EXTRACT_QUIET) { + error_msg("%s not created: newer or same age file exists", file_header->name); + } + extract_flag = FALSE; + } + } + archive_handle->action_header(file_header); + } + + archive_handle->action_header(file_header); + if (extract_flag) { + archive_handle->action_data(archive_handle); + } else { + data_skip(archive_handle); + } + archive_handle->offset += file_header->size; } - return EXIT_SUCCESS; + + return(EXIT_SUCCESS); } diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c index 29692f7d7..06a810cc1 100644 --- a/archival/dpkg_deb.c +++ b/archival/dpkg_deb.c @@ -13,119 +13,100 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - +#include <fcntl.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <getopt.h> #include "unarchive.h" #include "busybox.h" extern int dpkg_deb_main(int argc, char **argv) { - char *prefix = NULL; - char *filename = NULL; - char *output_buffer = NULL; + archive_handle_t *ar_archive; + archive_handle_t *tar_gz_archive; int opt = 0; - int arg_type = 0; - int deb_extract_funct = extract_create_leading_dirs | extract_unconditional; +#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY + const llist_t *control_tar_gz_llist = add_to_list(NULL, "control.tar.gz"); +#endif +#ifndef CONFIG_AR + char magic[7]; +#endif + + /* a .deb file is an ar archive that contain three files, + * data.tar.gz, control.tar.gz and debian + */ - const int arg_type_prefix = 1; - const int arg_type_field = 2; - const int arg_type_filename = 4; -// const int arg_type_un_ar_gz = 8; + /* Setup the tar archive handle */ + tar_gz_archive = init_handle(); - while ((opt = getopt(argc, argv, "ceftXxI")) != -1) { + /* Setup an ar archive handle that refers to the gzip sub archive */ + ar_archive = init_handle(); + ar_archive->action_data_subarchive = get_header_tar_gz; + ar_archive->sub_archive = tar_gz_archive; + ar_archive->filter = filter_accept_list; + ar_archive->accept = add_to_list(NULL, "data.tar.gz"); + +#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY + while ((opt = getopt(argc, argv, "cefXx")) != -1) { +#else + while ((opt = getopt(argc, argv, "x")) != -1) { +#endif switch (opt) { +#ifndef CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY case 'c': - deb_extract_funct |= extract_data_tar_gz; - deb_extract_funct |= extract_verbose_list; + tar_gz_archive->action_header = header_verbose_list; break; case 'e': - arg_type = arg_type_prefix; - deb_extract_funct |= extract_control_tar_gz; - deb_extract_funct |= extract_all_to_fs; + ar_archive->accept = control_tar_gz_llist; + tar_gz_archive->action_data = data_extract_all; break; case 'f': - arg_type = arg_type_field; - deb_extract_funct |= extract_control_tar_gz; - deb_extract_funct |= extract_one_to_buffer; - filename = xstrdup("./control"); - break; - case 't': /* --fsys-tarfile, i just made up this short name */ - /* Integrate the functionality needed with some code from ar.c */ - error_msg_and_die("Option disabled"); -// arg_type = arg_type_un_ar_gz; + /* Print the entire control file + * it should accept a second argument which specifies a + * specific field to print */ + ar_archive->accept = control_tar_gz_llist; + tar_gz_archive->accept = add_to_list(NULL, "./control");; + tar_gz_archive->filter = filter_accept_list; + tar_gz_archive->action_data = data_extract_to_stdout; break; case 'X': - arg_type = arg_type_prefix; - deb_extract_funct |= extract_data_tar_gz; - deb_extract_funct |= extract_all_to_fs; - deb_extract_funct |= extract_list; + tar_gz_archive->action_header = header_list; +#endif case 'x': - arg_type = arg_type_prefix; - deb_extract_funct |= extract_data_tar_gz; - deb_extract_funct |= extract_all_to_fs; - break; - case 'I': - arg_type = arg_type_filename; - deb_extract_funct |= extract_control_tar_gz; - deb_extract_funct |= extract_one_to_buffer; + tar_gz_archive->action_data = data_extract_all; break; default: show_usage(); } } - if (optind == argc) { + if (optind + 2 < argc) { show_usage(); } + tar_gz_archive->src_fd = ar_archive->src_fd = xopen(argv[optind++], O_RDONLY); + /* Workout where to extract the files */ - if (arg_type == arg_type_prefix) { - /* argument is a dir name */ - if ((optind + 1) == argc ) { - prefix = xstrdup("./DEBIAN/"); - } else { - prefix = (char *) xmalloc(strlen(argv[optind + 1]) + 2); - strcpy(prefix, argv[optind + 1]); - /* Make sure the directory has a trailing '/' */ - if (last_char_is(prefix, '/') == NULL) { - strcat(prefix, "/"); - } - } - mkdir(prefix, 0777); - } + /* 2nd argument is a dir name */ + mkdir(argv[optind], 0777); + chdir(argv[optind]); - if (arg_type == arg_type_filename) { - if ((optind + 1) != argc) { - filename = xstrdup(argv[optind + 1]); - } else { - error_msg_and_die("-I currently requires a filename to be specified"); - } +#ifdef CONFIG_AR + unpack_ar_archive(ar_archive); +#else + xread_all(ar_archive->src_fd, magic, 7); + if (strncmp(magic, "!<arch>", 7) != 0) { + error_msg_and_die("Invalid ar magic"); } + ar_archive->offset += 7; - output_buffer = deb_extract(argv[optind], stdout, deb_extract_funct, prefix, filename); + while (get_header_ar(ar_archive) == EXIT_SUCCESS); +#endif - if ((arg_type == arg_type_filename) && (output_buffer != NULL)) { - puts(output_buffer); - } - else if (arg_type == arg_type_field) { - char *field = NULL; - char *name; - char *value; - int field_start = 0; - - while (1) { - field_start += read_package_field(&output_buffer[field_start], &name, &value); - if (name == NULL) { - break; - } - if (strcmp(name, argv[optind + 1]) == 0) { - puts(value); - } - free(field); - } - } + /* Cleanup */ + close (ar_archive->src_fd); return(EXIT_SUCCESS); } + diff --git a/archival/gunzip.c b/archival/gunzip.c index ac2fb17ed..4489204fb 100644 --- a/archival/gunzip.c +++ b/archival/gunzip.c @@ -64,7 +64,12 @@ static char *license_msg[] = { #include <string.h> #include <unistd.h> #include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + #include "busybox.h" +#include "unarchive.h" const char gunzip_to_stdout = 1; const char gunzip_force = 2; @@ -100,23 +105,20 @@ extern int gunzip_main(int argc, char **argv) } do { - FILE *in_file, *out_file; struct stat stat_buf; const char *old_path = argv[optind]; const char *delete_path = NULL; char *new_path = NULL; + int src_fd; + int dst_fd; optind++; if (old_path == NULL || strcmp(old_path, "-") == 0) { - in_file = stdin; + src_fd = fileno(stdin); flags |= gunzip_to_stdout; } else { - in_file = wfopen(old_path, "r"); - if (in_file == NULL) { - status = EXIT_FAILURE; - break; - } + src_fd = xopen(old_path, O_RDONLY); /* Get the time stamp on the input file. */ if (stat(old_path, &stat_buf) < 0) { @@ -125,16 +127,16 @@ extern int gunzip_main(int argc, char **argv) } /* Check that the input is sane. */ - if (isatty(fileno(in_file)) && ((flags & gunzip_force) == 0)) { + if (isatty(src_fd) && ((flags & gunzip_force) == 0)) { error_msg_and_die ("compressed data not read from terminal. Use -f to force it."); } /* Set output filename and number */ if (flags & gunzip_test) { - out_file = xfopen("/dev/null", "w"); /* why does test use filenum 2 ? */ + dst_fd = xopen("/dev/null", O_WRONLY); /* why does test use filenum 2 ? */ } else if (flags & gunzip_to_stdout) { - out_file = stdout; + dst_fd = fileno(stdout); } else { char *extension; @@ -151,7 +153,7 @@ extern int gunzip_main(int argc, char **argv) } /* Open output file */ - out_file = xfopen(new_path, "w"); + dst_fd = xopen(new_path, O_WRONLY | O_CREAT); /* Set permissions on the file */ chmod(new_path, stat_buf.st_mode); @@ -161,16 +163,22 @@ extern int gunzip_main(int argc, char **argv) } /* do the decompression, and cleanup */ - if ((unzip(in_file, out_file) != 0) && (new_path)) { + check_header_gzip(src_fd); + if (inflate(src_fd, dst_fd) != 0) { + error_msg("Error inflating"); + } + check_trailer_gzip(src_fd); + + if ((status != EXIT_SUCCESS) && (new_path)) { /* Unzip failed, remove new path instead of old path */ delete_path = new_path; } - if (out_file != stdout) { - fclose(out_file); + if (dst_fd != fileno(stdout)) { + close(dst_fd); } - if (in_file != stdin) { - fclose(in_file); + if (src_fd != fileno(stdin)) { + close(src_fd); } /* delete_path will be NULL if in test mode or from stdin */ diff --git a/archival/libunarchive/Makefile.in b/archival/libunarchive/Makefile.in index cd68be732..e2ac546ab 100644 --- a/archival/libunarchive/Makefile.in +++ b/archival/libunarchive/Makefile.in @@ -22,14 +22,41 @@ ifndef $(LIBUNARCHIVE_DIR) LIBUNARCHIVE_DIR:=$(TOPDIR)archival/libunarchive/ endif -LIBUNARCHIVE-y:=unarchive.o seek_sub_file.o -LIBUNARCHIVE-$(CONFIG_DPKG) += deb_extract.o get_header_ar.o get_header_tar.o -LIBUNARCHIVE-$(CONFIG_DPKG_DEB) += deb_extract.o get_header_ar.o get_header_tar.o -LIBUNARCHIVE-$(CONFIG_AR) += get_header_ar.o -LIBUNARCHIVE-$(CONFIG_CPIO) += get_header_cpio.o -LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += get_header_cpio.o -LIBUNARCHIVE-$(CONFIG_TAR) += get_header_tar.o -LIBUNARCHIVE-$(CONFIG_UNZIP) += get_header_zip.o +LIBUNARCHIVE-y:= \ +\ + data_skip.o \ + data_extract_all.o \ + data_extract_to_stdout.o \ +\ + filter_accept_all.o \ + filter_accept_list.o \ + filter_accept_reject_list.o \ +\ + get_header_ar.o \ + get_header_tar.o \ + get_header_tar_gz.o \ +\ + header_skip.o \ + header_list.o \ + header_verbose_list.o \ +\ + add_to_list.o \ + check_header_gzip.o \ + check_trailer_gzip.o \ + copy_file_chunk_fd.o \ + data_align.o \ + init_handle.o \ + seek_sub_file.o \ + unpack_ar_archive.o \ + +LIBUNARCHIVE-$(CONFIG_DPKG) += +LIBUNARCHIVE-$(CONFIG_DPKG_DEB) += +LIBUNARCHIVE-$(CONFIG_AR) += +LIBUNARCHIVE-$(CONFIG_CPIO) += +LIBUNARCHIVE-$(CONFIG_GUNZIP) += +LIBUNARCHIVE-$(CONFIG_RPM2CPIO) += +LIBUNARCHIVE-$(CONFIG_TAR) += +LIBUNARCHIVE-$(CONFIG_UNZIP) += libraries-y+=$(LIBUNARCHIVE_DIR)$(LIBUNARCHIVE_AR) diff --git a/archival/libunarchive/add_to_list.c b/archival/libunarchive/add_to_list.c new file mode 100644 index 000000000..052bca351 --- /dev/null +++ b/archival/libunarchive/add_to_list.c @@ -0,0 +1,15 @@ +#include <stdlib.h> +#include <string.h> +#include "unarchive.h" +#include "libbb.h" + +extern const llist_t *add_to_list(const llist_t *old_head, const char *new_item) +{ + llist_t *new_head; + + new_head = xmalloc(sizeof(llist_t)); + new_head->data = new_item; + new_head->link = old_head; + + return(new_head); +} diff --git a/archival/libunarchive/check_header_gzip.c b/archival/libunarchive/check_header_gzip.c new file mode 100644 index 000000000..508d30924 --- /dev/null +++ b/archival/libunarchive/check_header_gzip.c @@ -0,0 +1,75 @@ +#include <stdlib.h> +#include <unistd.h> +#include "libbb.h" + +extern void check_header_gzip(int src_fd) +{ + union { + unsigned char raw[10]; + struct { + unsigned char magic[2]; + unsigned char method; + unsigned char flags; + unsigned int mtime; + unsigned char xtra_flags; + unsigned char os_flags; + } formated; + } header; + + xread_all(src_fd, header.raw, 10); + + /* Magic header for gzip files, 1F 8B = \037\213 */ + if ((header.formated.magic[0] != 0x1F) + || (header.formated.magic[1] != 0x8b)) { + error_msg_and_die("Invalid gzip magic"); + } + + /* Check the compression method */ + if (header.formated.method != 8) { + error_msg_and_die("Unknown compression method %d", + header.formated.method); + } + + if (header.formated.flags & 0x04) { + /* bit 2 set: extra field present */ + unsigned char extra_short; + + extra_short = xread_char(src_fd); + extra_short += xread_char(src_fd) << 8; + while (extra_short > 0) { + /* Ignore extra field */ + xread_char(src_fd); + extra_short--; + } + } + + /* Discard original name if any */ + if (header.formated.flags & 0x08) { + /* bit 3 set: original file name present */ + char tmp; + + do { + read(src_fd, &tmp, 1); + } while (tmp != 0); + } + + /* Discard file comment if any */ + if (header.formated.flags & 0x10) { + /* bit 4 set: file comment present */ + char tmp; + + do { + read(src_fd, &tmp, 1); + } while (tmp != 0); + } + + /* Read the header checksum */ + if (header.formated.flags & 0x02) { + char tmp; + + read(src_fd, &tmp, 1); + read(src_fd, &tmp, 1); + } + + return; +} diff --git a/archival/libunarchive/check_trailer_gzip.c b/archival/libunarchive/check_trailer_gzip.c new file mode 100644 index 000000000..215e8293f --- /dev/null +++ b/archival/libunarchive/check_trailer_gzip.c @@ -0,0 +1,62 @@ +/* vi: set sw=4 ts=4: */ +/* + * busybox gunzip trailing header handler + * Copyright Glenn McGrath 2002 + * + * 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 <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include "config.h" +#include "busybox.h" +#include "unarchive.h" + +extern unsigned int gunzip_crc; +extern unsigned int gunzip_bytes_out; +extern unsigned char *gunzip_in_buffer; +extern unsigned char gunzip_in_buffer_count; + +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 */ + xread_all(src_fd, &gunzip_in_buffer[gunzip_in_buffer_count], 8 - gunzip_in_buffer_count); + for (count = 0; count != 4; count++) { + stored_crc |= (gunzip_in_buffer[count] << (count * 8)); + } + + /* Validate decompression - crc */ + if (stored_crc != (gunzip_crc ^ 0xffffffffL)) { + error_msg("invalid compressed data--crc error"); + } + + /* Validate decompression - size */ + if (gunzip_bytes_out != + (gunzip_in_buffer[4] | (gunzip_in_buffer[5] << 8) | + (gunzip_in_buffer[6] << 16) | (gunzip_in_buffer[7] << 24))) { + error_msg("invalid compressed data--length error"); + } + +} diff --git a/archival/libunarchive/copy_file_chunk_fd.c b/archival/libunarchive/copy_file_chunk_fd.c new file mode 100644 index 000000000..fb513e6d5 --- /dev/null +++ b/archival/libunarchive/copy_file_chunk_fd.c @@ -0,0 +1,33 @@ +#include <unistd.h> +#include <sys/types.h> +#include "libbb.h" + +/* Copy CHUNKSIZE bytes (or untill EOF if chunksize == -1) + * from SRC_FILE to DST_FILE. */ +extern int copy_file_chunk_fd(int src_fd, int dst_fd, off_t chunksize) +{ + size_t nread, size; + char buffer[BUFSIZ]; + + while (chunksize != 0) { + if (chunksize > BUFSIZ) { + size = BUFSIZ; + } else { + size = chunksize; + } + nread = xread(src_fd, buffer, size); + if (nread == 0) { + return 1; + } + + if (write (dst_fd, buffer, nread) != nread) { + error_msg_and_die ("Short write"); + } + + if (chunksize != -1) { + chunksize -= nread; + } + } + + return 0; +} diff --git a/archival/libunarchive/data_align.c b/archival/libunarchive/data_align.c new file mode 100644 index 000000000..d6243bc19 --- /dev/null +++ b/archival/libunarchive/data_align.c @@ -0,0 +1,13 @@ +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include "unarchive.h" +#include "libbb.h" + +extern const unsigned short data_align(const int src_fd, const unsigned int offset, const unsigned short align_to) +{ + const unsigned short skip_amount = (align_to - (offset % align_to)) % align_to; + seek_sub_file(src_fd, skip_amount); + + return(skip_amount); +} diff --git a/archival/libunarchive/data_extract_all.c b/archival/libunarchive/data_extract_all.c new file mode 100644 index 000000000..20d99aa58 --- /dev/null +++ b/archival/libunarchive/data_extract_all.c @@ -0,0 +1,76 @@ +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <utime.h> +#include <unistd.h> +#include <stdlib.h> +#include "libbb.h" +#include "unarchive.h" + +extern void data_extract_all(archive_handle_t *archive_handle) +{ + file_header_t *file_header = archive_handle->file_header; + int dst_fd; + int res; + + if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) { + char *dir = dirname(strdup(file_header->name)); + make_directory (dir, 0777, FILEUTILS_RECUR); + free(dir); + } + + /* Create the file */ + switch(file_header->mode & S_IFMT) { + case S_IFREG: { +#ifdef CONFIG_CPIO + if (file_header->link_name) { + /* hard link */ + res = link(file_header->link_name, file_header->name); + if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + perror_msg("Couldnt create hard link"); + } + } else +#endif + { + /* Regular file */ + dst_fd = xopen(file_header->name, O_WRONLY | O_CREAT); + copy_file_chunk_fd(archive_handle->src_fd, dst_fd, file_header->size); + close(dst_fd); + } + break; + } + case S_IFDIR: + res = mkdir(file_header->name, file_header->mode); + if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + perror_msg("extract_archive: %s", file_header->name); + } + break; + case S_IFLNK: + /* Symlink */ + res = symlink(file_header->link_name, file_header->name); + if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + perror_msg("Cannot create symlink from %s to '%s'", file_header->name, file_header->link_name); + } + break; + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + res = mknod(file_header->name, file_header->mode, file_header->device); + if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { + perror_msg("Cannot create node %s", file_header->name); + } + break; + } + + chmod(file_header->name, file_header->mode); + chown(file_header->name, file_header->uid, file_header->gid); + + if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) { + struct utimbuf t; + t.actime = t.modtime = file_header->mtime; + utime(file_header->name, &t); + } +} diff --git a/archival/libunarchive/data_extract_to_stdout.c b/archival/libunarchive/data_extract_to_stdout.c new file mode 100644 index 000000000..00687b315 --- /dev/null +++ b/archival/libunarchive/data_extract_to_stdout.c @@ -0,0 +1,8 @@ +#include <stdlib.h> +#include <stdio.h> +#include "unarchive.h" + +extern void data_extract_to_stdout(archive_handle_t *archive_handle) +{ + copy_file_chunk_fd(archive_handle->src_fd, fileno(stdout), archive_handle->file_header->size); +} diff --git a/archival/libunarchive/data_skip.c b/archival/libunarchive/data_skip.c new file mode 100644 index 000000000..4e63d4304 --- /dev/null +++ b/archival/libunarchive/data_skip.c @@ -0,0 +1,27 @@ +/* + * 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 <sys/types.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include "unarchive.h" +#include "libbb.h" + +extern void data_skip(archive_handle_t *archive_handle) +{ + seek_sub_file(archive_handle->src_fd, archive_handle->file_header->size); +} diff --git a/archival/libunarchive/decompress_unzip.c b/archival/libunarchive/decompress_unzip.c index d84067068..20bf88f55 100644 --- a/archival/libunarchive/decompress_unzip.c +++ b/archival/libunarchive/decompress_unzip.c @@ -65,114 +65,126 @@ static char *license_msg[] = { #include <signal.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> +#include <fcntl.h> #include "config.h" -#include "libbb.h" +#include "busybox.h" +#include "unarchive.h" -#ifdef CONFIG_FEATURE_UNCOMPRESS -int uncompress(FILE * in, FILE * out); -#endif - -static FILE *in_file, *out_file; +typedef struct huft_s { + unsigned char e; /* number of extra bits or operation */ + unsigned char b; /* number of bits in this code or subcode */ + union { + unsigned short n; /* literal, length base, or distance base */ + struct huft_s *t; /* pointer to next level of table */ + } v; +} huft_t; -/* these are freed by gz_close */ -static unsigned char *window; -static unsigned long *crc_table; +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 */ -static unsigned long crc; /* shift register contents */ +/* 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; -/* Return codes from gzip */ -#define ERROR 1 +/* gunzip_window size--must be a power of two, and + * at least 32K for zip's deflate method */ +static const int gunzip_wsize = 0x8000; -/* - * window size--must be a power of two, and - * at least 32K for zip's deflate method - */ -#define WSIZE 0x8000 +static unsigned char *gunzip_window; +static unsigned int *gunzip_crc_table; +unsigned int gunzip_crc; /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ #define BMAX 16 /* maximum bit length of any code (16 for explode) */ #define N_MAX 288 /* maximum number of codes in any set */ -static long bytes_out; /* number of output bytes */ -static unsigned long outcnt; /* bytes in output buffer */ - -static unsigned hufts; /* track memory usage */ -static unsigned long bb; /* bit buffer */ -static unsigned bk; /* bits in bit buffer */ - -typedef struct huft_s { - unsigned char e; /* number of extra bits or operation */ - unsigned char b; /* number of bits in this code or subcode */ - union { - unsigned short n; /* literal, length base, or distance base */ - struct huft_s *t; /* pointer to next level of table */ - } v; -} huft_t; +static unsigned int gunzip_hufts; /* track memory usage */ +static unsigned int gunzip_bb; /* bit buffer */ +static unsigned char gunzip_bk; /* bits in bit buffer */ static const unsigned short mask_bits[] = { - 0x0000, - 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; -/* static int error_number = 0; */ -/* ======================================================================== - * Signal and error handler. - */ +/* Copy lengths for literal codes 257..285 */ +static const unsigned short cplens[] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +}; + +/* note: see note #13 above about the 258 in this list. */ +/* Extra bits for literal codes 257..285 */ +static const unsigned char cplext[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 5, 0, 99, 99 +}; /* 99==invalid */ + +/* Copy offsets for distance codes 0..29 */ +static const unsigned short cpdist[] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 +}; + +/* Extra bits for distance codes */ +static const unsigned char cpdext[] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; + +/* Tables for deflate from PKZIP's appnote.txt. */ +/* Order of the bit length code lengths */ +static const unsigned char border[] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +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; + *current += 8; + } + + return(bitbuffer); +} static void abort_gzip(void) { error_msg("gzip aborted\n"); - exit(ERROR); + exit(-1); } -static void make_crc_table(void) +static void make_gunzip_crc_table(void) { - const unsigned long poly = 0xedb88320; /* polynomial exclusive-or pattern */ + const unsigned int poly = 0xedb88320; /* polynomial exclusive-or pattern */ unsigned short i; /* counter for all possible eight bit values */ /* initial shift register value */ - crc = 0xffffffffL; - crc_table = (unsigned long *) malloc(256 * sizeof(unsigned long)); + gunzip_crc = 0xffffffffL; + gunzip_crc_table = (unsigned int *) malloc(256 * sizeof(unsigned int)); /* Compute and print table of CRC's, five per line */ for (i = 0; i < 256; i++) { - unsigned long table_entry; /* crc shift register */ - char k; /* byte being shifted into crc apparatus */ + unsigned int table_entry; /* crc shift register */ + unsigned char k; /* byte being shifted into crc apparatus */ table_entry = i; /* The idea to initialize the register with the byte instead of * zero was stolen from Haruhiko Okumura's ar002 */ for (k = 8; k; k--) { - table_entry = - table_entry & 1 ? (table_entry >> 1) ^ poly : table_entry >> - 1; - } - crc_table[i] = table_entry; + if (table_entry & 1) { + table_entry = (table_entry >> 1) ^ poly; + } else { + table_entry >>= 1; } -} - -/* =========================================================================== - * 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) -{ - int n; - - if (outcnt == 0) - return; - - for (n = 0; n < outcnt; n++) { - crc = crc_table[((int) crc ^ (window[n])) & 0xff] ^ (crc >> 8); } - - if (fwrite(window, 1, outcnt, out_file) != outcnt) { - error_msg_and_die("Couldnt write"); + gunzip_crc_table[i] = table_entry; } - bytes_out += (unsigned long) outcnt; - outcnt = 0; } /* @@ -183,7 +195,8 @@ static void flush_window(void) */ static int huft_free(huft_t * t) { - huft_t *p, *q; + huft_t *p; + huft_t *q; /* Go through linked list, freeing from the malloced (t[-1]) address. */ p = t; @@ -195,8 +208,6 @@ static int huft_free(huft_t * t) return 0; } -typedef unsigned char extra_bits_t; - /* Given a list of code lengths and a maximum table size, make a set of * tables to decode that set of codes. Return zero on success, one if * the given code set is incomplete (the tables are still built in this @@ -213,7 +224,7 @@ typedef unsigned char extra_bits_t; */ static int huft_build(unsigned int *b, const unsigned int n, const unsigned int s, const unsigned short *d, - const extra_bits_t * e, huft_t ** t, int *m) + const unsigned char *e, huft_t ** t, int *m) { unsigned a; /* counter for codes of length k */ unsigned c[BMAX + 1]; /* bit length count table */ @@ -251,26 +262,35 @@ static int huft_build(unsigned int *b, const unsigned int n, /* Find minimum and maximum length, bound *m by those */ l = *m; - for (j = 1; j <= BMAX; j++) - if (c[j]) + for (j = 1; j <= BMAX; j++) { + if (c[j]) { break; + } + } k = j; /* minimum code length */ - if ((unsigned) l < j) + if ((unsigned) l < j) { l = j; - for (i = BMAX; i; i--) - if (c[i]) + } + for (i = BMAX; i; i--) { + if (c[i]) { break; + } + } g = i; /* maximum code length */ - if ((unsigned) l > i) + if ((unsigned) l > i) { l = i; + } *m = l; /* Adjust last length count to fill out codes, if needed */ - for (y = 1 << j; j < i; j++, y <<= 1) - if ((y -= c[j]) < 0) + for (y = 1 << j; j < i; j++, y <<= 1) { + if ((y -= c[j]) < 0) { return 2; /* bad input: more codes than bits */ - if ((y -= c[i]) < 0) + } + } + if ((y -= c[i]) < 0) { return 2; + } c[i] += y; /* Generate starting offsets into the value table for each length */ @@ -285,8 +305,9 @@ static int huft_build(unsigned int *b, const unsigned int n, p = b; i = 0; do { - if ((j = *p++) != 0) + if ((j = *p++) != 0) { v[x[j]++] = i; + } } while (++i < n); /* Generate the Huffman codes and for each, make the table entries */ @@ -314,8 +335,9 @@ static int huft_build(unsigned int *b, const unsigned int n, f -= a + 1; /* deduct codes from patterns left */ xp = c + k; while (++j < z) { /* try smaller tables up to z bits */ - if ((f <<= 1) <= *++xp) + if ((f <<= 1) <= *++xp) { break; /* enough codes to use up j bits */ + } f -= *xp; /* else deduct codes from patterns */ } } @@ -324,7 +346,7 @@ static int huft_build(unsigned int *b, const unsigned int n, /* allocate and link in new table */ q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t)); - hufts += z + 1; /* track memory usage */ + 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 */ @@ -342,9 +364,9 @@ static int huft_build(unsigned int *b, const unsigned int n, /* set up table entry in r */ r.b = (unsigned char) (k - w); - if (p >= v + n) + if (p >= v + n) { r.e = 99; /* out of values--invalid code */ - else if (*p < s) { + } else if (*p < s) { r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is end-of-block code */ r.v.n = (unsigned short) (*p); /* simple code is just the value */ p++; /* one compiler does not like *p++ */ @@ -355,12 +377,14 @@ static int huft_build(unsigned int *b, const unsigned int n, /* fill code-like entries with r */ f = 1 << (k - w); - for (j = i >> w; j < z; j += f) + for (j = i >> w; j < z; j += f) { q[j] = r; + } /* backwards increment the k-bit code i */ - for (j = 1 << (k - 1); i & j; j >>= 1) + for (j = 1 << (k - 1); i & j; j >>= 1) { i ^= j; + } i ^= j; /* backup over finished tables */ @@ -374,6 +398,25 @@ static int huft_build(unsigned int *b, const unsigned int n, 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. @@ -381,32 +424,26 @@ static int huft_build(unsigned int *b, const unsigned int n, * 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, int bl, int bd) +static int inflate_codes(huft_t * tl, huft_t * td, const unsigned int bl, const unsigned int bd) { - register unsigned long e; /* table entry flag/number of extra bits */ - unsigned long n, d; /* length and index for copy */ - unsigned long w; /* current window position */ + 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 ml, md; /* masks for bl and bd bits */ - register unsigned long b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - register int input_char; + 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 = bb; /* initialize bit buffer */ - k = bk; - w = outcnt; /* initialize window position */ + 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]; - for (;;) { /* do until end of block */ - while (k < (unsigned) bl) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + 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) { @@ -415,20 +452,16 @@ static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd) b >>= t->b; k -= t->b; e -= 16; - while (k < e) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, e); } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); b >>= t->b; k -= t->b; if (e == 16) { /* then it's a literal */ - window[w++] = (unsigned char) t->v.n; - if (w == WSIZE) { - outcnt = (w), flush_window(); + gunzip_window[w++] = (unsigned char) t->v.n; + if (w == gunzip_wsize) { + gunzip_outbuf_count = (w); + flush_gunzip_window(); w = 0; } } else { /* it's an EOB or a length */ @@ -439,24 +472,13 @@ static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd) } /* get length of block to copy */ - while (k < e) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, e); n = t->v.n + ((unsigned) b & mask_bits[e]); b >>= e; k -= e; /* decode distance of block to copy */ - while (k < (unsigned) bd) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } - + b = fill_bitbuffer(b, &k, bd); if ((e = (t = td + ((unsigned) b & md))->e) > 16) do { if (e == 99) @@ -464,23 +486,13 @@ static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd) b >>= t->b; k -= t->b; e -= 16; - while (k < e) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, e); } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); b >>= t->b; k -= t->b; - while (k < e) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, e); d = w - t->v.n - ((unsigned) b & mask_bits[e]); b >>= e; k -= e; @@ -489,60 +501,38 @@ static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd) do { n -= (e = (e = - WSIZE - ((d &= WSIZE - 1) > w ? d : w)) > n ? n : e); -#if !defined(NOMEMCPY) && !defined(DEBUG) + gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e); + /* copy to new buffer to prevent possible overwrite */ if (w - d >= e) { /* (this test assumes unsigned comparison) */ - memcpy(window + w, window + d, e); + memcpy(gunzip_window + w, gunzip_window + d, e); w += e; d += e; - } else /* do it slow to avoid memcpy() overlap */ -#endif /* !NOMEMCPY */ + } else { + /* do it slow to avoid memcpy() overlap */ + /* !NOMEMCPY */ do { - window[w++] = window[d++]; + gunzip_window[w++] = gunzip_window[d++]; } while (--e); - if (w == WSIZE) { - outcnt = (w), flush_window(); + } + if (w == gunzip_wsize) { + gunzip_outbuf_count = (w); + flush_gunzip_window(); w = 0; } + } while (n); } } /* restore the globals from the locals */ - outcnt = w; /* restore global window pointer */ - bb = b; /* restore global bit buffer */ - bk = k; + gunzip_outbuf_count = w; /* restore global gunzip_window pointer */ + gunzip_bb = b; /* restore global bit buffer */ + gunzip_bk = k; /* done */ return 0; } -static const unsigned short cplens[] = { /* Copy lengths for literal codes 257..285 */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -}; - -/* note: see note #13 above about the 258 in this list. */ -static const extra_bits_t cplext[] = { /* Extra bits for literal codes 257..285 */ - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 -}; /* 99==invalid */ -static const unsigned short cpdist[] = { /* Copy offsets for distance codes 0..29 */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577 -}; -static const extra_bits_t cpdext[] = { /* Extra bits for distance codes */ - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, - 12, 12, 13, 13 -}; - -/* Tables for deflate from PKZIP's appnote.txt. */ -static const extra_bits_t border[] = { /* Order of the bit length code lengths */ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 -}; - /* * decompress an inflated block * e: last block flag @@ -552,53 +542,43 @@ static const extra_bits_t border[] = { /* Order of the bit length code lengths * static int inflate_block(int *e) { unsigned t; /* block type */ - register unsigned long b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - int input_char; + register unsigned int b; /* bit buffer */ + unsigned int k; /* number of bits in bit buffer */ /* make local bit buffer */ - b = bb; - k = bk; + + b = gunzip_bb; + k = gunzip_bk; /* read in last block bit */ - while (k < 1) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, 1); *e = (int) b & 1; b >>= 1; k -= 1; /* read in block type */ - while (k < 2) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, 2); t = (unsigned) b & 3; b >>= 2; k -= 2; /* restore the global bit buffer */ - bb = b; - bk = k; + gunzip_bb = b; + gunzip_bk = k; /* inflate that block type */ switch (t) { case 0: /* Inflate stored */ { - unsigned long n; /* number of bytes in block */ - unsigned long w; /* current window position */ - register unsigned long b_stored; /* bit buffer */ - register unsigned long k_stored; /* number of bits in bit buffer */ + 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 = bb; /* initialize bit buffer */ - k_stored = bk; - w = outcnt; /* initialize window position */ + 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; @@ -606,21 +586,12 @@ static int inflate_block(int *e) k_stored -= n; /* get the length and its complement */ - while (k_stored < 16) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_stored |= ((unsigned long)input_char) << k_stored; - k_stored += 8; - } + b_stored = fill_bitbuffer(b_stored, &k_stored, 16); n = ((unsigned) b_stored & 0xffff); b_stored >>= 16; k_stored -= 16; - while (k_stored < 16) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_stored |= ((unsigned long)input_char) << k_stored; - k_stored += 8; - } + + b_stored = fill_bitbuffer(b_stored, &k_stored, 16); if (n != (unsigned) ((~b_stored) & 0xffff)) { return 1; /* error in compressed data */ } @@ -629,15 +600,11 @@ static int inflate_block(int *e) /* read and output the compressed data */ while (n--) { - while (k_stored < 8) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_stored |= ((unsigned long)input_char) << k_stored; - k_stored += 8; - } - window[w++] = (unsigned char) b_stored; - if (w == (unsigned long) WSIZE) { - outcnt = (w), flush_window(); + 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; @@ -645,9 +612,9 @@ static int inflate_block(int *e) } /* restore the globals from the locals */ - outcnt = w; /* restore global window pointer */ - bb = b_stored; /* restore global bit buffer */ - bk = k_stored; + gunzip_outbuf_count = w; /* restore global gunzip_window pointer */ + gunzip_bb = b_stored; /* restore global bit buffer */ + gunzip_bk = k_stored; return 0; } case 1: /* Inflate fixed @@ -659,8 +626,8 @@ static int inflate_block(int *e) int i; /* temporary variable */ huft_t *tl; /* literal/length code table */ huft_t *td; /* distance code table */ - int bl; /* lookup bits for tl */ - int bd; /* lookup bits for td */ + unsigned int bl; /* lookup bits for tl */ + unsigned int bd; /* lookup bits for td */ unsigned int l[288]; /* length list for huft_build */ /* set up literal table */ @@ -692,8 +659,9 @@ static int inflate_block(int *e) } /* decompress until an end-of-block code */ - if (inflate_codes(tl, td, bl, bd)) + if (inflate_codes(tl, td, bl, bd)) { return 1; + } /* free the decoding tables, return */ huft_free(tl); @@ -705,53 +673,41 @@ static int inflate_block(int *e) const int dbits = 6; /* bits in base distance lookup table */ const int lbits = 9; /* bits in base literal/length lookup table */ - int i; /* temporary variables */ - unsigned j; - unsigned l; /* last length */ - unsigned m; /* mask for bit lengths table */ - unsigned n; /* number of lengths to get */ huft_t *tl; /* literal/length code table */ huft_t *td; /* distance code table */ - int bl; /* lookup bits for tl */ - int bd; /* lookup bits for td */ - unsigned nb; /* number of bit length codes */ - unsigned nl; /* number of literal/length codes */ - unsigned nd; /* number of distance codes */ - - unsigned ll[286 + 30]; /* literal/length and distance code lengths */ - register unsigned long b_dynamic; /* bit buffer */ - register unsigned k_dynamic; /* number of bits in bit buffer */ + unsigned int i; /* temporary variables */ + unsigned int j; + unsigned int l; /* last length */ + unsigned int m; /* mask for bit lengths table */ + unsigned int n; /* number of lengths to get */ + unsigned int bl; /* lookup bits for tl */ + unsigned int bd; /* lookup bits for td */ + unsigned int nb; /* number of bit length codes */ + unsigned int nl; /* number of literal/length codes */ + unsigned int nd; /* number of distance codes */ + + unsigned int ll[286 + 30]; /* literal/length and distance code lengths */ + unsigned int b_dynamic; /* bit buffer */ + unsigned int k_dynamic; /* number of bits in bit buffer */ /* make local bit buffer */ - b_dynamic = bb; - k_dynamic = bk; + b_dynamic = gunzip_bb; + k_dynamic = gunzip_bk; /* read in table lengths */ - while (k_dynamic < 5) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - nl = 257 + ((unsigned) b_dynamic & 0x1f); /* number of literal/length codes */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5); + nl = 257 + ((unsigned int) b_dynamic & 0x1f); /* number of literal/length codes */ + b_dynamic >>= 5; k_dynamic -= 5; - while (k_dynamic < 5) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - nd = 1 + ((unsigned) b_dynamic & 0x1f); /* number of distance codes */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5); + nd = 1 + ((unsigned int) b_dynamic & 0x1f); /* number of distance codes */ + b_dynamic >>= 5; k_dynamic -= 5; - while (k_dynamic < 4) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - nb = 4 + ((unsigned) b_dynamic & 0xf); /* number of bit length codes */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4); + nb = 4 + ((unsigned int) b_dynamic & 0xf); /* number of bit length codes */ + b_dynamic >>= 4; k_dynamic -= 4; if (nl > 286 || nd > 30) { @@ -760,13 +716,8 @@ static int inflate_block(int *e) /* read in bit-length-code lengths */ for (j = 0; j < nb; j++) { - while (k_dynamic < 3) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - ll[border[j]] = (unsigned) b_dynamic & 7; + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3); + ll[border[j]] = (unsigned int) b_dynamic & 7; b_dynamic >>= 3; k_dynamic -= 3; } @@ -776,7 +727,8 @@ static int inflate_block(int *e) /* build decoding table for trees--single level, 7 bit lookup */ bl = 7; - if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) { + i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl); + if (i != 0) { if (i == 1) { huft_free(tl); } @@ -787,46 +739,31 @@ static int inflate_block(int *e) n = nl + nd; m = mask_bits[bl]; i = l = 0; - while ((unsigned) i < n) { - while (k_dynamic < (unsigned) bl) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - j = (td = tl + ((unsigned) b_dynamic & m))->b; + while ((unsigned int) i < n) { + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl); + j = (td = tl + ((unsigned int) b_dynamic & m))->b; b_dynamic >>= j; k_dynamic -= j; j = td->v.n; if (j < 16) { /* length of code in bits (0..15) */ ll[i++] = l = j; /* save last length in l */ } else if (j == 16) { /* repeat last length 3 to 6 times */ - while (k_dynamic < 2) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - j = 3 + ((unsigned) b_dynamic & 3); + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2); + j = 3 + ((unsigned int) b_dynamic & 3); b_dynamic >>= 2; k_dynamic -= 2; - if ((unsigned) i + j > n) { + if ((unsigned int) i + j > n) { return 1; } while (j--) { ll[i++] = l; } } else if (j == 17) { /* 3 to 10 zero length codes */ - while (k_dynamic < 3) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - j = 3 + ((unsigned) b_dynamic & 7); + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3); + j = 3 + ((unsigned int) b_dynamic & 7); b_dynamic >>= 3; k_dynamic -= 3; - if ((unsigned) i + j > n) { + if ((unsigned int) i + j > n) { return 1; } while (j--) { @@ -834,16 +771,11 @@ static int inflate_block(int *e) } l = 0; } else { /* j == 18: 11 to 138 zero length codes */ - while (k_dynamic < 7) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - j = 11 + ((unsigned) b_dynamic & 0x7f); + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7); + j = 11 + ((unsigned int) b_dynamic & 0x7f); b_dynamic >>= 7; k_dynamic -= 7; - if ((unsigned) i + j > n) { + if ((unsigned int) i + j > n) { return 1; } while (j--) { @@ -857,22 +789,24 @@ static int inflate_block(int *e) huft_free(tl); /* restore the global bit buffer */ - bb = b_dynamic; - bk = k_dynamic; + gunzip_bb = b_dynamic; + gunzip_bk = k_dynamic; /* build the decoding tables for literal/length and distance codes */ bl = lbits; + if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) { if (i == 1) { - error_msg("Incomplete literal tree"); + error_msg_and_die("Incomplete literal tree"); huft_free(tl); } return i; /* incomplete code set */ } + bd = dbits; if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) { if (i == 1) { - error_msg("incomplete distance tree"); + error_msg_and_die("incomplete distance tree"); huft_free(td); } huft_free(tl); @@ -880,8 +814,9 @@ static int inflate_block(int *e) } /* decompress until an end-of-block code */ - if (inflate_codes(tl, td, bl, bd)) + if (inflate_codes(tl, td, bl, bd)) { return 1; + } /* free the decoding tables, return */ huft_free(tl); @@ -890,6 +825,7 @@ static int inflate_block(int *e) } default: /* bad block type */ + error_msg("bad block type %d\n", t); return 2; } } @@ -897,72 +833,24 @@ static int inflate_block(int *e) /* * decompress an inflated entry * - * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr + * GLOBAL VARIABLES: gunzip_outbuf_count, bk, gunzip_bb, hufts, inptr */ -extern int inflate(FILE * in, FILE * out) +extern int inflate(int in, int out) { + typedef void (*sig_type) (int); int e; /* last block flag */ int r; /* result code */ unsigned h = 0; /* maximum struct huft's malloc'ed */ - /* initialize window, bit buffer */ - outcnt = 0; - 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; + gunzip_window = xmalloc(0x8000); + gunzip_outbuf_count = 0; + gunzip_bytes_out = 0; - /* Create the crc table */ - make_crc_table(); + gunzip_src_fd = in; + gunzip_dst_fd = out; - /* decompress until the last block */ - do { - hufts = 0; - if ((r = inflate_block(&e)) != 0) { - return r; - } - if (hufts > h) { - h = hufts; - } - } while (!e); - - /* Undo too much lookahead. The next read will be byte aligned so we - * can discard unused bits in the last meaningful byte. - */ - while (bk >= 8) { - bk -= 8; - ungetc((bb << bk), in_file); - } - - /* flush out window */ - flush_window(); - free(window); - free(crc_table); - - /* return success */ - return 0; -} - -/* =========================================================================== - * 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. - * The magic header has already been checked. The output buffer is cleared. - * in, out: input and output file descriptors - */ -extern int unzip(FILE * l_in_file, FILE * l_out_file) -{ - unsigned char buf[8]; /* extended local header */ - unsigned char flags; /* compression flags */ - typedef void (*sig_type) (int); - unsigned short i; - unsigned char magic[2]; + gunzip_in_buffer = malloc(8); if (signal(SIGINT, SIG_IGN) != SIG_IGN) { (void) signal(SIGINT, (sig_type) abort_gzip); @@ -973,97 +861,44 @@ extern int unzip(FILE * l_in_file, FILE * l_out_file) } #endif - magic[0] = fgetc(l_in_file); - magic[1] = fgetc(l_in_file); - -#ifdef CONFIG_FEATURE_UNCOMPRESS - /* Magic header for compress files, 1F 9d = \037\235 */ - if ((magic[0] == 0x1F) && (magic[1] == 0x9d)) { - return uncompress(l_in_file, l_out_file); - } -#endif - - /* Magic header for gzip files, 1F 8B = \037\213 */ - if ((magic[0] != 0x1F) || (magic[1] != 0x8b)) { - error_msg("Invalid gzip magic"); - return EXIT_FAILURE; - } - - /* Check the compression method */ - if (fgetc(l_in_file) != 8) /* also catches EOF */ { - error_msg("Unknown compression method"); - return (-1); - } - - flags = (unsigned char) fgetc(l_in_file); - - /* Ignore time stamp(4), extra flags(1), OS type(1) */ - for (i = 0; i < 6; i++) { - fgetc(l_in_file); - } + /* initialize gunzip_window, bit buffer */ + gunzip_bk = 0; + gunzip_bb = 0; - if (flags & 0x04) { - /* bit 2 set: extra field present */ - const unsigned short extra = - fgetc(l_in_file) + (fgetc(l_in_file) << 8); - if (feof(in_file)) return 1; - for (i = 0; i < extra; i++) { - fgetc(l_in_file); - } - } - - /* Discard original name if any */ - if (flags & 0x08) { - /* bit 3 set: original file name present */ - while (fgetc(l_in_file) != 0 && !feof(l_in_file)); /* null */ - } + /* Create the crc table */ + make_gunzip_crc_table(); - /* Discard file comment if any */ - if (flags & 0x10) { - /* bit 4 set: file comment present */ - while (fgetc(l_in_file) != 0 && !feof(l_in_file)); /* null */ + /* 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; } - - /* Decompress */ - if (inflate(l_in_file, l_out_file) != 0) { - error_msg("invalid compressed data--format violated"); + if (gunzip_hufts > h) { + h = gunzip_hufts; } + } while (!e); - /* Get the crc and original length - * crc32 (see algorithm.doc) - * uncompressed input size modulo 2^32 - */ - 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)) { - error_msg("invalid compressed data--crc error"); - } - /* Validate decompression - size */ - if (((buf[4] | (buf[5] << 8)) | ((buf[6] | (buf[7] << 8)) << 16)) != - (unsigned long) bytes_out) { - error_msg("invalid compressed data--length error"); + /* write any buffered uncompressed data */ + flush_gunzip_window(); + free(gunzip_window); + + /* Cleanup */ + 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++; + gunzip_bb >>= 8; + gunzip_bk -= 8; } + /* return success */ return 0; } - -/* - * This needs access to global variables window and crc_table, so its not in its own file. - */ -extern void gz_close(int gunzip_pid) -{ - if (kill(gunzip_pid, SIGTERM) == -1) { - error_msg_and_die - ("*** Couldnt kill old gunzip process *** aborting"); - } - - if (waitpid(gunzip_pid, NULL, 0) == -1) { - printf("Couldnt wait ?"); - } - - free(window); - free(crc_table); -} diff --git a/archival/libunarchive/filter_accept_all.c b/archival/libunarchive/filter_accept_all.c new file mode 100644 index 000000000..0471ccef0 --- /dev/null +++ b/archival/libunarchive/filter_accept_all.c @@ -0,0 +1,10 @@ +#include <fnmatch.h> +#include <stdlib.h> +#include "unarchive.h" +/* + * Accept names that are in the accept list + */ +extern char filter_accept_all(const llist_t *accept_list, const llist_t *reject_list, const char *key) +{ + return(EXIT_SUCCESS); +} diff --git a/archival/libunarchive/filter_accept_list.c b/archival/libunarchive/filter_accept_list.c new file mode 100644 index 000000000..06b1dd3dd --- /dev/null +++ b/archival/libunarchive/filter_accept_list.c @@ -0,0 +1,16 @@ +#include <fnmatch.h> +#include <stdlib.h> +#include "unarchive.h" +/* + * Accept names that are in the accept list + */ +extern char filter_accept_list(const llist_t *accept_list, const llist_t *reject_list, const char *key) +{ + while (accept_list) { + if (fnmatch(accept_list->data, key, 0) == 0) { + return(EXIT_SUCCESS); + } + accept_list = accept_list->link; + } + return(EXIT_FAILURE); +} diff --git a/archival/libunarchive/filter_accept_reject_list.c b/archival/libunarchive/filter_accept_reject_list.c new file mode 100644 index 000000000..c893dfcfc --- /dev/null +++ b/archival/libunarchive/filter_accept_reject_list.c @@ -0,0 +1,34 @@ +#include <fnmatch.h> +#include <stdlib.h> +#include "unarchive.h" + +static char check_list(const llist_t *list, const char *filename) +{ + if (list) { + while (list) { + if (fnmatch(list->data, filename, 0) == 0) { + return(EXIT_SUCCESS); + } + list = list->link; + } + } + return(EXIT_FAILURE); +} + +/* + * Accept names that are in the accept list + */ +extern char filter_accept_reject_list(const llist_t *accept_list, const llist_t *reject_list, const char *key) +{ + /* Fail if an accept list was specified and the key wasnt in there */ + if ((accept_list) && (check_list(accept_list, key) == EXIT_FAILURE)) { + return(EXIT_FAILURE); + } + + /* If the key is in a reject list fail */ + if (check_list(reject_list, key) == EXIT_FAILURE) { + return(EXIT_FAILURE); + } + + return(EXIT_SUCCESS); +} diff --git a/archival/libunarchive/get_header_ar.c b/archival/libunarchive/get_header_ar.c index f172fa7c9..b7f2cfbcd 100644 --- a/archival/libunarchive/get_header_ar.c +++ b/archival/libunarchive/get_header_ar.c @@ -17,12 +17,13 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include "unarchive.h" #include "libbb.h" -file_header_t *get_header_ar(FILE *src_stream) +extern char get_header_ar(archive_handle_t *archive_handle) { - file_header_t *typed; + file_header_t *typed = archive_handle->file_header; union { char raw[60]; struct { @@ -35,72 +36,87 @@ file_header_t *get_header_ar(FILE *src_stream) char magic[2]; } formated; } ar; +#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES static char *ar_long_names; + static unsigned int ar_long_name_size; +#endif - if (fread(ar.raw, 1, 60, src_stream) != 60) { - return(NULL); - } - archive_offset += 60; - /* align the headers based on the header magic */ - if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) { - /* some version of ar, have an extra '\n' after each data entry, - * this puts the next header out by 1 */ - if (ar.formated.magic[1] != '`') { - error_msg("Invalid magic"); - return(NULL); - } - /* read the next char out of what would be the data section, - * if its a '\n' then it is a valid header offset by 1*/ - archive_offset++; - if (fgetc(src_stream) != '\n') { - error_msg("Invalid magic"); - return(NULL); + /* dont use xread as we want to handle the error ourself */ + if (read(archive_handle->src_fd, ar.raw, 60) != 60) { + /* End Of File */ + return(EXIT_FAILURE); } + + /* Some ar entries have a trailing '\n' after the previous data entry */ + if (ar.raw[0] == '\n') { /* fix up the header, we started reading 1 byte too early */ - /* raw_header[60] wont be '\n' as it should, but it doesnt matter */ memmove(ar.raw, &ar.raw[1], 59); + ar.raw[59] = xread_char(archive_handle->src_fd); + archive_handle->offset++; } + archive_handle->offset += 60; - typed = (file_header_t *) xcalloc(1, sizeof(file_header_t)); + /* align the headers based on the header magic */ + if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) { + error_msg_and_die("Invalid ar header"); + } + + typed->mode = strtol(ar.formated.mode, NULL, 8); + typed->mtime = atoi(ar.formated.date); + typed->uid = atoi(ar.formated.uid); + typed->gid = atoi(ar.formated.gid); + typed->size = atoi(ar.formated.size); - typed->size = (size_t) atoi(ar.formated.size); /* long filenames have '/' as the first character */ if (ar.formated.name[0] == '/') { +#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES if (ar.formated.name[1] == '/') { /* If the second char is a '/' then this entries data section * stores long filename for multiple entries, they are stored * in static variable long_names for use in future entries */ - ar_long_names = (char *) xrealloc(ar_long_names, typed->size); - fread(ar_long_names, 1, typed->size, src_stream); - archive_offset += typed->size; + ar_long_name_size = typed->size; + ar_long_names = xmalloc(ar_long_name_size); + xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size); + archive_handle->offset += ar_long_name_size; /* This ar entries data section only contained filenames for other records * they are stored in the static ar_long_names for future reference */ - return (get_header_ar(src_stream)); /* Return next header */ + return (get_header_ar(archive_handle)); /* Return next header */ } else if (ar.formated.name[1] == ' ') { /* This is the index of symbols in the file for compilers */ - seek_sub_file(src_stream, typed->size); - return (get_header_ar(src_stream)); /* Return next header */ + data_skip(archive_handle); + return (get_header_ar(archive_handle)); /* Return next header */ } else { /* The number after the '/' indicates the offset in the ar data section (saved in variable long_name) that conatains the real filename */ - if (!ar_long_names) { - error_msg("Cannot resolve long file name"); - return (NULL); + const unsigned int long_offset = atoi(&ar.formated.name[1]); + if (long_offset >= ar_long_name_size) { + error_msg_and_die("Cant resolve long filename"); } - typed->name = xstrdup(ar_long_names + atoi(&ar.formated.name[1])); + typed->name = xstrdup(ar_long_names + long_offset); } +#else + error_msg_and_die("long filenames not supported"); +#endif } else { /* short filenames */ typed->name = xstrndup(ar.formated.name, 16); } - typed->name[strcspn(typed->name, " /")]='\0'; - /* convert the rest of the now valid char header to its typed struct */ - parse_mode(ar.formated.mode, &typed->mode); - typed->mtime = atoi(ar.formated.date); - typed->uid = atoi(ar.formated.uid); - typed->gid = atoi(ar.formated.gid); + typed->name[strcspn(typed->name, " /")] = '\0'; + + if (archive_handle->filter(archive_handle->accept, archive_handle->reject, typed->name) == EXIT_SUCCESS) { + archive_handle->action_header(typed); + if (archive_handle->sub_archive) { + while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS); + } else { + archive_handle->action_data(archive_handle); + } + } else { + data_skip(archive_handle); + } + + archive_handle->offset += typed->size + 1; - return(typed); + return(EXIT_SUCCESS); } diff --git a/archival/libunarchive/get_header_tar.c b/archival/libunarchive/get_header_tar.c index e6747b72f..2c8fc0aa0 100644 --- a/archival/libunarchive/get_header_tar.c +++ b/archival/libunarchive/get_header_tar.c @@ -20,8 +20,9 @@ #include "unarchive.h" #include "libbb.h" -file_header_t *get_header_tar(FILE * tar_stream) +extern char get_header_tar(archive_handle_t *archive_handle) { + file_header_t *file_header = archive_handle->file_header; union { unsigned char raw[512]; struct { @@ -44,20 +45,22 @@ file_header_t *get_header_tar(FILE * tar_stream) char padding[12]; /* 500-512 */ } formated; } tar; - file_header_t *tar_entry = NULL; long sum = 0; long i; - if (archive_offset % 512 != 0) { - seek_sub_file(tar_stream, 512 - (archive_offset % 512)); + /* Align header */ + archive_handle->offset += data_align(archive_handle->src_fd, archive_handle->offset, 512); + + if (xread_all_eof(archive_handle->src_fd, tar.raw, 512) == 0) { + /* End of file */ + return(EXIT_FAILURE); } + archive_handle->offset += 512; - if (fread(tar.raw, 1, 512, tar_stream) != 512) { - /* Unfortunatly its common for tar files to have all sorts of - * trailing garbage, fail silently */ - return (NULL); + /* If there is no filename its an empty header */ + if (tar.formated.name[0] == 0) { + return(EXIT_SUCCESS); } - archive_offset += 512; /* Check header has valid magic, "ustar" is for the proper tar * 0's are for the old tar format @@ -66,100 +69,102 @@ file_header_t *get_header_tar(FILE * tar_stream) #ifdef CONFIG_FEATURE_TAR_OLD_FORMAT if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) #endif - return (NULL); - } - - /* If there is no filename its an empty header, skip it */ - if (tar.formated.name[0] == 0) { - return (NULL); + error_msg_and_die("Invalid tar magic"); } /* Do checksum on headers */ - for (i = 0; i < 148; i++) { + for (i = 0; i < 148 ; i++) { sum += tar.raw[i]; } sum += ' ' * 8; - for (i = 156; i < 512; i++) { + for (i = 156; i < 512 ; i++) { sum += tar.raw[i]; } if (sum != strtol(tar.formated.chksum, NULL, 8)) { error_msg("Invalid tar header checksum"); - return (NULL); + return(EXIT_FAILURE); } /* convert to type'ed variables */ - tar_entry = xcalloc(1, sizeof(file_header_t)); if (tar.formated.prefix[0] == 0) { - tar_entry->name = xstrdup(tar.formated.name); + file_header->name = strdup(tar.formated.name); } else { - tar_entry->name = - concat_path_file(tar.formated.prefix, tar.formated.name); + file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name); } - - tar_entry->mode = strtol(tar.formated.mode, NULL, 8); - tar_entry->uid = strtol(tar.formated.uid, NULL, 8); - tar_entry->gid = strtol(tar.formated.gid, NULL, 8); - tar_entry->size = strtol(tar.formated.size, NULL, 8); - tar_entry->mtime = strtol(tar.formated.mtime, NULL, 8); - tar_entry->link_name = - strlen(tar.formated.linkname) ? xstrdup(tar.formated.linkname) : NULL; - tar_entry->device = - (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) + + file_header->mode = strtol(tar.formated.mode, NULL, 8); + file_header->uid = strtol(tar.formated.uid, NULL, 8); + file_header->gid = strtol(tar.formated.gid, NULL, 8); + file_header->size = strtol(tar.formated.size, NULL, 8); + file_header->mtime = strtol(tar.formated.mtime, NULL, 8); + file_header->link_name = (tar.formated.linkname[0] != '\0') ? + xstrdup(tar.formated.linkname) : NULL; + file_header->device = (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) + strtol(tar.formated.devminor, NULL, 8)); #if defined CONFIG_FEATURE_TAR_OLD_FORMAT || defined CONFIG_FEATURE_GNUTAR_LONG_FILENAME + /* Fix mode, used by the old format */ switch (tar.formated.typeflag) { # ifdef CONFIG_FEATURE_TAR_OLD_FORMAT case 0: - tar_entry->mode |= S_IFREG; + file_header->mode |= S_IFREG; break; case 1: - error_msg("internal hard link not handled\n"); + error_msg("Internal hard link not supported"); break; case 2: - tar_entry->mode |= S_IFLNK; + file_header->mode |= S_IFLNK; break; case 3: - tar_entry->mode |= S_IFCHR; + file_header->mode |= S_IFCHR; break; case 4: - tar_entry->mode |= S_IFBLK; + file_header->mode |= S_IFBLK; break; case 5: - tar_entry->mode |= S_IFDIR; + file_header->mode |= S_IFDIR; break; case 6: - tar_entry->mode |= S_IFIFO; + file_header->mode |= S_IFIFO; break; # endif # ifdef CONFIG_FEATURE_GNUTAR_LONG_FILENAME case 'L': { char *longname; - longname = xmalloc(tar_entry->size + 1); - fread(longname, 1, tar_entry->size, tar_stream); - archive_offset += tar_entry->size; - longname[tar_entry->size] = '\0'; + longname = xmalloc(file_header->size + 1); + xread_all(archive_handle->src_fd, longname, file_header->size); + longname[file_header->size] = '\0'; + archive_handle->offset += file_header->size; - tar_entry = get_header_tar(tar_stream); - tar_entry->name = longname; + get_header_tar(archive_handle); + file_header->name = longname; break; } case 'K': { - char *longname; + char *linkname; - longname = xmalloc(tar_entry->size + 1); - fread(longname, 1, tar_entry->size, tar_stream); - archive_offset += tar_entry->size; - longname[tar_entry->size] = '\0'; + linkname = xmalloc(file_header->size + 1); + xread_all(archive_handle->src_fd, linkname, file_header->size); + linkname[file_header->size] = '\0'; + archive_handle->offset += file_header->size; - tar_entry = get_header_tar(tar_stream); - tar_entry->link_name = longname; + get_header_tar(archive_handle); + file_header->name = linkname; break; } # endif } #endif - return (tar_entry); + if (archive_handle->filter(archive_handle->accept, archive_handle->reject, archive_handle->file_header->name) == EXIT_SUCCESS) { + archive_handle->action_header(archive_handle->file_header); + archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; + archive_handle->action_data(archive_handle); + } else { + data_skip(archive_handle); + } + archive_handle->offset += file_header->size; + + return(EXIT_SUCCESS); } + diff --git a/archival/libunarchive/get_header_tar_gz.c b/archival/libunarchive/get_header_tar_gz.c new file mode 100644 index 000000000..c06beac9d --- /dev/null +++ b/archival/libunarchive/get_header_tar_gz.c @@ -0,0 +1,73 @@ +/* + * 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 Library 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 <sys/types.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "libbb.h" +#include "unarchive.h" + +extern char get_header_tar_gz(archive_handle_t *archive_handle) +{ + int fd_pipe[2]; + int pid; + + check_header_gzip(archive_handle->src_fd); + + if (pipe(fd_pipe) != 0) { + error_msg_and_die("Can't create pipe\n"); + } + + pid = fork(); + if (pid == -1) { + error_msg_and_die("Fork failed\n"); + } + + if (pid == 0) { + /* child process */ + close(fd_pipe[0]); /* We don't wan't to read from the pipe */ + inflate(archive_handle->src_fd, fd_pipe[1]); + check_trailer_gzip(archive_handle->src_fd); + close(fd_pipe[1]); /* Send EOF */ + exit(0); + /* notreached */ + } + /* parent process */ + close(fd_pipe[1]); /* Don't want to write down the pipe */ + close(archive_handle->src_fd); + + archive_handle->src_fd = fd_pipe[0]; + + while (get_header_tar(archive_handle) == EXIT_SUCCESS); + + if (kill(pid, SIGTERM) == -1) { + error_msg_and_die("Couldnt kill gunzip process"); + } + + /* I dont think this is needed */ +#if 0 + if (waitpid(pid, NULL, 0) == -1) { + error_msg("Couldnt wait ?"); + } +#endif + + /* Can only do one file at a time */ + return(EXIT_FAILURE); +} + diff --git a/archival/libunarchive/header_list.c b/archival/libunarchive/header_list.c new file mode 100644 index 000000000..5849a762e --- /dev/null +++ b/archival/libunarchive/header_list.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "unarchive.h" + +extern void header_list(const file_header_t *file_header) +{ + puts(file_header->name); +} diff --git a/archival/libunarchive/header_skip.c b/archival/libunarchive/header_skip.c new file mode 100644 index 000000000..4430178f8 --- /dev/null +++ b/archival/libunarchive/header_skip.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "unarchive.h" + +extern void header_skip(const file_header_t *file_header) +{ + return; +} diff --git a/archival/libunarchive/header_verbose_list.c b/archival/libunarchive/header_verbose_list.c new file mode 100644 index 000000000..ff7b3bca2 --- /dev/null +++ b/archival/libunarchive/header_verbose_list.c @@ -0,0 +1,29 @@ +#include <stdio.h> +#include <string.h> +#include <time.h> +#include "libbb.h" +#include "unarchive.h" + +extern void header_verbose_list(const file_header_t *file_header) +{ + struct tm *mtime = localtime(&file_header->mtime); + + printf("%s %d/%d%10u %4u-%02u-%02u %02u:%02u:%02u %s", + mode_string(file_header->mode), + file_header->uid, + file_header->gid, + (unsigned int) file_header->size, + 1900 + mtime->tm_year, + 1 + mtime->tm_mon, + mtime->tm_mday, + mtime->tm_hour, + mtime->tm_min, + mtime->tm_sec, + file_header->name); + + if (file_header->link_name) { + printf(" -> %s", file_header->link_name); + } + /* putchar isnt used anywhere else i dont think */ + puts(""); +} diff --git a/archival/libunarchive/init_handle.c b/archival/libunarchive/init_handle.c new file mode 100644 index 000000000..12d9e7183 --- /dev/null +++ b/archival/libunarchive/init_handle.c @@ -0,0 +1,18 @@ +#include <string.h> +#include "libbb.h" +#include "unarchive.h" + +archive_handle_t *init_handle(void) +{ + archive_handle_t *archive_handle; + + /* Initialise default values */ + archive_handle = xmalloc(sizeof(archive_handle_t)); + memset(archive_handle, 0, sizeof(archive_handle_t)); + archive_handle->file_header = xmalloc(sizeof(file_header_t)); + archive_handle->action_header = header_skip; + archive_handle->action_data = data_skip; + archive_handle->filter = filter_accept_all; + + return(archive_handle); +} diff --git a/archival/libunarchive/seek_sub_file.c b/archival/libunarchive/seek_sub_file.c index 7523a52ab..733bb36a9 100644 --- a/archival/libunarchive/seek_sub_file.c +++ b/archival/libunarchive/seek_sub_file.c @@ -14,23 +14,19 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include <stdio.h> +#include <sys/types.h> #include <errno.h> -#include <stdlib.h> #include <unistd.h> +#include <stdlib.h> +#include "unarchive.h" +#include "libbb.h" -off_t archive_offset; - -void seek_sub_file(FILE *src_stream, const int count) +extern void seek_sub_file(const int src_fd, const unsigned int amount) { - /* Try to fseek as faster */ - archive_offset += count; - if (fseek(src_stream, count, SEEK_CUR) != 0 && errno == ESPIPE) { - int i; - for (i = 0; i < count; i++) { - fgetc(src_stream); + if ((lseek(src_fd, amount, SEEK_CUR) == -1) && (errno == ESPIPE)) { + unsigned int i; + for (i = 0; i < amount; i++) { + xread_char(src_fd); } } - return; } - diff --git a/archival/libunarchive/unpack_ar_archive.c b/archival/libunarchive/unpack_ar_archive.c new file mode 100644 index 000000000..923b8a068 --- /dev/null +++ b/archival/libunarchive/unpack_ar_archive.c @@ -0,0 +1,34 @@ +/* + * 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 Library 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 <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "unarchive.h" +#include "busybox.h" + +extern void unpack_ar_archive(archive_handle_t *ar_archive) +{ + char magic[7]; + + xread_all(ar_archive->src_fd, magic, 7); + if (strncmp(magic, "!<arch>", 7) != 0) { + error_msg_and_die("Invalid ar magic"); + } + ar_archive->offset += 7; + + while (get_header_ar(ar_archive) == EXIT_SUCCESS); +} diff --git a/archival/libunarchive/unzip.c b/archival/libunarchive/unzip.c index d84067068..20bf88f55 100644 --- a/archival/libunarchive/unzip.c +++ b/archival/libunarchive/unzip.c @@ -65,114 +65,126 @@ static char *license_msg[] = { #include <signal.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> +#include <fcntl.h> #include "config.h" -#include "libbb.h" +#include "busybox.h" +#include "unarchive.h" -#ifdef CONFIG_FEATURE_UNCOMPRESS -int uncompress(FILE * in, FILE * out); -#endif - -static FILE *in_file, *out_file; +typedef struct huft_s { + unsigned char e; /* number of extra bits or operation */ + unsigned char b; /* number of bits in this code or subcode */ + union { + unsigned short n; /* literal, length base, or distance base */ + struct huft_s *t; /* pointer to next level of table */ + } v; +} huft_t; -/* these are freed by gz_close */ -static unsigned char *window; -static unsigned long *crc_table; +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 */ -static unsigned long crc; /* shift register contents */ +/* 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; -/* Return codes from gzip */ -#define ERROR 1 +/* gunzip_window size--must be a power of two, and + * at least 32K for zip's deflate method */ +static const int gunzip_wsize = 0x8000; -/* - * window size--must be a power of two, and - * at least 32K for zip's deflate method - */ -#define WSIZE 0x8000 +static unsigned char *gunzip_window; +static unsigned int *gunzip_crc_table; +unsigned int gunzip_crc; /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ #define BMAX 16 /* maximum bit length of any code (16 for explode) */ #define N_MAX 288 /* maximum number of codes in any set */ -static long bytes_out; /* number of output bytes */ -static unsigned long outcnt; /* bytes in output buffer */ - -static unsigned hufts; /* track memory usage */ -static unsigned long bb; /* bit buffer */ -static unsigned bk; /* bits in bit buffer */ - -typedef struct huft_s { - unsigned char e; /* number of extra bits or operation */ - unsigned char b; /* number of bits in this code or subcode */ - union { - unsigned short n; /* literal, length base, or distance base */ - struct huft_s *t; /* pointer to next level of table */ - } v; -} huft_t; +static unsigned int gunzip_hufts; /* track memory usage */ +static unsigned int gunzip_bb; /* bit buffer */ +static unsigned char gunzip_bk; /* bits in bit buffer */ static const unsigned short mask_bits[] = { - 0x0000, - 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; -/* static int error_number = 0; */ -/* ======================================================================== - * Signal and error handler. - */ +/* Copy lengths for literal codes 257..285 */ +static const unsigned short cplens[] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +}; + +/* note: see note #13 above about the 258 in this list. */ +/* Extra bits for literal codes 257..285 */ +static const unsigned char cplext[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 5, 0, 99, 99 +}; /* 99==invalid */ + +/* Copy offsets for distance codes 0..29 */ +static const unsigned short cpdist[] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 +}; + +/* Extra bits for distance codes */ +static const unsigned char cpdext[] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; + +/* Tables for deflate from PKZIP's appnote.txt. */ +/* Order of the bit length code lengths */ +static const unsigned char border[] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +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; + *current += 8; + } + + return(bitbuffer); +} static void abort_gzip(void) { error_msg("gzip aborted\n"); - exit(ERROR); + exit(-1); } -static void make_crc_table(void) +static void make_gunzip_crc_table(void) { - const unsigned long poly = 0xedb88320; /* polynomial exclusive-or pattern */ + const unsigned int poly = 0xedb88320; /* polynomial exclusive-or pattern */ unsigned short i; /* counter for all possible eight bit values */ /* initial shift register value */ - crc = 0xffffffffL; - crc_table = (unsigned long *) malloc(256 * sizeof(unsigned long)); + gunzip_crc = 0xffffffffL; + gunzip_crc_table = (unsigned int *) malloc(256 * sizeof(unsigned int)); /* Compute and print table of CRC's, five per line */ for (i = 0; i < 256; i++) { - unsigned long table_entry; /* crc shift register */ - char k; /* byte being shifted into crc apparatus */ + unsigned int table_entry; /* crc shift register */ + unsigned char k; /* byte being shifted into crc apparatus */ table_entry = i; /* The idea to initialize the register with the byte instead of * zero was stolen from Haruhiko Okumura's ar002 */ for (k = 8; k; k--) { - table_entry = - table_entry & 1 ? (table_entry >> 1) ^ poly : table_entry >> - 1; - } - crc_table[i] = table_entry; + if (table_entry & 1) { + table_entry = (table_entry >> 1) ^ poly; + } else { + table_entry >>= 1; } -} - -/* =========================================================================== - * 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) -{ - int n; - - if (outcnt == 0) - return; - - for (n = 0; n < outcnt; n++) { - crc = crc_table[((int) crc ^ (window[n])) & 0xff] ^ (crc >> 8); } - - if (fwrite(window, 1, outcnt, out_file) != outcnt) { - error_msg_and_die("Couldnt write"); + gunzip_crc_table[i] = table_entry; } - bytes_out += (unsigned long) outcnt; - outcnt = 0; } /* @@ -183,7 +195,8 @@ static void flush_window(void) */ static int huft_free(huft_t * t) { - huft_t *p, *q; + huft_t *p; + huft_t *q; /* Go through linked list, freeing from the malloced (t[-1]) address. */ p = t; @@ -195,8 +208,6 @@ static int huft_free(huft_t * t) return 0; } -typedef unsigned char extra_bits_t; - /* Given a list of code lengths and a maximum table size, make a set of * tables to decode that set of codes. Return zero on success, one if * the given code set is incomplete (the tables are still built in this @@ -213,7 +224,7 @@ typedef unsigned char extra_bits_t; */ static int huft_build(unsigned int *b, const unsigned int n, const unsigned int s, const unsigned short *d, - const extra_bits_t * e, huft_t ** t, int *m) + const unsigned char *e, huft_t ** t, int *m) { unsigned a; /* counter for codes of length k */ unsigned c[BMAX + 1]; /* bit length count table */ @@ -251,26 +262,35 @@ static int huft_build(unsigned int *b, const unsigned int n, /* Find minimum and maximum length, bound *m by those */ l = *m; - for (j = 1; j <= BMAX; j++) - if (c[j]) + for (j = 1; j <= BMAX; j++) { + if (c[j]) { break; + } + } k = j; /* minimum code length */ - if ((unsigned) l < j) + if ((unsigned) l < j) { l = j; - for (i = BMAX; i; i--) - if (c[i]) + } + for (i = BMAX; i; i--) { + if (c[i]) { break; + } + } g = i; /* maximum code length */ - if ((unsigned) l > i) + if ((unsigned) l > i) { l = i; + } *m = l; /* Adjust last length count to fill out codes, if needed */ - for (y = 1 << j; j < i; j++, y <<= 1) - if ((y -= c[j]) < 0) + for (y = 1 << j; j < i; j++, y <<= 1) { + if ((y -= c[j]) < 0) { return 2; /* bad input: more codes than bits */ - if ((y -= c[i]) < 0) + } + } + if ((y -= c[i]) < 0) { return 2; + } c[i] += y; /* Generate starting offsets into the value table for each length */ @@ -285,8 +305,9 @@ static int huft_build(unsigned int *b, const unsigned int n, p = b; i = 0; do { - if ((j = *p++) != 0) + if ((j = *p++) != 0) { v[x[j]++] = i; + } } while (++i < n); /* Generate the Huffman codes and for each, make the table entries */ @@ -314,8 +335,9 @@ static int huft_build(unsigned int *b, const unsigned int n, f -= a + 1; /* deduct codes from patterns left */ xp = c + k; while (++j < z) { /* try smaller tables up to z bits */ - if ((f <<= 1) <= *++xp) + if ((f <<= 1) <= *++xp) { break; /* enough codes to use up j bits */ + } f -= *xp; /* else deduct codes from patterns */ } } @@ -324,7 +346,7 @@ static int huft_build(unsigned int *b, const unsigned int n, /* allocate and link in new table */ q = (huft_t *) xmalloc((z + 1) * sizeof(huft_t)); - hufts += z + 1; /* track memory usage */ + 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 */ @@ -342,9 +364,9 @@ static int huft_build(unsigned int *b, const unsigned int n, /* set up table entry in r */ r.b = (unsigned char) (k - w); - if (p >= v + n) + if (p >= v + n) { r.e = 99; /* out of values--invalid code */ - else if (*p < s) { + } else if (*p < s) { r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is end-of-block code */ r.v.n = (unsigned short) (*p); /* simple code is just the value */ p++; /* one compiler does not like *p++ */ @@ -355,12 +377,14 @@ static int huft_build(unsigned int *b, const unsigned int n, /* fill code-like entries with r */ f = 1 << (k - w); - for (j = i >> w; j < z; j += f) + for (j = i >> w; j < z; j += f) { q[j] = r; + } /* backwards increment the k-bit code i */ - for (j = 1 << (k - 1); i & j; j >>= 1) + for (j = 1 << (k - 1); i & j; j >>= 1) { i ^= j; + } i ^= j; /* backup over finished tables */ @@ -374,6 +398,25 @@ static int huft_build(unsigned int *b, const unsigned int n, 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. @@ -381,32 +424,26 @@ static int huft_build(unsigned int *b, const unsigned int n, * 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, int bl, int bd) +static int inflate_codes(huft_t * tl, huft_t * td, const unsigned int bl, const unsigned int bd) { - register unsigned long e; /* table entry flag/number of extra bits */ - unsigned long n, d; /* length and index for copy */ - unsigned long w; /* current window position */ + 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 ml, md; /* masks for bl and bd bits */ - register unsigned long b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - register int input_char; + 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 = bb; /* initialize bit buffer */ - k = bk; - w = outcnt; /* initialize window position */ + 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]; - for (;;) { /* do until end of block */ - while (k < (unsigned) bl) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + 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) { @@ -415,20 +452,16 @@ static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd) b >>= t->b; k -= t->b; e -= 16; - while (k < e) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, e); } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); b >>= t->b; k -= t->b; if (e == 16) { /* then it's a literal */ - window[w++] = (unsigned char) t->v.n; - if (w == WSIZE) { - outcnt = (w), flush_window(); + gunzip_window[w++] = (unsigned char) t->v.n; + if (w == gunzip_wsize) { + gunzip_outbuf_count = (w); + flush_gunzip_window(); w = 0; } } else { /* it's an EOB or a length */ @@ -439,24 +472,13 @@ static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd) } /* get length of block to copy */ - while (k < e) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, e); n = t->v.n + ((unsigned) b & mask_bits[e]); b >>= e; k -= e; /* decode distance of block to copy */ - while (k < (unsigned) bd) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } - + b = fill_bitbuffer(b, &k, bd); if ((e = (t = td + ((unsigned) b & md))->e) > 16) do { if (e == 99) @@ -464,23 +486,13 @@ static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd) b >>= t->b; k -= t->b; e -= 16; - while (k < e) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, e); } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); b >>= t->b; k -= t->b; - while (k < e) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, e); d = w - t->v.n - ((unsigned) b & mask_bits[e]); b >>= e; k -= e; @@ -489,60 +501,38 @@ static int inflate_codes(huft_t * tl, huft_t * td, int bl, int bd) do { n -= (e = (e = - WSIZE - ((d &= WSIZE - 1) > w ? d : w)) > n ? n : e); -#if !defined(NOMEMCPY) && !defined(DEBUG) + gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e); + /* copy to new buffer to prevent possible overwrite */ if (w - d >= e) { /* (this test assumes unsigned comparison) */ - memcpy(window + w, window + d, e); + memcpy(gunzip_window + w, gunzip_window + d, e); w += e; d += e; - } else /* do it slow to avoid memcpy() overlap */ -#endif /* !NOMEMCPY */ + } else { + /* do it slow to avoid memcpy() overlap */ + /* !NOMEMCPY */ do { - window[w++] = window[d++]; + gunzip_window[w++] = gunzip_window[d++]; } while (--e); - if (w == WSIZE) { - outcnt = (w), flush_window(); + } + if (w == gunzip_wsize) { + gunzip_outbuf_count = (w); + flush_gunzip_window(); w = 0; } + } while (n); } } /* restore the globals from the locals */ - outcnt = w; /* restore global window pointer */ - bb = b; /* restore global bit buffer */ - bk = k; + gunzip_outbuf_count = w; /* restore global gunzip_window pointer */ + gunzip_bb = b; /* restore global bit buffer */ + gunzip_bk = k; /* done */ return 0; } -static const unsigned short cplens[] = { /* Copy lengths for literal codes 257..285 */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -}; - -/* note: see note #13 above about the 258 in this list. */ -static const extra_bits_t cplext[] = { /* Extra bits for literal codes 257..285 */ - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 -}; /* 99==invalid */ -static const unsigned short cpdist[] = { /* Copy offsets for distance codes 0..29 */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577 -}; -static const extra_bits_t cpdext[] = { /* Extra bits for distance codes */ - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, - 12, 12, 13, 13 -}; - -/* Tables for deflate from PKZIP's appnote.txt. */ -static const extra_bits_t border[] = { /* Order of the bit length code lengths */ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 -}; - /* * decompress an inflated block * e: last block flag @@ -552,53 +542,43 @@ static const extra_bits_t border[] = { /* Order of the bit length code lengths * static int inflate_block(int *e) { unsigned t; /* block type */ - register unsigned long b; /* bit buffer */ - register unsigned k; /* number of bits in bit buffer */ - int input_char; + register unsigned int b; /* bit buffer */ + unsigned int k; /* number of bits in bit buffer */ /* make local bit buffer */ - b = bb; - k = bk; + + b = gunzip_bb; + k = gunzip_bk; /* read in last block bit */ - while (k < 1) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, 1); *e = (int) b & 1; b >>= 1; k -= 1; /* read in block type */ - while (k < 2) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b |= ((unsigned long)input_char) << k; - k += 8; - } + b = fill_bitbuffer(b, &k, 2); t = (unsigned) b & 3; b >>= 2; k -= 2; /* restore the global bit buffer */ - bb = b; - bk = k; + gunzip_bb = b; + gunzip_bk = k; /* inflate that block type */ switch (t) { case 0: /* Inflate stored */ { - unsigned long n; /* number of bytes in block */ - unsigned long w; /* current window position */ - register unsigned long b_stored; /* bit buffer */ - register unsigned long k_stored; /* number of bits in bit buffer */ + 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 = bb; /* initialize bit buffer */ - k_stored = bk; - w = outcnt; /* initialize window position */ + 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; @@ -606,21 +586,12 @@ static int inflate_block(int *e) k_stored -= n; /* get the length and its complement */ - while (k_stored < 16) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_stored |= ((unsigned long)input_char) << k_stored; - k_stored += 8; - } + b_stored = fill_bitbuffer(b_stored, &k_stored, 16); n = ((unsigned) b_stored & 0xffff); b_stored >>= 16; k_stored -= 16; - while (k_stored < 16) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_stored |= ((unsigned long)input_char) << k_stored; - k_stored += 8; - } + + b_stored = fill_bitbuffer(b_stored, &k_stored, 16); if (n != (unsigned) ((~b_stored) & 0xffff)) { return 1; /* error in compressed data */ } @@ -629,15 +600,11 @@ static int inflate_block(int *e) /* read and output the compressed data */ while (n--) { - while (k_stored < 8) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_stored |= ((unsigned long)input_char) << k_stored; - k_stored += 8; - } - window[w++] = (unsigned char) b_stored; - if (w == (unsigned long) WSIZE) { - outcnt = (w), flush_window(); + 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; @@ -645,9 +612,9 @@ static int inflate_block(int *e) } /* restore the globals from the locals */ - outcnt = w; /* restore global window pointer */ - bb = b_stored; /* restore global bit buffer */ - bk = k_stored; + gunzip_outbuf_count = w; /* restore global gunzip_window pointer */ + gunzip_bb = b_stored; /* restore global bit buffer */ + gunzip_bk = k_stored; return 0; } case 1: /* Inflate fixed @@ -659,8 +626,8 @@ static int inflate_block(int *e) int i; /* temporary variable */ huft_t *tl; /* literal/length code table */ huft_t *td; /* distance code table */ - int bl; /* lookup bits for tl */ - int bd; /* lookup bits for td */ + unsigned int bl; /* lookup bits for tl */ + unsigned int bd; /* lookup bits for td */ unsigned int l[288]; /* length list for huft_build */ /* set up literal table */ @@ -692,8 +659,9 @@ static int inflate_block(int *e) } /* decompress until an end-of-block code */ - if (inflate_codes(tl, td, bl, bd)) + if (inflate_codes(tl, td, bl, bd)) { return 1; + } /* free the decoding tables, return */ huft_free(tl); @@ -705,53 +673,41 @@ static int inflate_block(int *e) const int dbits = 6; /* bits in base distance lookup table */ const int lbits = 9; /* bits in base literal/length lookup table */ - int i; /* temporary variables */ - unsigned j; - unsigned l; /* last length */ - unsigned m; /* mask for bit lengths table */ - unsigned n; /* number of lengths to get */ huft_t *tl; /* literal/length code table */ huft_t *td; /* distance code table */ - int bl; /* lookup bits for tl */ - int bd; /* lookup bits for td */ - unsigned nb; /* number of bit length codes */ - unsigned nl; /* number of literal/length codes */ - unsigned nd; /* number of distance codes */ - - unsigned ll[286 + 30]; /* literal/length and distance code lengths */ - register unsigned long b_dynamic; /* bit buffer */ - register unsigned k_dynamic; /* number of bits in bit buffer */ + unsigned int i; /* temporary variables */ + unsigned int j; + unsigned int l; /* last length */ + unsigned int m; /* mask for bit lengths table */ + unsigned int n; /* number of lengths to get */ + unsigned int bl; /* lookup bits for tl */ + unsigned int bd; /* lookup bits for td */ + unsigned int nb; /* number of bit length codes */ + unsigned int nl; /* number of literal/length codes */ + unsigned int nd; /* number of distance codes */ + + unsigned int ll[286 + 30]; /* literal/length and distance code lengths */ + unsigned int b_dynamic; /* bit buffer */ + unsigned int k_dynamic; /* number of bits in bit buffer */ /* make local bit buffer */ - b_dynamic = bb; - k_dynamic = bk; + b_dynamic = gunzip_bb; + k_dynamic = gunzip_bk; /* read in table lengths */ - while (k_dynamic < 5) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - nl = 257 + ((unsigned) b_dynamic & 0x1f); /* number of literal/length codes */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5); + nl = 257 + ((unsigned int) b_dynamic & 0x1f); /* number of literal/length codes */ + b_dynamic >>= 5; k_dynamic -= 5; - while (k_dynamic < 5) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - nd = 1 + ((unsigned) b_dynamic & 0x1f); /* number of distance codes */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5); + nd = 1 + ((unsigned int) b_dynamic & 0x1f); /* number of distance codes */ + b_dynamic >>= 5; k_dynamic -= 5; - while (k_dynamic < 4) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - nb = 4 + ((unsigned) b_dynamic & 0xf); /* number of bit length codes */ + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4); + nb = 4 + ((unsigned int) b_dynamic & 0xf); /* number of bit length codes */ + b_dynamic >>= 4; k_dynamic -= 4; if (nl > 286 || nd > 30) { @@ -760,13 +716,8 @@ static int inflate_block(int *e) /* read in bit-length-code lengths */ for (j = 0; j < nb; j++) { - while (k_dynamic < 3) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - ll[border[j]] = (unsigned) b_dynamic & 7; + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3); + ll[border[j]] = (unsigned int) b_dynamic & 7; b_dynamic >>= 3; k_dynamic -= 3; } @@ -776,7 +727,8 @@ static int inflate_block(int *e) /* build decoding table for trees--single level, 7 bit lookup */ bl = 7; - if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) { + i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl); + if (i != 0) { if (i == 1) { huft_free(tl); } @@ -787,46 +739,31 @@ static int inflate_block(int *e) n = nl + nd; m = mask_bits[bl]; i = l = 0; - while ((unsigned) i < n) { - while (k_dynamic < (unsigned) bl) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - j = (td = tl + ((unsigned) b_dynamic & m))->b; + while ((unsigned int) i < n) { + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl); + j = (td = tl + ((unsigned int) b_dynamic & m))->b; b_dynamic >>= j; k_dynamic -= j; j = td->v.n; if (j < 16) { /* length of code in bits (0..15) */ ll[i++] = l = j; /* save last length in l */ } else if (j == 16) { /* repeat last length 3 to 6 times */ - while (k_dynamic < 2) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - j = 3 + ((unsigned) b_dynamic & 3); + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2); + j = 3 + ((unsigned int) b_dynamic & 3); b_dynamic >>= 2; k_dynamic -= 2; - if ((unsigned) i + j > n) { + if ((unsigned int) i + j > n) { return 1; } while (j--) { ll[i++] = l; } } else if (j == 17) { /* 3 to 10 zero length codes */ - while (k_dynamic < 3) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - j = 3 + ((unsigned) b_dynamic & 7); + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3); + j = 3 + ((unsigned int) b_dynamic & 7); b_dynamic >>= 3; k_dynamic -= 3; - if ((unsigned) i + j > n) { + if ((unsigned int) i + j > n) { return 1; } while (j--) { @@ -834,16 +771,11 @@ static int inflate_block(int *e) } l = 0; } else { /* j == 18: 11 to 138 zero length codes */ - while (k_dynamic < 7) { - input_char = fgetc(in_file); - if (input_char == EOF) return 1; - b_dynamic |= ((unsigned long)input_char) << k_dynamic; - k_dynamic += 8; - } - j = 11 + ((unsigned) b_dynamic & 0x7f); + b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7); + j = 11 + ((unsigned int) b_dynamic & 0x7f); b_dynamic >>= 7; k_dynamic -= 7; - if ((unsigned) i + j > n) { + if ((unsigned int) i + j > n) { return 1; } while (j--) { @@ -857,22 +789,24 @@ static int inflate_block(int *e) huft_free(tl); /* restore the global bit buffer */ - bb = b_dynamic; - bk = k_dynamic; + gunzip_bb = b_dynamic; + gunzip_bk = k_dynamic; /* build the decoding tables for literal/length and distance codes */ bl = lbits; + if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) { if (i == 1) { - error_msg("Incomplete literal tree"); + error_msg_and_die("Incomplete literal tree"); huft_free(tl); } return i; /* incomplete code set */ } + bd = dbits; if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) { if (i == 1) { - error_msg("incomplete distance tree"); + error_msg_and_die("incomplete distance tree"); huft_free(td); } huft_free(tl); @@ -880,8 +814,9 @@ static int inflate_block(int *e) } /* decompress until an end-of-block code */ - if (inflate_codes(tl, td, bl, bd)) + if (inflate_codes(tl, td, bl, bd)) { return 1; + } /* free the decoding tables, return */ huft_free(tl); @@ -890,6 +825,7 @@ static int inflate_block(int *e) } default: /* bad block type */ + error_msg("bad block type %d\n", t); return 2; } } @@ -897,72 +833,24 @@ static int inflate_block(int *e) /* * decompress an inflated entry * - * GLOBAL VARIABLES: outcnt, bk, bb, hufts, inptr + * GLOBAL VARIABLES: gunzip_outbuf_count, bk, gunzip_bb, hufts, inptr */ -extern int inflate(FILE * in, FILE * out) +extern int inflate(int in, int out) { + typedef void (*sig_type) (int); int e; /* last block flag */ int r; /* result code */ unsigned h = 0; /* maximum struct huft's malloc'ed */ - /* initialize window, bit buffer */ - outcnt = 0; - 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; + gunzip_window = xmalloc(0x8000); + gunzip_outbuf_count = 0; + gunzip_bytes_out = 0; - /* Create the crc table */ - make_crc_table(); + gunzip_src_fd = in; + gunzip_dst_fd = out; - /* decompress until the last block */ - do { - hufts = 0; - if ((r = inflate_block(&e)) != 0) { - return r; - } - if (hufts > h) { - h = hufts; - } - } while (!e); - - /* Undo too much lookahead. The next read will be byte aligned so we - * can discard unused bits in the last meaningful byte. - */ - while (bk >= 8) { - bk -= 8; - ungetc((bb << bk), in_file); - } - - /* flush out window */ - flush_window(); - free(window); - free(crc_table); - - /* return success */ - return 0; -} - -/* =========================================================================== - * 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. - * The magic header has already been checked. The output buffer is cleared. - * in, out: input and output file descriptors - */ -extern int unzip(FILE * l_in_file, FILE * l_out_file) -{ - unsigned char buf[8]; /* extended local header */ - unsigned char flags; /* compression flags */ - typedef void (*sig_type) (int); - unsigned short i; - unsigned char magic[2]; + gunzip_in_buffer = malloc(8); if (signal(SIGINT, SIG_IGN) != SIG_IGN) { (void) signal(SIGINT, (sig_type) abort_gzip); @@ -973,97 +861,44 @@ extern int unzip(FILE * l_in_file, FILE * l_out_file) } #endif - magic[0] = fgetc(l_in_file); - magic[1] = fgetc(l_in_file); - -#ifdef CONFIG_FEATURE_UNCOMPRESS - /* Magic header for compress files, 1F 9d = \037\235 */ - if ((magic[0] == 0x1F) && (magic[1] == 0x9d)) { - return uncompress(l_in_file, l_out_file); - } -#endif - - /* Magic header for gzip files, 1F 8B = \037\213 */ - if ((magic[0] != 0x1F) || (magic[1] != 0x8b)) { - error_msg("Invalid gzip magic"); - return EXIT_FAILURE; - } - - /* Check the compression method */ - if (fgetc(l_in_file) != 8) /* also catches EOF */ { - error_msg("Unknown compression method"); - return (-1); - } - - flags = (unsigned char) fgetc(l_in_file); - - /* Ignore time stamp(4), extra flags(1), OS type(1) */ - for (i = 0; i < 6; i++) { - fgetc(l_in_file); - } + /* initialize gunzip_window, bit buffer */ + gunzip_bk = 0; + gunzip_bb = 0; - if (flags & 0x04) { - /* bit 2 set: extra field present */ - const unsigned short extra = - fgetc(l_in_file) + (fgetc(l_in_file) << 8); - if (feof(in_file)) return 1; - for (i = 0; i < extra; i++) { - fgetc(l_in_file); - } - } - - /* Discard original name if any */ - if (flags & 0x08) { - /* bit 3 set: original file name present */ - while (fgetc(l_in_file) != 0 && !feof(l_in_file)); /* null */ - } + /* Create the crc table */ + make_gunzip_crc_table(); - /* Discard file comment if any */ - if (flags & 0x10) { - /* bit 4 set: file comment present */ - while (fgetc(l_in_file) != 0 && !feof(l_in_file)); /* null */ + /* 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; } - - /* Decompress */ - if (inflate(l_in_file, l_out_file) != 0) { - error_msg("invalid compressed data--format violated"); + if (gunzip_hufts > h) { + h = gunzip_hufts; } + } while (!e); - /* Get the crc and original length - * crc32 (see algorithm.doc) - * uncompressed input size modulo 2^32 - */ - 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)) { - error_msg("invalid compressed data--crc error"); - } - /* Validate decompression - size */ - if (((buf[4] | (buf[5] << 8)) | ((buf[6] | (buf[7] << 8)) << 16)) != - (unsigned long) bytes_out) { - error_msg("invalid compressed data--length error"); + /* write any buffered uncompressed data */ + flush_gunzip_window(); + free(gunzip_window); + + /* Cleanup */ + 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++; + gunzip_bb >>= 8; + gunzip_bk -= 8; } + /* return success */ return 0; } - -/* - * This needs access to global variables window and crc_table, so its not in its own file. - */ -extern void gz_close(int gunzip_pid) -{ - if (kill(gunzip_pid, SIGTERM) == -1) { - error_msg_and_die - ("*** Couldnt kill old gunzip process *** aborting"); - } - - if (waitpid(gunzip_pid, NULL, 0) == -1) { - printf("Couldnt wait ?"); - } - - free(window); - free(crc_table); -} diff --git a/archival/rpm2cpio.c b/archival/rpm2cpio.c index 22051dabf..e1e93988f 100644 --- a/archival/rpm2cpio.c +++ b/archival/rpm2cpio.c @@ -18,10 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include "busybox.h" +#include <sys/types.h> #include <netinet/in.h> /* For ntohl & htonl function */ +#include <fcntl.h> +#include <unistd.h> #include <string.h> +#include "busybox.h" +#include "unarchive.h" #define RPM_MAGIC "\355\253\356\333" #define RPM_HEADER_MAGIC "\216\255\350" @@ -45,46 +48,54 @@ struct rpm_header { u_int32_t size; /* Size of store (4 bytes) */ }; -void skip_header(FILE *rpmfile) +void skip_header(int rpm_fd) { struct rpm_header header; - fread(&header, sizeof(struct rpm_header), 1, rpmfile); - if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */ - if (header.version != 1) error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */ + xread_all(rpm_fd, &header, sizeof(struct rpm_header)); + if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) { + error_msg_and_die("Invalid RPM header magic"); /* Invalid magic */ + } + if (header.version != 1) { + error_msg_and_die("Unsupported RPM header version"); /* This program only supports v1 headers */ + } header.entries = ntohl(header.entries); header.size = ntohl(header.size); - fseek (rpmfile, 16 * header.entries, SEEK_CUR); /* Seek past index entries */ - fseek (rpmfile, header.size, SEEK_CUR); /* Seek past store */ + lseek (rpm_fd, 16 * header.entries, SEEK_CUR); /* Seek past index entries */ + lseek (rpm_fd, header.size, SEEK_CUR); /* Seek past store */ } /* No getopt required */ extern int rpm2cpio_main(int argc, char **argv) { struct rpm_lead lead; - int gunzip_pid; - FILE *rpmfile, *cpiofile; + int rpm_fd; if (argc == 1) { - rpmfile = stdin; + rpm_fd = fileno(stdin); } else { - rpmfile = xfopen(argv[1], "r"); - /* set the buffer size */ - setvbuf(rpmfile, NULL, _IOFBF, 0x8000); + rpm_fd = xopen(argv[1], O_RDONLY); + } + + xread_all(rpm_fd, &lead, sizeof(struct rpm_lead)); + if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) { + error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */ } - fread (&lead, sizeof(struct rpm_lead), 1, rpmfile); - if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) error_msg_and_die("Invalid RPM magic"); /* Just check the magic, the rest is irrelevant */ /* Skip the signature header */ - skip_header(rpmfile); - fseek(rpmfile, (8 - (ftell(rpmfile) % 8)) % 8, SEEK_CUR); /* Pad to 8 byte boundary */ + skip_header(rpm_fd); + data_align(rpm_fd, lseek(rpm_fd, 0, SEEK_CUR), 8); + /* Skip the main header */ - skip_header(rpmfile); + skip_header(rpm_fd); + + check_header_gzip(rpm_fd); + if (inflate(rpm_fd, fileno(stdout)) != 0) { + error_msg("Error inflating"); + } + check_trailer_gzip(rpm_fd); - cpiofile = gz_open(rpmfile, &gunzip_pid); + close(rpm_fd); - copyfd(fileno(cpiofile), fileno(stdout)); - gz_close(gunzip_pid); - fclose(rpmfile); return 0; } diff --git a/archival/tar.c b/archival/tar.c index 2ab022954..6ef698edf 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -115,7 +115,7 @@ struct TarBallInfo { tarball lives, so we can avoid trying to include the tarball into itself */ int verboseFlag; /* Whether to print extra stuff or not */ - char **excludeList; /* List of files to not include */ + const llist_t *excludeList; /* List of files to not include */ HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ }; @@ -325,16 +325,15 @@ static inline int writeTarHeader(struct TarBallInfo *tbInfo, } # if defined CONFIG_FEATURE_TAR_EXCLUDE -static inline int exclude_file(char **excluded_files, const char *file) +static inline int exclude_file(const llist_t *excluded_files, const char *file) { - int i; - - if (excluded_files == NULL) + if (excluded_files == NULL) { return 0; + } - for (i = 0; excluded_files[i] != NULL; i++) { - if (excluded_files[i][0] == '/') { - if (fnmatch(excluded_files[i], file, + while (excluded_files) { + if (excluded_files->data[0] == '/') { + if (fnmatch(excluded_files->data, file, FNM_PATHNAME | FNM_LEADING_DIR) == 0) return 1; } else { @@ -342,11 +341,12 @@ static inline int exclude_file(char **excluded_files, const char *file) for (p = file; p[0] != '\0'; p++) { if ((p == file || p[-1] == '/') && p[0] != '/' && - fnmatch(excluded_files[i], p, + fnmatch(excluded_files->data, p, FNM_PATHNAME | FNM_LEADING_DIR) == 0) return 1; } } + excluded_files = excluded_files->link; } return 0; @@ -455,8 +455,8 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, return (TRUE); } -static inline int writeTarFile(const char *tarName, int verboseFlag, - char **argv, char **excludeList, int gzip) +static inline int writeTarFile(const char *tarName, const int verboseFlag, + const llist_t *include, const llist_t *exclude, const int gzip) { #ifdef CONFIG_FEATURE_TAR_GZIP int gzipDataPipe[2] = { -1, -1 }; @@ -471,8 +471,9 @@ static inline int writeTarFile(const char *tarName, int verboseFlag, tbInfo.hlInfoHead = NULL; /* Make sure there is at least one file to tar up. */ - if (*argv == NULL) + if (include == NULL) { error_msg_and_die("Cowardly refusing to create an empty archive"); + } /* Open the tar file for writing. */ if (tarName == NULL) { @@ -544,15 +545,16 @@ static inline int writeTarFile(const char *tarName, int verboseFlag, } #endif - tbInfo.excludeList = excludeList; + tbInfo.excludeList = exclude; /* Read the directory/files and iterate over them one at a time */ - while (*argv != NULL) { - if (!recursive_action(*argv++, TRUE, FALSE, FALSE, + while (include) { + if (!recursive_action(include->data, TRUE, FALSE, FALSE, writeFileToTarball, writeFileToTarball, (void *) &tbInfo)) { errorFlag = TRUE; } + include = include->link; } /* Write two empty blocks to the end of the archive */ for (size = 0; size < (2 * TAR_BLOCK_SIZE); size++) { @@ -582,64 +584,48 @@ static inline int writeTarFile(const char *tarName, int verboseFlag, } #endif /* tar_create */ -void append_file_to_list(const char *new_name, char ***list, int *list_count) -{ - *list = realloc(*list, sizeof(char *) * (*list_count + 2)); - (*list)[*list_count] = xstrdup(new_name); - (*list_count)++; - (*list)[*list_count] = NULL; -} - -void append_file_list_to_list(char *filename, char ***name_list, - int *num_of_entries) +#ifdef CONFIG_FEATURE_TAR_EXCLUDE +static const llist_t *append_file_list_to_list(const char *filename, const llist_t *list) { - FILE *src_stream; - char *line; - - src_stream = xfopen(filename, "r"); - while ((line = get_line_from_file(src_stream)) != NULL) { + FILE *src_stream = xfopen(filename, "r"); + while(1) { + char *line = get_line_from_file(src_stream); + if (line == NULL) { + break; + } chomp(line); - append_file_to_list(line, name_list, num_of_entries); + list = add_to_list(list, line); free(line); } fclose(src_stream); + + return (list); } +#endif int tar_main(int argc, char **argv) { - enum untar_funct_e { - /* This is optional */ - untar_unzip = 1, - /* Require one and only one of these */ - untar_list = 2, - untar_create = 4, - untar_extract = 8 - }; - - FILE *src_stream = NULL; - FILE *uncompressed_stream = NULL; - char **include_list = NULL; - char **exclude_list = NULL; - char *src_filename = NULL; - char *dst_prefix = NULL; +#ifdef CONFIG_FEATURE_TAR_GZIP + char (*get_header_ptr)(archive_handle_t *) = get_header_tar; +#endif + archive_handle_t *tar_handle; int opt; - unsigned short untar_funct = 0; - unsigned short untar_funct_required = 0; - unsigned short extract_function = 0; - int include_list_count = 0; + char *base_dir = NULL; -#ifdef CONFIG_FEATURE_TAR_EXCLUDE - int exclude_list_count = 0; -#endif -#ifdef CONFIG_FEATURE_TAR_GZIP - int gunzip_pid; - int gz_fd = 0; +#ifdef CONFIG_FEATURE_TAR_CREATE + char *src_filename = NULL; + unsigned char tar_create = FALSE; #endif if (argc < 2) { show_usage(); } + /* Initialise default values */ + tar_handle = init_handle(); + tar_handle->src_fd = fileno(stdin); + tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS; + /* Prepend '-' to the first argument if required */ if (argv[1][0] != '-') { char *tmp = xmalloc(strlen(argv[1]) + 2); @@ -648,61 +634,69 @@ int tar_main(int argc, char **argv) strcpy(tmp + 1, argv[1]); argv[1] = tmp; } - while ((opt = getopt(argc, argv, "ctxT:X:C:f:Opvz")) != -1) { switch (opt) { - /* One and only one of these is required */ +#ifdef CONFIG_FEATURE_TAR_CREATE case 'c': - untar_funct_required |= untar_create; + tar_create = TRUE; break; +#endif case 't': - untar_funct_required |= untar_list; - extract_function |= extract_list | extract_unconditional; + if ((tar_handle->action_header == header_list) || + (tar_handle->action_header == header_verbose_list)) { + tar_handle->action_header = header_verbose_list; + } else { + tar_handle->action_header = header_list; + } break; case 'x': - untar_funct_required |= untar_extract; - extract_function |= - (extract_all_to_fs | extract_unconditional | - extract_create_leading_dirs); + tar_handle->action_data = data_extract_all; break; /* These are optional */ /* Exclude or Include files listed in <filename> */ #ifdef CONFIG_FEATURE_TAR_EXCLUDE case 'X': - append_file_list_to_list(optarg, &exclude_list, - &exclude_list_count); + tar_handle->reject = + append_file_list_to_list(optarg, tar_handle->reject); break; #endif case 'T': /* by default a list is an include list */ - append_file_list_to_list(optarg, &include_list, - &include_list_count); break; - case 'C': /* Change to dir <optarg> */ - /* Make sure dst_prefix ends in a '/' */ - dst_prefix = concat_path_file(optarg, "/"); + base_dir = optarg; break; case 'f': /* archive filename */ - if (strcmp(optarg, "-") == 0) { - src_filename = NULL; - } else { - src_filename = xstrdup(optarg); - } +#ifdef CONFIG_FEATURE_TAR_CREATE + src_filename = optarg; +#endif + tar_handle->src_fd = xopen(optarg, O_RDONLY); break; - case 'O': - extract_function |= extract_to_stdout; + case 'O': /* To stdout */ + tar_handle->action_data = data_extract_to_stdout; break; case 'p': + tar_handle->flags |= ARCHIVE_PRESERVE_DATE; break; case 'v': - extract_function |= extract_verbose_list; + if ((tar_handle->action_header == header_list) || + (tar_handle->action_header == header_verbose_list)) { + tar_handle->action_header = header_verbose_list; + } else { + tar_handle->action_header = header_list; + } break; #ifdef CONFIG_FEATURE_TAR_GZIP case 'z': - untar_funct |= untar_unzip; + get_header_ptr = get_header_tar_gz; + break; +#endif +#ifdef CONFIG_FEATURE_TAR_BZIP2 + /* Not enabled yet */ + case 'j': + archive_handle->archive_action = bunzip2; break; #endif default: @@ -710,77 +704,54 @@ int tar_main(int argc, char **argv) } } - /* Make sure the valid arguments were passed */ - if (untar_funct_required == 0) { - error_msg_and_die("You must specify one of the `-ctx' options"); - } - if ((untar_funct_required != untar_create) && - (untar_funct_required != untar_extract) && - (untar_funct_required != untar_list)) { - error_msg_and_die("You may not specify more than one `ctx' option."); - } - untar_funct |= untar_funct_required; - /* Setup an array of filenames to work with */ + /* TODO: This is the same as in ar, seperate function ? */ while (optind < argc) { - append_file_to_list(argv[optind], &include_list, &include_list_count); + char absolute_path[PATH_MAX]; + + realpath(argv[optind], absolute_path); + tar_handle->accept = add_to_list(tar_handle->accept, absolute_path); optind++; - } - if (extract_function & (extract_list | extract_all_to_fs)) { - if (dst_prefix == NULL) { - dst_prefix = xstrdup("./"); +#ifdef CONFIG_FEATURE_TAR_EXCLUDE + if (tar_handle->reject) { + tar_handle->filter = filter_accept_reject_list; + } else +#endif + tar_handle->filter = filter_accept_list; } - /* Setup the source of the tar data */ - if (src_filename != NULL) { - src_stream = xfopen(src_filename, "r"); - } else { - src_stream = stdin; + if ((base_dir) && (chdir(base_dir))) { + perror_msg_and_die("Couldnt chdir"); } -#ifdef CONFIG_FEATURE_TAR_GZIP - /* Get a binary tree of all the tar file headers */ - if (untar_funct & untar_unzip) { - uncompressed_stream = gz_open(src_stream, &gunzip_pid); - } else -#endif /* CONFIG_FEATURE_TAR_GZIP */ - uncompressed_stream = src_stream; - /* extract or list archive */ - unarchive(uncompressed_stream, stdout, &get_header_tar, - extract_function, dst_prefix, include_list, exclude_list); - fclose(uncompressed_stream); - } #ifdef CONFIG_FEATURE_TAR_CREATE /* create an archive */ - else if (untar_funct & untar_create) { + if (tar_create == TRUE) { int verboseFlag = FALSE; int gzipFlag = FALSE; -#ifdef CONFIG_FEATURE_TAR_GZIP - if (untar_funct & untar_unzip) +# ifdef CONFIG_FEATURE_TAR_GZIP + if (get_header_ptr == get_header_tar_gz) { gzipFlag = TRUE; - -#endif /* CONFIG_FEATURE_TAR_GZIP */ - if (extract_function & extract_verbose_list) + } +# endif + if (tar_handle->action_header == header_verbose_list) { verboseFlag = TRUE; - - writeTarFile(src_filename, verboseFlag, include_list, exclude_list, - gzipFlag); } -#endif /* CONFIG_FEATURE_TAR_CREATE */ - - /* Cleanups */ + writeTarFile(src_filename, verboseFlag, tar_handle->accept, + tar_handle->reject, gzipFlag); + } else +#endif #ifdef CONFIG_FEATURE_TAR_GZIP - if (!(untar_funct & untar_create) && (untar_funct & untar_unzip)) { - fclose(src_stream); - close(gz_fd); - gz_close(gunzip_pid); - } -#endif /* CONFIG_FEATURE_TAR_GZIP */ + if (get_header_ptr == get_header_tar_gz) { + get_header_tar_gz(tar_handle); + } else +#endif + while (get_header_tar(tar_handle) == EXIT_SUCCESS); + #ifdef CONFIG_FEATURE_CLEAN_UP - if (src_filename) { - free(src_filename); - } + close(tar_handle->src_fd); #endif - return (EXIT_SUCCESS); + + return(EXIT_SUCCESS); } diff --git a/archival/unzip.c b/archival/unzip.c index ae0d7c1dc..5a22d242a 100644 --- a/archival/unzip.c +++ b/archival/unzip.c @@ -20,6 +20,10 @@ * */ +/* For reference to format see http://www.pkware.com/support/appnote.html */ + +/* TODO Endian issues, exclude, should we accept input from stdin ? */ + #include <fcntl.h> #include <getopt.h> #include <stdlib.h> @@ -28,67 +32,213 @@ #include "unarchive.h" #include "busybox.h" +#define ZIP_FILEHEADER_MAGIC 0x04034b50 +#define ZIP_CDS_MAGIC 0x02014b50 +#define ZIP_CDS_END_MAGIC 0x06054b50 +#define ZIP_DD_MAGIC 0x08074b50 + +extern unsigned int gunzip_crc; +extern unsigned int gunzip_bytes_out; + +static void header_list_unzip(const file_header_t *file_header) +{ + printf(" inflating: %s\n", file_header->name); +} + +static void header_verbose_list_unzip(const file_header_t *file_header) +{ + unsigned int dostime = (unsigned int) file_header->mtime; + + /* can printf arguments cut of the decade component ? */ + unsigned short year = 1980 + ((dostime & 0xfe000000) >> 25); + while (year >= 100) { + year -= 100; + } + + printf("%9u %02u-%02u-%02u %02u:%02u %s\n", + (unsigned int) file_header->size, + (dostime & 0x01e00000) >> 21, + (dostime & 0x001f0000) >> 16, + year, + (dostime & 0x0000f800) >> 11, + (dostime & 0x000007e0) >> 5, + file_header->name); +} + 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; + union { + unsigned char raw[26]; + struct { + unsigned short version; /* 0-1 */ + unsigned short flags; /* 2-3 */ + unsigned short method; /* 4-5 */ + unsigned short modtime; /* 6-7 */ + unsigned short moddate; /* 8-9 */ + unsigned int crc32 __attribute__ ((packed)); /* 10-13 */ + unsigned int cmpsize __attribute__ ((packed));; /* 14-17 */ + unsigned int ucmpsize __attribute__ ((packed));; /* 18-21 */ + unsigned short filename_len; /* 22-23 */ + unsigned short extra_len; /* 24-25 */ + } formated __attribute__ ((packed)); + } zip_header; + + archive_handle_t *archive_handle; + unsigned int total_size = 0; + unsigned int total_entries = 0; + char *base_dir = NULL; int opt = 0; - int num_of_entries = 0; - int exclude = 0; - char *outdir = "./"; - FILE *msgout = stdout; - while ((opt = getopt(argc, argv, "lnopqxd:")) != -1) { + /* Initialise */ + archive_handle = init_handle(); + archive_handle->action_data = NULL; + archive_handle->action_header = header_list_unzip; + + while ((opt = getopt(argc, argv, "lnopqd:")) != -1) { switch (opt) { - case 'l': - extract_function |= extract_verbose_list; - extract_function ^= extract_all_to_fs; + case 'l': /* list */ + archive_handle->action_header = header_verbose_list_unzip; + archive_handle->action_data = data_skip; break; - case 'n': + case 'n': /* never overwright existing files */ break; case 'o': - extract_function |= extract_unconditional; + archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL; break; - case 'p': - extract_function |= extract_to_stdout; - extract_function ^= extract_all_to_fs; - /* FALLTHROUGH */ - case 'q': - msgout = xfopen("/dev/null", "w"); + case 'p': /* extract files to stdout */ + archive_handle->action_data = data_extract_to_stdout; break; - case 'd': - outdir = xstrdup(optarg); - strcat(outdir, "/"); + case 'q': /* Extract files quietly */ + archive_handle->action_header = header_skip; break; - case 'x': - exclude = 1; + case 'd': /* Extract files to specified base directory*/ + base_dir = optarg; break; +#if 0 + case 'x': /* Exclude the specified files */ + archive_handle->filter = filter_accept_reject_list; + break; +#endif + default: + show_usage(); } } - if (optind == argc) { + if (argc == optind) { show_usage(); } - if (*argv[optind] == '-') src_stream = stdin; - else src_stream = xfopen(argv[optind++], "r"); + printf("Archive: %s\n", argv[optind]); + if (archive_handle->action_header == header_verbose_list_unzip) { + printf(" Length Date Time Name\n"); + printf(" -------- ---- ---- ----\n"); + } - while (optind < argc) { - if (exclude) { - exclude_names = xrealloc(exclude_names, sizeof(char *) * (num_of_entries + 2)); - exclude_names[num_of_entries] = xstrdup(argv[optind]); + if (*argv[optind] == '-') { + archive_handle->src_fd = fileno(stdin); } else { - extract_names = xrealloc(extract_names, sizeof(char *) * (num_of_entries + 2)); - extract_names[num_of_entries] = xstrdup(argv[optind]); + archive_handle->src_fd = xopen(argv[optind++], O_RDONLY); } - num_of_entries++; - if (exclude) exclude_names[num_of_entries] = NULL; - else extract_names[num_of_entries] = NULL; + + if ((base_dir) && (chdir(base_dir))) { + perror_msg_and_die("Couldnt chdir"); + } + + while (optind < argc) { + archive_handle->filter = filter_accept_list; + archive_handle->accept = add_to_list(archive_handle->accept, argv[optind]); optind++; } - unarchive(src_stream, msgout, &get_header_zip, extract_function, outdir, extract_names, exclude_names); - return EXIT_SUCCESS; + while (1) { + unsigned int magic; + int dst_fd; + + /* TODO Endian issues */ + xread_all(archive_handle->src_fd, &magic, 4); + archive_handle->offset += 4; + + if (magic == ZIP_CDS_MAGIC) { + break; + } + else if (magic != ZIP_FILEHEADER_MAGIC) { + error_msg_and_die("Invlaide zip magic"); + } + + /* Read the file header */ + xread_all(archive_handle->src_fd, zip_header.raw, 26); + archive_handle->offset += 26; + archive_handle->file_header->mode = S_IFREG | 0777; + + if (zip_header.formated.method != 8) { + error_msg_and_die("Unsupported compression method %d\n", zip_header.formated.method); + } + + /* Read filename */ + archive_handle->file_header->name = xmalloc(zip_header.formated.filename_len + 1); + xread_all(archive_handle->src_fd, archive_handle->file_header->name, zip_header.formated.filename_len); + archive_handle->offset += zip_header.formated.filename_len; + archive_handle->file_header->name[zip_header.formated.filename_len] = '\0'; + + /* Skip extra header bits */ + archive_handle->file_header->size = zip_header.formated.extra_len; + data_skip(archive_handle); + archive_handle->offset += zip_header.formated.extra_len; + + /* Handle directories */ + archive_handle->file_header->mode = S_IFREG | 0777; + if (last_char_is(archive_handle->file_header->name, '/')) { + archive_handle->file_header->mode ^= S_IFREG; + archive_handle->file_header->mode |= S_IFDIR; + } + + /* Data section */ + archive_handle->file_header->size = zip_header.formated.cmpsize; + if (archive_handle->action_data) { + archive_handle->action_data(archive_handle); + } else { + dst_fd = xopen(archive_handle->file_header->name, O_WRONLY | O_CREAT); + inflate(archive_handle->src_fd, dst_fd); + close(dst_fd); + chmod(archive_handle->file_header->name, archive_handle->file_header->mode); + + /* Validate decompression - crc */ + if (zip_header.formated.crc32 != (gunzip_crc ^ 0xffffffffL)) { + error_msg("Invalid compressed data--crc error"); + } + + /* Validate decompression - size */ + if (gunzip_bytes_out != zip_header.formated.ucmpsize) { + error_msg("Invalid compressed data--length error"); + } + } + + /* local file descriptor section */ + archive_handle->offset += zip_header.formated.cmpsize; + /* This ISNT unix time */ + archive_handle->file_header->mtime = zip_header.formated.modtime | (zip_header.formated.moddate << 16); + archive_handle->file_header->size = zip_header.formated.ucmpsize; + total_size += archive_handle->file_header->size; + total_entries++; + + archive_handle->action_header(archive_handle->file_header); + + /* Data descriptor section */ + if (zip_header.formated.flags & 4) { + /* skip over duplicate crc, compressed size and uncompressed size */ + unsigned short i; + for (i = 0; i != 12; i++) { + xread_char(archive_handle->src_fd); + } + archive_handle->offset += 12; + } + } + /* Central directory section */ + + if (archive_handle->action_header == header_verbose_list_unzip) { + printf(" -------- -------\n"); + printf("%9d %d files\n", total_size, total_entries); + } + + return(EXIT_SUCCESS); } |