diff options
-rw-r--r-- | archival/libunarchive/get_header_zip.c | 155 |
1 files changed, 108 insertions, 47 deletions
diff --git a/archival/libunarchive/get_header_zip.c b/archival/libunarchive/get_header_zip.c index 84f2a54f2..5fa3a6c8c 100644 --- a/archival/libunarchive/get_header_zip.c +++ b/archival/libunarchive/get_header_zip.c @@ -19,92 +19,153 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "unarchive.h" #include "libbb.h" -#define ZIP_FILEHEADER_MAGIC 0x04034b50 -#define ZIP_CDS_MAGIC 0x02014b50 -#define ZIP_CDS_END_MAGIC 0x06054b50 -#define ZIP_DD_MAGIC 0x8074b50 +#define ZIP_FILEHEADER_MAGIC 0x04034b50 +#define ZIP_CDS_MAGIC 0x02014b50 +#define ZIP_CDS_END_MAGIC 0x06054b50 +#define ZIP_DD_MAGIC 0x08074b50 + + +enum { + zh_version, + zh_flags, + zh_method, + zh_modtime, + zh_moddate, + zh_crc32, + zh_cmpsize, + zh_ucmpsize, + zh_filename_len, + zh_extra_len +}; + +static const char fail_msg[] = "Invalid file or read error"; + +static int le_read_int(FILE *fp, int n) +{ + int r = 0; + int s = 1; + int c; + + archive_offset += n; + + do { + if ((c = fgetc(fp)) == EOF) { + error_msg_and_die(fail_msg); + } + r += s * c; + s <<= 8; + } while (--n); + + return r; +} + +static void read_header(FILE *fp, int *zh) +{ + static const char s[] = { 2, 2, 2, 2, 2, 4, 4, 4, 2, 2, 0 }; + int i = 0; + + do { + zh[i] = le_read_int(fp, s[i]); + } while (s[++i]); +} file_header_t *get_header_zip(FILE *zip_stream) { - struct { - short version __attribute__ ((packed)); - short flags __attribute__ ((packed)); - short method __attribute__ ((packed)); - short modtime __attribute__ ((packed)); - short moddate __attribute__ ((packed)); - int crc32 __attribute__ ((packed)); - int cmpsize __attribute__ ((packed)); - int ucmpsize __attribute__ ((packed)); - short filename_len __attribute__ ((packed)); - short extra_len __attribute__ ((packed)); - } zip_header; + int zip_header[10]; file_header_t *zip_entry = NULL; - int magic; - static int dd_ahead = 0; // If this is true, the we didn't know how long the last extraced file was + int magic, tmpmagic; + /* If dd_ahead is true, we didn't know the last extracted file's length. */ + static int dd_ahead = 0; - fread (&magic, 4, 1, zip_stream); - archive_offset += 4; + magic = le_read_int(zip_stream, 4); - if (feof(zip_stream)) return(NULL); -checkmagic: + checkmagic: switch (magic) { case ZIP_FILEHEADER_MAGIC: zip_entry = xcalloc(1, sizeof(file_header_t)); - fread (&zip_header, sizeof(zip_header), 1, zip_stream); - archive_offset += sizeof(zip_header); - if (!(zip_header.method == 8 || zip_header.method == 0)) { printf("Unsupported compression method %d\n", zip_header.method); return(NULL); } - zip_entry->name = calloc(zip_header.filename_len + 1, sizeof(char)); - fread (zip_entry->name, sizeof(char), zip_header.filename_len, zip_stream); - archive_offset += zip_header.filename_len; - seek_sub_file(zip_stream, zip_header.extra_len); - zip_entry->size = zip_header.cmpsize; - if (zip_header.method == 8) zip_entry->extract_func = &inflate; + + read_header(zip_stream, zip_header); + + if (zip_header[zh_method] == 8) { + zip_entry->extract_func = &inflate; + } else if (zip_header[zh_method] != 0) { + error_msg_and_die("Unsupported compression method %d\n", + zip_header[zh_method]); + } + + zip_entry->name = xmalloc(zip_header[zh_filename_len] + 1); + if (!fread(zip_entry->name, zip_header[zh_filename_len], 1, zip_stream)) { + error_msg_and_die(fail_msg); + } + archive_offset += zip_header[zh_filename_len]; + + seek_sub_file(zip_stream, zip_header[zh_extra_len]); + + zip_entry->size = zip_header[zh_cmpsize]; + zip_entry->mode = S_IFREG | 0777; - // Time/Date? - if (*(zip_entry->name + strlen(zip_entry->name) - 1) == '/') { // Files that end in a / are directories + + /* Time/Date? */ + + zip_entry->name[zip_header[zh_filename_len]] = 0; + if (*(zip_entry->name + zip_header[zh_filename_len] - 1) == '/') { + /* Files that end in a / are directories */ + /* Remove trailing / so unarchive doesn't get confused */ + *(zip_entry->name + zip_header[zh_filename_len] - 1) = '\0'; zip_entry->mode ^= S_IFREG; zip_entry->mode |= S_IFDIR; - *(zip_entry->name + strlen(zip_entry->name) - 1) = '\0'; // Remove trailing / so unarchive doesn't get confused } - //printf("cmpsize: %d, ucmpsize: %d, method: %d\n", zip_header.cmpsize, zip_header.ucmpsize, zip_header.method); - if (zip_header.flags & 0x8) { // crc32, and sizes are in the data description _after_ the file - if (zip_header.cmpsize == 0) dd_ahead = 1; // As we don't know how long this file it is difficult to skip! but it is compressed, so normally its ok - if (zip_header.ucmpsize != 0) dd_ahead = 2; // Humm... we would otherwise skip this twice - not good! + + if (zip_header[zh_flags] & 0x8) { + /* crc32, and sizes are in the data description _after_ the file */ + if (zip_header[zh_cmpsize] == 0) { + /* As we don't know how long this file it is difficult to skip! + * but it is compressed, so normally its ok */ + dd_ahead = 1; + } + if (zip_header[zh_ucmpsize] != 0) { + /* Humm... we would otherwise skip this twice - not good! */ + dd_ahead = 2; + } } break; + case ZIP_CDS_MAGIC: /* FALLTHRU */ case ZIP_CDS_END_MAGIC: return(NULL); break; + case ZIP_DD_MAGIC: { int cmpsize; seek_sub_file(zip_stream, 4); // Skip crc32 - fread(&cmpsize, 4, 1, zip_stream); - archive_offset += 4; + cmpsize = le_read_int(zip_stream, 4); if (dd_ahead == 1) archive_offset += cmpsize; seek_sub_file(zip_stream, 4); // Skip uncompressed size dd_ahead = 0; return (get_header_zip(zip_stream)); break; } + default: if (!dd_ahead) error_msg("Invalid magic (%#x): Trying to skip junk", magic); dd_ahead = 0; - while (!feof(zip_stream)) { - int tmpmagic; - tmpmagic = fgetc(zip_stream); + while ((tmpmagic = fgetc(zip_stream)) != EOF) { archive_offset++; magic = ((magic >> 8) & 0x00ffffff) | ((tmpmagic << 24) & 0xff000000); - if (magic == ZIP_FILEHEADER_MAGIC || magic == ZIP_CDS_MAGIC || magic == ZIP_CDS_END_MAGIC) goto checkmagic; + if (magic == ZIP_FILEHEADER_MAGIC + || magic == ZIP_CDS_MAGIC + || magic == ZIP_CDS_END_MAGIC) { + goto checkmagic; + } } - error_msg("End of archive reached: Bad archive"); - return(NULL); + error_msg_and_die(fail_msg); } - //if (archive_offset != ftell(zip_stream)) printf("Archive offset out of sync (%d,%d)\n", (int) archive_offset, (int) ftell(zip_stream)); + return(zip_entry); } |