diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2013-07-21 02:31:08 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2013-07-21 02:31:08 +0200 |
| commit | 26cd90c7fd32f2ef252acb146f8b9152e55346ec (patch) | |
| tree | b5a2a93c13d44eb91c94979f0aa74815bd740b5a | |
| parent | 1390a010b60cbb5adad4e4c56a3613122b4298c6 (diff) | |
| download | busybox-w32-26cd90c7fd32f2ef252acb146f8b9152e55346ec.tar.gz busybox-w32-26cd90c7fd32f2ef252acb146f8b9152e55346ec.tar.bz2 busybox-w32-26cd90c7fd32f2ef252acb146f8b9152e55346ec.zip | |
unzip: survive lack of CDF on non-streaming zip files
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -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 c250d7555..673e5fe08 100644 --- a/archival/unzip.c +++ b/archival/unzip.c | |||
| @@ -172,6 +172,9 @@ enum { zip_fd = 3 }; | |||
| 172 | */ | 172 | */ |
| 173 | #define PEEK_FROM_END (64*1024) | 173 | #define PEEK_FROM_END (64*1024) |
| 174 | 174 | ||
| 175 | /* This value means that we failed to find CDF */ | ||
| 176 | #define BAD_CDF_OFFSET ((uint32_t)0xffffffff) | ||
| 177 | |||
| 175 | /* NB: does not preserve file position! */ | 178 | /* NB: does not preserve file position! */ |
| 176 | static uint32_t find_cdf_offset(void) | 179 | static uint32_t find_cdf_offset(void) |
| 177 | { | 180 | { |
| @@ -187,6 +190,7 @@ static uint32_t find_cdf_offset(void) | |||
| 187 | xlseek(zip_fd, end, SEEK_SET); | 190 | xlseek(zip_fd, end, SEEK_SET); |
| 188 | full_read(zip_fd, buf, PEEK_FROM_END); | 191 | full_read(zip_fd, buf, PEEK_FROM_END); |
| 189 | 192 | ||
| 193 | cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; | ||
| 190 | p = buf; | 194 | p = buf; |
| 191 | while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { | 195 | while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { |
| 192 | if (*p != 'P') { | 196 | if (*p != 'P') { |
| @@ -202,11 +206,17 @@ static uint32_t find_cdf_offset(void) | |||
| 202 | /* we found CDE! */ | 206 | /* we found CDE! */ |
| 203 | memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); | 207 | memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); |
| 204 | FIX_ENDIANNESS_CDE(cde_header); | 208 | FIX_ENDIANNESS_CDE(cde_header); |
| 205 | free(buf); | 209 | /* |
| 206 | return cde_header.formatted.cdf_offset; | 210 | * I've seen .ZIP files with seemingly valid CDEs |
| 211 | * where cdf_offset points past EOF - ?? | ||
| 212 | * Ignore such CDEs: | ||
| 213 | */ | ||
| 214 | if (cde_header.formatted.cdf_offset < end + (p - buf)) | ||
| 215 | break; | ||
| 216 | cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; | ||
| 207 | } | 217 | } |
| 208 | //free(buf); | 218 | free(buf); |
| 209 | bb_error_msg_and_die("can't find file table"); | 219 | return cde_header.formatted.cdf_offset; |
| 210 | }; | 220 | }; |
| 211 | 221 | ||
| 212 | static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) | 222 | 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) | |||
| 218 | if (!cdf_offset) | 228 | if (!cdf_offset) |
| 219 | cdf_offset = find_cdf_offset(); | 229 | cdf_offset = find_cdf_offset(); |
| 220 | 230 | ||
| 221 | xlseek(zip_fd, cdf_offset + 4, SEEK_SET); | 231 | if (cdf_offset != BAD_CDF_OFFSET) { |
| 222 | xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); | 232 | xlseek(zip_fd, cdf_offset + 4, SEEK_SET); |
| 223 | FIX_ENDIANNESS_CDF(*cdf_ptr); | 233 | xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); |
| 224 | cdf_offset += 4 + CDF_HEADER_LEN | 234 | FIX_ENDIANNESS_CDF(*cdf_ptr); |
| 225 | + cdf_ptr->formatted.file_name_length | 235 | cdf_offset += 4 + CDF_HEADER_LEN |
| 226 | + cdf_ptr->formatted.extra_field_length | 236 | + cdf_ptr->formatted.file_name_length |
| 227 | + cdf_ptr->formatted.file_comment_length; | 237 | + cdf_ptr->formatted.extra_field_length |
| 238 | + cdf_ptr->formatted.file_comment_length; | ||
| 239 | } | ||
| 228 | 240 | ||
| 229 | xlseek(zip_fd, org, SEEK_SET); | 241 | xlseek(zip_fd, org, SEEK_SET); |
| 230 | return cdf_offset; | 242 | return cdf_offset; |
| @@ -233,8 +245,9 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) | |||
| 233 | 245 | ||
| 234 | static void unzip_skip(off_t skip) | 246 | static void unzip_skip(off_t skip) |
| 235 | { | 247 | { |
| 236 | if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) | 248 | if (skip != 0) |
| 237 | bb_copyfd_exact_size(zip_fd, -1, skip); | 249 | if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) |
| 250 | bb_copyfd_exact_size(zip_fd, -1, skip); | ||
| 238 | } | 251 | } |
| 239 | 252 | ||
| 240 | static void unzip_create_leading_dirs(const char *fn) | 253 | static void unzip_create_leading_dirs(const char *fn) |
| @@ -535,21 +548,31 @@ int unzip_main(int argc, char **argv) | |||
| 535 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); | 548 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); |
| 536 | } | 549 | } |
| 537 | 550 | ||
| 538 | { | 551 | if (cdf_offset != BAD_CDF_OFFSET) { |
| 539 | cdf_header_t cdf_header; | 552 | cdf_header_t cdf_header; |
| 540 | cdf_offset = read_next_cdf(cdf_offset, &cdf_header); | 553 | cdf_offset = read_next_cdf(cdf_offset, &cdf_header); |
| 554 | /* | ||
| 555 | * Note: cdf_offset can become BAD_CDF_OFFSET after the above call. | ||
| 556 | */ | ||
| 541 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { | 557 | if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { |
| 542 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten | 558 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten |
| 543 | * only from Central Directory. See unzip_doc.txt */ | 559 | * only from Central Directory. See unzip_doc.txt |
| 560 | */ | ||
| 544 | zip_header.formatted.crc32 = cdf_header.formatted.crc32; | 561 | zip_header.formatted.crc32 = cdf_header.formatted.crc32; |
| 545 | zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; | 562 | zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; |
| 546 | zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; | 563 | zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; |
| 547 | } | 564 | } |
| 548 | if ((cdf_header.formatted.version_made_by >> 8) == 3) { | 565 | if ((cdf_header.formatted.version_made_by >> 8) == 3) { |
| 549 | /* this archive is created on Unix */ | 566 | /* This archive is created on Unix */ |
| 550 | dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); | 567 | dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); |
| 551 | } | 568 | } |
| 552 | } | 569 | } |
| 570 | if (cdf_offset == BAD_CDF_OFFSET | ||
| 571 | && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) | ||
| 572 | ) { | ||
| 573 | /* If it's a streaming zip, we _require_ CDF */ | ||
| 574 | bb_error_msg_and_die("can't find file table"); | ||
| 575 | } | ||
| 553 | #endif | 576 | #endif |
| 554 | 577 | ||
| 555 | /* Read filename */ | 578 | /* Read filename */ |
