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 */ |