diff options
Diffstat (limited to 'archival')
-rw-r--r-- | archival/unzip.c | 55 |
1 files changed, 39 insertions, 16 deletions
diff --git a/archival/unzip.c b/archival/unzip.c index c250d75..673e5fe 100644 --- a/archival/unzip.c +++ b/archival/unzip.c @@ -172,6 +172,9 @@ enum { zip_fd = 3 }; */ #define PEEK_FROM_END (64*1024) +/* This value means that we failed to find CDF */ +#define BAD_CDF_OFFSET ((uint32_t)0xffffffff) + /* NB: does not preserve file position! */ static uint32_t find_cdf_offset(void) { @@ -187,6 +190,7 @@ static uint32_t find_cdf_offset(void) xlseek(zip_fd, end, SEEK_SET); full_read(zip_fd, buf, PEEK_FROM_END); + cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; p = buf; while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { if (*p != 'P') { @@ -202,11 +206,17 @@ static uint32_t find_cdf_offset(void) /* we found CDE! */ memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); FIX_ENDIANNESS_CDE(cde_header); - free(buf); - return cde_header.formatted.cdf_offset; + /* + * I've seen .ZIP files with seemingly valid CDEs + * where cdf_offset points past EOF - ?? + * Ignore such CDEs: + */ + if (cde_header.formatted.cdf_offset < end + (p - buf)) + break; + cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; } - //free(buf); - bb_error_msg_and_die("can't find file table"); + free(buf); + return cde_header.formatted.cdf_offset; }; static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) @@ -218,13 +228,15 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) if (!cdf_offset) cdf_offset = find_cdf_offset(); - xlseek(zip_fd, cdf_offset + 4, SEEK_SET); - xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); - FIX_ENDIANNESS_CDF(*cdf_ptr); - cdf_offset += 4 + CDF_HEADER_LEN - + cdf_ptr->formatted.file_name_length - + cdf_ptr->formatted.extra_field_length - + cdf_ptr->formatted.file_comment_length; + if (cdf_offset != BAD_CDF_OFFSET) { + xlseek(zip_fd, cdf_offset + 4, SEEK_SET); + xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); + FIX_ENDIANNESS_CDF(*cdf_ptr); + cdf_offset += 4 + CDF_HEADER_LEN + + cdf_ptr->formatted.file_name_length + + cdf_ptr->formatted.extra_field_length + + cdf_ptr->formatted.file_comment_length; + } xlseek(zip_fd, org, SEEK_SET); return cdf_offset; @@ -233,8 +245,9 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) static void unzip_skip(off_t skip) { - if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) - bb_copyfd_exact_size(zip_fd, -1, skip); + if (skip != 0) + if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) + bb_copyfd_exact_size(zip_fd, -1, skip); } static void unzip_create_leading_dirs(const char *fn) @@ -535,21 +548,31 @@ int unzip_main(int argc, char **argv) bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); } - { + if (cdf_offset != BAD_CDF_OFFSET) { cdf_header_t cdf_header; cdf_offset = read_next_cdf(cdf_offset, &cdf_header); + /* + * Note: cdf_offset can become BAD_CDF_OFFSET after the above call. + */ if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { /* 0x0008 - streaming. [u]cmpsize can be reliably gotten - * only from Central Directory. See unzip_doc.txt */ + * only from Central Directory. See unzip_doc.txt + */ zip_header.formatted.crc32 = cdf_header.formatted.crc32; zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; } if ((cdf_header.formatted.version_made_by >> 8) == 3) { - /* this archive is created on Unix */ + /* This archive is created on Unix */ dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); } } + if (cdf_offset == BAD_CDF_OFFSET + && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) + ) { + /* If it's a streaming zip, we _require_ CDF */ + bb_error_msg_and_die("can't find file table"); + } #endif /* Read filename */ |