aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2013-07-21 02:31:08 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2013-07-21 02:31:08 +0200
commit26cd90c7fd32f2ef252acb146f8b9152e55346ec (patch)
treeb5a2a93c13d44eb91c94979f0aa74815bd740b5a
parent1390a010b60cbb5adad4e4c56a3613122b4298c6 (diff)
downloadbusybox-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.c55
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! */
176static uint32_t find_cdf_offset(void) 179static 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
212static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) 222static 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
234static void unzip_skip(off_t skip) 246static 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
240static void unzip_create_leading_dirs(const char *fn) 253static 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 */