unzip: survive lack of CDF on non-streaming zip files
No binary (build failed)
—
(log yok)
| @@ -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 */ | |