aboutsummaryrefslogtreecommitdiff
path: root/archival/unzip.c
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2016-05-16 09:33:03 +0100
committerRon Yorston <rmy@pobox.com>2016-05-16 09:33:03 +0100
commit35d2f5bccb0f3dde600702ebcdb5424d4d50be4a (patch)
tree6e0ff0341c69839e268459a199682628bae734ed /archival/unzip.c
parent248a2600a2f4b442101ad568d1994b908bb28d4b (diff)
parentf2559e5c2b7bd2c5fa0dd8e88d0a931da92a23af (diff)
downloadbusybox-w32-35d2f5bccb0f3dde600702ebcdb5424d4d50be4a.tar.gz
busybox-w32-35d2f5bccb0f3dde600702ebcdb5424d4d50be4a.tar.bz2
busybox-w32-35d2f5bccb0f3dde600702ebcdb5424d4d50be4a.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'archival/unzip.c')
-rw-r--r--archival/unzip.c138
1 files changed, 96 insertions, 42 deletions
diff --git a/archival/unzip.c b/archival/unzip.c
index 203073434..27adb3420 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -48,6 +48,12 @@
48#pragma pack(2) 48#pragma pack(2)
49#endif 49#endif
50 50
51#if 0
52# define dbg(...) bb_error_msg(__VA_ARGS__)
53#else
54# define dbg(...) ((void)0)
55#endif
56
51enum { 57enum {
52#if BB_BIG_ENDIAN 58#if BB_BIG_ENDIAN
53 ZIP_FILEHEADER_MAGIC = 0x504b0304, 59 ZIP_FILEHEADER_MAGIC = 0x504b0304,
@@ -196,15 +202,17 @@ static uint32_t find_cdf_offset(void)
196 unsigned char *p; 202 unsigned char *p;
197 off_t end; 203 off_t end;
198 unsigned char *buf = xzalloc(PEEK_FROM_END); 204 unsigned char *buf = xzalloc(PEEK_FROM_END);
205 uint32_t found;
199 206
200 end = xlseek(zip_fd, 0, SEEK_END); 207 end = xlseek(zip_fd, 0, SEEK_END);
201 end -= PEEK_FROM_END; 208 end -= PEEK_FROM_END;
202 if (end < 0) 209 if (end < 0)
203 end = 0; 210 end = 0;
204 xlseek(zip_fd, end, SEEK_SET); 211 dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end);
212 xlseek(zip_fd, end, SEEK_SET);
205 full_read(zip_fd, buf, PEEK_FROM_END); 213 full_read(zip_fd, buf, PEEK_FROM_END);
206 214
207 cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; 215 found = BAD_CDF_OFFSET;
208 p = buf; 216 p = buf;
209 while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { 217 while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
210 if (*p != 'P') { 218 if (*p != 'P') {
@@ -223,14 +231,25 @@ static uint32_t find_cdf_offset(void)
223 /* 231 /*
224 * I've seen .ZIP files with seemingly valid CDEs 232 * I've seen .ZIP files with seemingly valid CDEs
225 * where cdf_offset points past EOF - ?? 233 * where cdf_offset points past EOF - ??
226 * Ignore such CDEs: 234 * This check ignores such CDEs:
227 */ 235 */
228 if (cde_header.formatted.cdf_offset < end + (p - buf)) 236 if (cde_header.formatted.cdf_offset < end + (p - buf)) {
229 break; 237 found = cde_header.formatted.cdf_offset;
230 cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; 238 dbg("Possible cdf_offset:0x%x at 0x%"OFF_FMT"x",
239 (unsigned)found, end + (p-3 - buf));
240 dbg(" cdf_offset+cdf_size:0x%x",
241 (unsigned)(found + SWAP_LE32(cde_header.formatted.cdf_size)));
242 /*
243 * We do not "break" here because only the last CDE is valid.
244 * I've seen a .zip archive which contained a .zip file,
245 * uncompressed, and taking the first CDE was using
246 * the CDE inside that file!
247 */
248 }
231 } 249 }
232 free(buf); 250 free(buf);
233 return cde_header.formatted.cdf_offset; 251 dbg("Found cdf_offset:0x%x", (unsigned)found);
252 return found;
234}; 253};
235 254
236static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) 255static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
@@ -243,15 +262,22 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
243 cdf_offset = find_cdf_offset(); 262 cdf_offset = find_cdf_offset();
244 263
245 if (cdf_offset != BAD_CDF_OFFSET) { 264 if (cdf_offset != BAD_CDF_OFFSET) {
265 dbg("Reading CDF at 0x%x", (unsigned)cdf_offset);
246 xlseek(zip_fd, cdf_offset + 4, SEEK_SET); 266 xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
247 xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); 267 xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
248 FIX_ENDIANNESS_CDF(*cdf_ptr); 268 FIX_ENDIANNESS_CDF(*cdf_ptr);
269 dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u",
270 (unsigned)cdf_ptr->formatted.file_name_length,
271 (unsigned)cdf_ptr->formatted.extra_field_length,
272 (unsigned)cdf_ptr->formatted.file_comment_length
273 );
249 cdf_offset += 4 + CDF_HEADER_LEN 274 cdf_offset += 4 + CDF_HEADER_LEN
250 + cdf_ptr->formatted.file_name_length 275 + cdf_ptr->formatted.file_name_length
251 + cdf_ptr->formatted.extra_field_length 276 + cdf_ptr->formatted.extra_field_length
252 + cdf_ptr->formatted.file_comment_length; 277 + cdf_ptr->formatted.file_comment_length;
253 } 278 }
254 279
280 dbg("Returning file position to 0x%"OFF_FMT"x", org);
255 xlseek(zip_fd, org, SEEK_SET); 281 xlseek(zip_fd, org, SEEK_SET);
256 return cdf_offset; 282 return cdf_offset;
257}; 283};
@@ -464,7 +490,7 @@ int unzip_main(int argc, char **argv)
464 if (overwrite == O_PROMPT) 490 if (overwrite == O_PROMPT)
465 overwrite = O_NEVER; 491 overwrite = O_NEVER;
466 } else { 492 } else {
467 static const char extn[][5] = { ".zip", ".ZIP" }; 493 static const char extn[][5] ALIGN1 = { ".zip", ".ZIP" };
468 char *ext = src_fn + strlen(src_fn); 494 char *ext = src_fn + strlen(src_fn);
469 int src_fd; 495 int src_fd;
470 496
@@ -491,11 +517,11 @@ int unzip_main(int argc, char **argv)
491 printf("Archive: %s\n", src_fn); 517 printf("Archive: %s\n", src_fn);
492 if (listing) { 518 if (listing) {
493 puts(verbose ? 519 puts(verbose ?
494 " Length Method Size Ratio Date Time CRC-32 Name\n" 520 " Length Method Size Cmpr Date Time CRC-32 Name\n"
495 "-------- ------ ------- ----- ---- ---- ------ ----" 521 "-------- ------ ------- ---- ---------- ----- -------- ----"
496 : 522 :
497 " Length Date Time Name\n" 523 " Length Date Time Name\n"
498 " -------- ---- ---- ----" 524 "--------- ---------- ----- ----"
499 ); 525 );
500 } 526 }
501 } 527 }
@@ -535,11 +561,14 @@ int unzip_main(int argc, char **argv)
535 /* Check magic number */ 561 /* Check magic number */
536 xread(zip_fd, &magic, 4); 562 xread(zip_fd, &magic, 4);
537 /* Central directory? It's at the end, so exit */ 563 /* Central directory? It's at the end, so exit */
538 if (magic == ZIP_CDF_MAGIC) 564 if (magic == ZIP_CDF_MAGIC) {
565 dbg("got ZIP_CDF_MAGIC");
539 break; 566 break;
567 }
540#if ENABLE_DESKTOP 568#if ENABLE_DESKTOP
541 /* Data descriptor? It was a streaming file, go on */ 569 /* Data descriptor? It was a streaming file, go on */
542 if (magic == ZIP_DD_MAGIC) { 570 if (magic == ZIP_DD_MAGIC) {
571 dbg("got ZIP_DD_MAGIC");
543 /* skip over duplicate crc32, cmpsize and ucmpsize */ 572 /* skip over duplicate crc32, cmpsize and ucmpsize */
544 unzip_skip(3 * 4); 573 unzip_skip(3 * 4);
545 continue; 574 continue;
@@ -547,6 +576,7 @@ int unzip_main(int argc, char **argv)
547#endif 576#endif
548 if (magic != ZIP_FILEHEADER_MAGIC) 577 if (magic != ZIP_FILEHEADER_MAGIC)
549 bb_error_msg_and_die("invalid zip magic %08X", (int)magic); 578 bb_error_msg_and_die("invalid zip magic %08X", (int)magic);
579 dbg("got ZIP_FILEHEADER_MAGIC");
550 580
551 /* Read the file header */ 581 /* Read the file header */
552 xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); 582 xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN);
@@ -590,6 +620,11 @@ int unzip_main(int argc, char **argv)
590 bb_error_msg_and_die("can't find file table"); 620 bb_error_msg_and_die("can't find file table");
591 } 621 }
592#endif 622#endif
623 dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x",
624 (unsigned)zip_header.formatted.cmpsize,
625 (unsigned)zip_header.formatted.extra_len,
626 (unsigned)zip_header.formatted.ucmpsize
627 );
593 628
594 /* Read filename */ 629 /* Read filename */
595 free(dst_fn); 630 free(dst_fn);
@@ -610,40 +645,55 @@ int unzip_main(int argc, char **argv)
610 } else { 645 } else {
611 if (listing) { 646 if (listing) {
612 /* List entry */ 647 /* List entry */
613 unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); 648 char dtbuf[sizeof("mm-dd-yyyy hh:mm")];
649 sprintf(dtbuf, "%02u-%02u-%04u %02u:%02u",
650 (zip_header.formatted.moddate >> 5) & 0xf, // mm: 0x01e0
651 (zip_header.formatted.moddate) & 0x1f, // dd: 0x001f
652 (zip_header.formatted.moddate >> 9) + 1980, // yy: 0xfe00
653 (zip_header.formatted.modtime >> 11), // hh: 0xf800
654 (zip_header.formatted.modtime >> 5) & 0x3f // mm: 0x07e0
655 // seconds/2 are not shown, encoded in ----------- 0x001f
656 );
614 if (!verbose) { 657 if (!verbose) {
615 // " Length Date Time Name\n" 658 // " Length Date Time Name\n"
616 // " -------- ---- ---- ----" 659 // "--------- ---------- ----- ----"
617 printf( "%9u %02u-%02u-%02u %02u:%02u %s\n", 660 printf( "%9u " "%s " "%s\n",
618 (unsigned)zip_header.formatted.ucmpsize, 661 (unsigned)zip_header.formatted.ucmpsize,
619 (dostime & 0x01e00000) >> 21, 662 dtbuf,
620 (dostime & 0x001f0000) >> 16,
621 (((dostime & 0xfe000000) >> 25) + 1980) % 100,
622 (dostime & 0x0000f800) >> 11,
623 (dostime & 0x000007e0) >> 5,
624 dst_fn); 663 dst_fn);
625 total_usize += zip_header.formatted.ucmpsize;
626 } else { 664 } else {
627 unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize; 665 unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize;
666 if ((int32_t)percents < 0)
667 percents = 0; /* happens if ucmpsize < cmpsize */
628 percents = percents * 100; 668 percents = percents * 100;
629 if (zip_header.formatted.ucmpsize) 669 if (zip_header.formatted.ucmpsize)
630 percents /= zip_header.formatted.ucmpsize; 670 percents /= zip_header.formatted.ucmpsize;
631 // " Length Method Size Ratio Date Time CRC-32 Name\n" 671 // " Length Method Size Cmpr Date Time CRC-32 Name\n"
632 // "-------- ------ ------- ----- ---- ---- ------ ----" 672 // "-------- ------ ------- ---- ---------- ----- -------- ----"
633 printf( "%8u Defl:N" "%9u%4u%% %02u-%02u-%02u %02u:%02u %08x %s\n", 673 printf( "%8u %s" "%9u%4u%% " "%s " "%08x " "%s\n",
634 (unsigned)zip_header.formatted.ucmpsize, 674 (unsigned)zip_header.formatted.ucmpsize,
675 zip_header.formatted.method == 0 ? "Stored" : "Defl:N", /* Defl is method 8 */
676/* TODO: show other methods?
677 * 1 - Shrunk
678 * 2 - Reduced with compression factor 1
679 * 3 - Reduced with compression factor 2
680 * 4 - Reduced with compression factor 3
681 * 5 - Reduced with compression factor 4
682 * 6 - Imploded
683 * 7 - Reserved for Tokenizing compression algorithm
684 * 9 - Deflate64
685 * 10 - PKWARE Data Compression Library Imploding
686 * 11 - Reserved by PKWARE
687 * 12 - BZIP2
688 */
635 (unsigned)zip_header.formatted.cmpsize, 689 (unsigned)zip_header.formatted.cmpsize,
636 (unsigned)percents, 690 (unsigned)percents,
637 (dostime & 0x01e00000) >> 21, 691 dtbuf,
638 (dostime & 0x001f0000) >> 16,
639 (((dostime & 0xfe000000) >> 25) + 1980) % 100,
640 (dostime & 0x0000f800) >> 11,
641 (dostime & 0x000007e0) >> 5,
642 zip_header.formatted.crc32, 692 zip_header.formatted.crc32,
643 dst_fn); 693 dst_fn);
644 total_usize += zip_header.formatted.ucmpsize;
645 total_size += zip_header.formatted.cmpsize; 694 total_size += zip_header.formatted.cmpsize;
646 } 695 }
696 total_usize += zip_header.formatted.ucmpsize;
647 i = 'n'; 697 i = 'n';
648 } else if (dst_fd == STDOUT_FILENO) { 698 } else if (dst_fd == STDOUT_FILENO) {
649 /* Extracting to STDOUT */ 699 /* Extracting to STDOUT */
@@ -746,21 +796,25 @@ int unzip_main(int argc, char **argv)
746 796
747 if (listing && quiet <= 1) { 797 if (listing && quiet <= 1) {
748 if (!verbose) { 798 if (!verbose) {
749 // " Length Date Time Name\n" 799 // " Length Date Time Name\n"
750 // " -------- ---- ---- ----" 800 // "--------- ---------- ----- ----"
751 printf( " -------- -------\n" 801 printf( " --------%21s" "-------\n"
752 "%9lu" " %u files\n", 802 "%9lu%21s" "%u files\n",
753 total_usize, total_entries); 803 "",
804 total_usize, "", total_entries);
754 } else { 805 } else {
755 unsigned long percents = total_usize - total_size; 806 unsigned long percents = total_usize - total_size;
807 if ((long)percents < 0)
808 percents = 0; /* happens if usize < size */
756 percents = percents * 100; 809 percents = percents * 100;
757 if (total_usize) 810 if (total_usize)
758 percents /= total_usize; 811 percents /= total_usize;
759 // " Length Method Size Ratio Date Time CRC-32 Name\n" 812 // " Length Method Size Cmpr Date Time CRC-32 Name\n"
760 // "-------- ------ ------- ----- ---- ---- ------ ----" 813 // "-------- ------ ------- ---- ---------- ----- -------- ----"
761 printf( "-------- ------- --- -------\n" 814 printf( "-------- ------- ----%28s" "----\n"
762 "%8lu" "%17lu%4u%% %u files\n", 815 "%8lu" "%17lu%4u%%%28s" "%u files\n",
763 total_usize, total_size, (unsigned)percents, 816 "",
817 total_usize, total_size, (unsigned)percents, "",
764 total_entries); 818 total_entries);
765 } 819 }
766 } 820 }