Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/archival/unzip.c
- Timestamp:
- Dec 20, 2016, 4:07:32 PM (7 years ago)
- Location:
- branches/3.3
- Files:
-
- 1 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
branches/3.3/mindi-busybox/archival/unzip.c
r3232 r3621 10 10 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 11 11 */ 12 13 12 /* For reference see 14 13 * http://www.pkware.com/company/standards/appnote/ 15 14 * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip 16 */ 17 18 /* TODO 15 * 16 * TODO 19 17 * Zip64 + other methods 20 18 */ 19 20 //config:config UNZIP 21 //config: bool "unzip" 22 //config: default y 23 //config: help 24 //config: unzip will list or extract files from a ZIP archive, 25 //config: commonly found on DOS/WIN systems. The default behavior 26 //config: (with no options) is to extract the archive into the 27 //config: current directory. Use the `-d' option to extract to a 28 //config: directory of your choice. 29 30 //applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP)) 31 //kbuild:lib-$(CONFIG_UNZIP) += unzip.o 21 32 22 33 //usage:#define unzip_trivial_usage … … 34 45 #include "libbb.h" 35 46 #include "bb_archive.h" 47 48 #if 0 49 # define dbg(...) bb_error_msg(__VA_ARGS__) 50 #else 51 # define dbg(...) ((void)0) 52 #endif 36 53 37 54 enum { … … 164 181 #if ENABLE_DESKTOP 165 182 166 #define PEEK_FROM_END 16384 183 /* Seen in the wild: 184 * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive, 185 * where CDE was nearly 48 kbytes before EOF. 186 * (Surprisingly, it also apparently has *another* CDE structure 187 * closer to the end, with bogus cdf_offset). 188 * To make extraction work, bumped PEEK_FROM_END from 16k to 64k. 189 */ 190 #define PEEK_FROM_END (64*1024) 191 192 /* This value means that we failed to find CDF */ 193 #define BAD_CDF_OFFSET ((uint32_t)0xffffffff) 167 194 168 195 /* NB: does not preserve file position! */ … … 173 200 off_t end; 174 201 unsigned char *buf = xzalloc(PEEK_FROM_END); 202 uint32_t found; 175 203 176 204 end = xlseek(zip_fd, 0, SEEK_END); … … 178 206 if (end < 0) 179 207 end = 0; 180 xlseek(zip_fd, end, SEEK_SET); 208 dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end); 209 xlseek(zip_fd, end, SEEK_SET); 181 210 full_read(zip_fd, buf, PEEK_FROM_END); 182 211 212 found = BAD_CDF_OFFSET; 183 213 p = buf; 184 214 while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { … … 196 226 memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); 197 227 FIX_ENDIANNESS_CDE(cde_header); 198 free(buf); 199 return cde_header.formatted.cdf_offset; 200 } 201 //free(buf); 202 bb_error_msg_and_die("can't find file table"); 228 /* 229 * I've seen .ZIP files with seemingly valid CDEs 230 * where cdf_offset points past EOF - ?? 231 * This check ignores such CDEs: 232 */ 233 if (cde_header.formatted.cdf_offset < end + (p - buf)) { 234 found = cde_header.formatted.cdf_offset; 235 dbg("Possible cdf_offset:0x%x at 0x%"OFF_FMT"x", 236 (unsigned)found, end + (p-3 - buf)); 237 dbg(" cdf_offset+cdf_size:0x%x", 238 (unsigned)(found + SWAP_LE32(cde_header.formatted.cdf_size))); 239 /* 240 * We do not "break" here because only the last CDE is valid. 241 * I've seen a .zip archive which contained a .zip file, 242 * uncompressed, and taking the first CDE was using 243 * the CDE inside that file! 244 */ 245 } 246 } 247 free(buf); 248 dbg("Found cdf_offset:0x%x", (unsigned)found); 249 return found; 203 250 }; 204 251 … … 212 259 cdf_offset = find_cdf_offset(); 213 260 214 xlseek(zip_fd, cdf_offset + 4, SEEK_SET); 215 xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); 216 FIX_ENDIANNESS_CDF(*cdf_ptr); 217 cdf_offset += 4 + CDF_HEADER_LEN 218 + cdf_ptr->formatted.file_name_length 219 + cdf_ptr->formatted.extra_field_length 220 + cdf_ptr->formatted.file_comment_length; 221 261 if (cdf_offset != BAD_CDF_OFFSET) { 262 dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); 263 xlseek(zip_fd, cdf_offset + 4, SEEK_SET); 264 xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); 265 FIX_ENDIANNESS_CDF(*cdf_ptr); 266 dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u", 267 (unsigned)cdf_ptr->formatted.file_name_length, 268 (unsigned)cdf_ptr->formatted.extra_field_length, 269 (unsigned)cdf_ptr->formatted.file_comment_length 270 ); 271 cdf_offset += 4 + CDF_HEADER_LEN 272 + cdf_ptr->formatted.file_name_length 273 + cdf_ptr->formatted.extra_field_length 274 + cdf_ptr->formatted.file_comment_length; 275 } 276 277 dbg("Returning file position to 0x%"OFF_FMT"x", org); 222 278 xlseek(zip_fd, org, SEEK_SET); 223 279 return cdf_offset; … … 227 283 static void unzip_skip(off_t skip) 228 284 { 229 if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) 230 bb_copyfd_exact_size(zip_fd, -1, skip); 285 if (skip != 0) 286 if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) 287 bb_copyfd_exact_size(zip_fd, -1, skip); 231 288 } 232 289 … … 250 307 } else { 251 308 /* Method 8 - inflate */ 252 transformer_aux_data_t aux; 253 init_transformer_aux_data(&aux); 254 aux.bytes_in = zip_header->formatted.cmpsize; 255 if (inflate_unzip(&aux, zip_fd, dst_fd) < 0) 309 transformer_state_t xstate; 310 init_transformer_state(&xstate); 311 xstate.bytes_in = zip_header->formatted.cmpsize; 312 xstate.src_fd = zip_fd; 313 xstate.dst_fd = dst_fd; 314 if (inflate_unzip(&xstate) < 0) 256 315 bb_error_msg_and_die("inflate error"); 257 316 /* Validate decompression - crc */ 258 if (zip_header->formatted.crc32 != ( aux.crc32 ^ 0xffffffffL)) {317 if (zip_header->formatted.crc32 != (xstate.crc32 ^ 0xffffffffL)) { 259 318 bb_error_msg_and_die("crc error"); 260 319 } 261 320 /* Validate decompression - size */ 262 if (zip_header->formatted.ucmpsize != aux.bytes_out) {321 if (zip_header->formatted.ucmpsize != xstate.bytes_out) { 263 322 /* Don't die. Who knows, maybe len calculation 264 323 * was botched somewhere. After all, crc matched! */ 265 324 bb_error_msg("bad length"); 266 325 } 326 } 327 } 328 329 static void my_fgets80(char *buf80) 330 { 331 fflush_all(); 332 if (!fgets(buf80, 80, stdin)) { 333 bb_perror_msg_and_die("can't read standard input"); 267 334 } 268 335 } … … 292 359 char *base_dir = NULL; 293 360 int i, opt; 294 char key_buf[80]; 361 char key_buf[80]; /* must match size used by my_fgets80 */ 295 362 struct stat stat_buf; 296 363 … … 421 488 overwrite = O_NEVER; 422 489 } else { 423 static const char extn[][5] = { ".zip", ".ZIP" };490 static const char extn[][5] ALIGN1 = { ".zip", ".ZIP" }; 424 491 char *ext = src_fn + strlen(src_fn); 425 492 int src_fd; … … 448 515 if (listing) { 449 516 puts(verbose ? 450 " Length Method Size Ratio Date Time CRC-32Name\n"451 "-------- ------ ------- ---- - ---- ---- ----------"517 " Length Method Size Cmpr Date Time CRC-32 Name\n" 518 "-------- ------ ------- ---- ---------- ----- -------- ----" 452 519 : 453 " Length DateTime Name\n"454 " -------- ---- --------"520 " Length Date Time Name\n" 521 "--------- ---------- ----- ----" 455 522 ); 456 523 } … … 492 559 xread(zip_fd, &magic, 4); 493 560 /* Central directory? It's at the end, so exit */ 494 if (magic == ZIP_CDF_MAGIC) 495 break; 561 if (magic == ZIP_CDF_MAGIC) { 562 dbg("got ZIP_CDF_MAGIC"); 563 break; 564 } 496 565 #if ENABLE_DESKTOP 497 566 /* Data descriptor? It was a streaming file, go on */ 498 567 if (magic == ZIP_DD_MAGIC) { 568 dbg("got ZIP_DD_MAGIC"); 499 569 /* skip over duplicate crc32, cmpsize and ucmpsize */ 500 570 unzip_skip(3 * 4); … … 504 574 if (magic != ZIP_FILEHEADER_MAGIC) 505 575 bb_error_msg_and_die("invalid zip magic %08X", (int)magic); 576 dbg("got ZIP_FILEHEADER_MAGIC"); 506 577 507 578 /* Read the file header */ … … 521 592 } 522 593 523 {594 if (cdf_offset != BAD_CDF_OFFSET) { 524 595 cdf_header_t cdf_header; 525 596 cdf_offset = read_next_cdf(cdf_offset, &cdf_header); 597 /* 598 * Note: cdf_offset can become BAD_CDF_OFFSET after the above call. 599 */ 526 600 if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { 527 601 /* 0x0008 - streaming. [u]cmpsize can be reliably gotten 528 * only from Central Directory. See unzip_doc.txt */ 602 * only from Central Directory. See unzip_doc.txt 603 */ 529 604 zip_header.formatted.crc32 = cdf_header.formatted.crc32; 530 605 zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; … … 532 607 } 533 608 if ((cdf_header.formatted.version_made_by >> 8) == 3) { 534 /* this archive is created on Unix */609 /* This archive is created on Unix */ 535 610 dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); 536 611 } 537 612 } 538 #endif 613 if (cdf_offset == BAD_CDF_OFFSET 614 && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) 615 ) { 616 /* If it's a streaming zip, we _require_ CDF */ 617 bb_error_msg_and_die("can't find file table"); 618 } 619 #endif 620 dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x", 621 (unsigned)zip_header.formatted.cmpsize, 622 (unsigned)zip_header.formatted.extra_len, 623 (unsigned)zip_header.formatted.ucmpsize 624 ); 539 625 540 626 /* Read filename */ … … 545 631 /* Skip extra header bytes */ 546 632 unzip_skip(zip_header.formatted.extra_len); 633 634 /* Guard against "/abspath", "/../" and similar attacks */ 635 overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn)); 547 636 548 637 /* Filter zip entries */ … … 551 640 ) { /* Skip entry */ 552 641 i = 'n'; 553 554 } else { /* Extract entry */ 555 if (listing) { /* List entry */ 556 unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); 642 } else { 643 if (listing) { 644 /* List entry */ 645 char dtbuf[sizeof("mm-dd-yyyy hh:mm")]; 646 sprintf(dtbuf, "%02u-%02u-%04u %02u:%02u", 647 (zip_header.formatted.moddate >> 5) & 0xf, // mm: 0x01e0 648 (zip_header.formatted.moddate) & 0x1f, // dd: 0x001f 649 (zip_header.formatted.moddate >> 9) + 1980, // yy: 0xfe00 650 (zip_header.formatted.modtime >> 11), // hh: 0xf800 651 (zip_header.formatted.modtime >> 5) & 0x3f // mm: 0x07e0 652 // seconds/2 are not shown, encoded in ----------- 0x001f 653 ); 557 654 if (!verbose) { 558 // " Length DateTime Name\n"559 // " -------- ---- --------"560 printf( "%9u %02u-%02u-%02u %02u:%02u%s\n",655 // " Length Date Time Name\n" 656 // "--------- ---------- ----- ----" 657 printf( "%9u " "%s " "%s\n", 561 658 (unsigned)zip_header.formatted.ucmpsize, 562 (dostime & 0x01e00000) >> 21, 563 (dostime & 0x001f0000) >> 16, 564 (((dostime & 0xfe000000) >> 25) + 1980) % 100, 565 (dostime & 0x0000f800) >> 11, 566 (dostime & 0x000007e0) >> 5, 659 dtbuf, 567 660 dst_fn); 568 total_usize += zip_header.formatted.ucmpsize;569 661 } else { 570 662 unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize; 663 if ((int32_t)percents < 0) 664 percents = 0; /* happens if ucmpsize < cmpsize */ 571 665 percents = percents * 100; 572 666 if (zip_header.formatted.ucmpsize) 573 667 percents /= zip_header.formatted.ucmpsize; 574 // " Length Method Size Ratio Date Time CRC-32Name\n"575 // "-------- ------ ------- ---- - ---- ---- ----------"576 printf( "%8u Defl:N" "%9u%4u%% %02u-%02u-%02u %02u:%02u %08x%s\n",668 // " Length Method Size Cmpr Date Time CRC-32 Name\n" 669 // "-------- ------ ------- ---- ---------- ----- -------- ----" 670 printf( "%8u %s" "%9u%4u%% " "%s " "%08x " "%s\n", 577 671 (unsigned)zip_header.formatted.ucmpsize, 672 zip_header.formatted.method == 0 ? "Stored" : "Defl:N", /* Defl is method 8 */ 673 /* TODO: show other methods? 674 * 1 - Shrunk 675 * 2 - Reduced with compression factor 1 676 * 3 - Reduced with compression factor 2 677 * 4 - Reduced with compression factor 3 678 * 5 - Reduced with compression factor 4 679 * 6 - Imploded 680 * 7 - Reserved for Tokenizing compression algorithm 681 * 9 - Deflate64 682 * 10 - PKWARE Data Compression Library Imploding 683 * 11 - Reserved by PKWARE 684 * 12 - BZIP2 685 */ 578 686 (unsigned)zip_header.formatted.cmpsize, 579 687 (unsigned)percents, 580 (dostime & 0x01e00000) >> 21, 581 (dostime & 0x001f0000) >> 16, 582 (((dostime & 0xfe000000) >> 25) + 1980) % 100, 583 (dostime & 0x0000f800) >> 11, 584 (dostime & 0x000007e0) >> 5, 688 dtbuf, 585 689 zip_header.formatted.crc32, 586 690 dst_fn); 587 total_usize += zip_header.formatted.ucmpsize;588 691 total_size += zip_header.formatted.cmpsize; 589 692 } 693 total_usize += zip_header.formatted.ucmpsize; 590 694 i = 'n'; 591 } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */ 695 } else if (dst_fd == STDOUT_FILENO) { 696 /* Extracting to STDOUT */ 592 697 i = -1; 593 } else if (last_char_is(dst_fn, '/')) { /* Extract directory */ 698 } else if (last_char_is(dst_fn, '/')) { 699 /* Extract directory */ 594 700 if (stat(dst_fn, &stat_buf) == -1) { 595 701 if (errno != ENOENT) { … … 605 711 } else { 606 712 if (!S_ISDIR(stat_buf.st_mode)) { 607 bb_error_msg_and_die("'%s' exists but is not directory", dst_fn); 713 bb_error_msg_and_die("'%s' exists but is not a %s", 714 dst_fn, "directory"); 608 715 } 609 716 } 610 717 i = 'n'; 611 612 } else {/* Extract file */718 } else { 719 /* Extract file */ 613 720 check_file: 614 if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ 721 if (stat(dst_fn, &stat_buf) == -1) { 722 /* File does not exist */ 615 723 if (errno != ENOENT) { 616 724 bb_perror_msg_and_die("can't stat '%s'", dst_fn); 617 725 } 618 726 i = 'y'; 619 } else { /* File already exists */ 727 } else { 728 /* File already exists */ 620 729 if (overwrite == O_NEVER) { 621 730 i = 'n'; 622 } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ 731 } else if (S_ISREG(stat_buf.st_mode)) { 732 /* File is regular file */ 623 733 if (overwrite == O_ALWAYS) { 624 734 i = 'y'; 625 735 } else { 626 736 printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); 627 fflush_all(); 628 if (!fgets(key_buf, sizeof(key_buf), stdin)) { 629 bb_perror_msg_and_die("can't read input"); 630 } 737 my_fgets80(key_buf); 631 738 i = key_buf[0]; 632 739 } 633 } else { /* File is not regular file */ 634 bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn); 740 } else { 741 /* File is not regular file */ 742 bb_error_msg_and_die("'%s' exists but is not a %s", 743 dst_fn, "regular file"); 635 744 } 636 745 } … … 669 778 /* Prompt for new name */ 670 779 printf("new name: "); 671 if (!fgets(key_buf, sizeof(key_buf), stdin)) { 672 bb_perror_msg_and_die("can't read input"); 673 } 780 my_fgets80(key_buf); 674 781 free(dst_fn); 675 782 dst_fn = xstrdup(key_buf); … … 687 794 if (listing && quiet <= 1) { 688 795 if (!verbose) { 689 // " Length Date Time Name\n" 690 // " -------- ---- ---- ----" 691 printf( " -------- -------\n" 692 "%9lu" " %u files\n", 693 total_usize, total_entries); 796 // " Length Date Time Name\n" 797 // "--------- ---------- ----- ----" 798 printf( " --------%21s" "-------\n" 799 "%9lu%21s" "%u files\n", 800 "", 801 total_usize, "", total_entries); 694 802 } else { 695 803 unsigned long percents = total_usize - total_size; 804 if ((long)percents < 0) 805 percents = 0; /* happens if usize < size */ 696 806 percents = percents * 100; 697 807 if (total_usize) 698 808 percents /= total_usize; 699 // " Length Method Size Ratio Date Time CRC-32 Name\n" 700 // "-------- ------ ------- ----- ---- ---- ------ ----" 701 printf( "-------- ------- --- -------\n" 702 "%8lu" "%17lu%4u%% %u files\n", 703 total_usize, total_size, (unsigned)percents, 809 // " Length Method Size Cmpr Date Time CRC-32 Name\n" 810 // "-------- ------ ------- ---- ---------- ----- -------- ----" 811 printf( "-------- ------- ----%28s" "----\n" 812 "%8lu" "%17lu%4u%%%28s" "%u files\n", 813 "", 814 total_usize, total_size, (unsigned)percents, "", 704 815 total_entries); 705 816 }
Note:
See TracChangeset
for help on using the changeset viewer.