unzip.c revision 234206
1/*- 2 * Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org> 3 * Copyright (c) 2007-2008 Dag-Erling Co��dan Sm��rgrav 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer 11 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: head/usr.bin/unzip/unzip.c 234206 2012-04-13 06:15:51Z kevlo $ 29 * 30 * This file would be much shorter if we didn't care about command-line 31 * compatibility with Info-ZIP's UnZip, which requires us to duplicate 32 * parts of libarchive in order to gain more detailed control of its 33 * behaviour for the purpose of implementing the -n, -o, -L and -a 34 * options. 35 */ 36 37#include <sys/queue.h> 38#include <sys/stat.h> 39 40#include <ctype.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <fnmatch.h> 44#include <stdarg.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <unistd.h> 49 50#include <archive.h> 51#include <archive_entry.h> 52 53/* command-line options */ 54static int a_opt; /* convert EOL */ 55static int C_opt; /* match case-insensitively */ 56static int c_opt; /* extract to stdout */ 57static const char *d_arg; /* directory */ 58static int f_opt; /* update existing files only */ 59static int j_opt; /* junk directories */ 60static int L_opt; /* lowercase names */ 61static int n_opt; /* never overwrite */ 62static int o_opt; /* always overwrite */ 63static int p_opt; /* extract to stdout, quiet */ 64static int q_opt; /* quiet */ 65static int t_opt; /* test */ 66static int u_opt; /* update */ 67static int v_opt; /* verbose/list */ 68static int Z1_opt; /* zipinfo mode list files only */ 69 70/* time when unzip started */ 71static time_t now; 72 73/* debug flag */ 74static int unzip_debug; 75 76/* zipinfo mode */ 77static int zipinfo_mode; 78 79/* running on tty? */ 80static int tty; 81 82/* convenience macro */ 83/* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */ 84#define ac(call) \ 85 do { \ 86 int acret = (call); \ 87 if (acret != ARCHIVE_OK) \ 88 errorx("%s", archive_error_string(a)); \ 89 } while (0) 90 91/* 92 * Indicates that last info() did not end with EOL. This helps error() et 93 * al. avoid printing an error message on the same line as an incomplete 94 * informational message. 95 */ 96static int noeol; 97 98/* fatal error message + errno */ 99static void 100error(const char *fmt, ...) 101{ 102 va_list ap; 103 104 if (noeol) 105 fprintf(stdout, "\n"); 106 fflush(stdout); 107 fprintf(stderr, "unzip: "); 108 va_start(ap, fmt); 109 vfprintf(stderr, fmt, ap); 110 va_end(ap); 111 fprintf(stderr, ": %s\n", strerror(errno)); 112 exit(1); 113} 114 115/* fatal error message, no errno */ 116static void 117errorx(const char *fmt, ...) 118{ 119 va_list ap; 120 121 if (noeol) 122 fprintf(stdout, "\n"); 123 fflush(stdout); 124 fprintf(stderr, "unzip: "); 125 va_start(ap, fmt); 126 vfprintf(stderr, fmt, ap); 127 va_end(ap); 128 fprintf(stderr, "\n"); 129 exit(1); 130} 131 132#if 0 133/* non-fatal error message + errno */ 134static void 135warning(const char *fmt, ...) 136{ 137 va_list ap; 138 139 if (noeol) 140 fprintf(stdout, "\n"); 141 fflush(stdout); 142 fprintf(stderr, "unzip: "); 143 va_start(ap, fmt); 144 vfprintf(stderr, fmt, ap); 145 va_end(ap); 146 fprintf(stderr, ": %s\n", strerror(errno)); 147} 148#endif 149 150/* non-fatal error message, no errno */ 151static void 152warningx(const char *fmt, ...) 153{ 154 va_list ap; 155 156 if (noeol) 157 fprintf(stdout, "\n"); 158 fflush(stdout); 159 fprintf(stderr, "unzip: "); 160 va_start(ap, fmt); 161 vfprintf(stderr, fmt, ap); 162 va_end(ap); 163 fprintf(stderr, "\n"); 164} 165 166/* informational message (if not -q) */ 167static void 168info(const char *fmt, ...) 169{ 170 va_list ap; 171 172 if (q_opt && !unzip_debug) 173 return; 174 va_start(ap, fmt); 175 vfprintf(stdout, fmt, ap); 176 va_end(ap); 177 fflush(stdout); 178 179 if (*fmt == '\0') 180 noeol = 1; 181 else 182 noeol = fmt[strlen(fmt) - 1] != '\n'; 183} 184 185/* debug message (if unzip_debug) */ 186static void 187debug(const char *fmt, ...) 188{ 189 va_list ap; 190 191 if (!unzip_debug) 192 return; 193 va_start(ap, fmt); 194 vfprintf(stderr, fmt, ap); 195 va_end(ap); 196 fflush(stderr); 197 198 if (*fmt == '\0') 199 noeol = 1; 200 else 201 noeol = fmt[strlen(fmt) - 1] != '\n'; 202} 203 204/* duplicate a path name, possibly converting to lower case */ 205static char * 206pathdup(const char *path) 207{ 208 char *str; 209 size_t i, len; 210 211 len = strlen(path); 212 while (len && path[len - 1] == '/') 213 len--; 214 if ((str = malloc(len + 1)) == NULL) { 215 errno = ENOMEM; 216 error("malloc()"); 217 } 218 if (L_opt) { 219 for (i = 0; i < len; ++i) 220 str[i] = tolower((unsigned char)path[i]); 221 } else { 222 memcpy(str, path, len); 223 } 224 str[len] = '\0'; 225 226 return (str); 227} 228 229/* concatenate two path names */ 230static char * 231pathcat(const char *prefix, const char *path) 232{ 233 char *str; 234 size_t prelen, len; 235 236 prelen = prefix ? strlen(prefix) + 1 : 0; 237 len = strlen(path) + 1; 238 if ((str = malloc(prelen + len)) == NULL) { 239 errno = ENOMEM; 240 error("malloc()"); 241 } 242 if (prefix) { 243 memcpy(str, prefix, prelen); /* includes zero */ 244 str[prelen - 1] = '/'; /* splat zero */ 245 } 246 memcpy(str + prelen, path, len); /* includes zero */ 247 248 return (str); 249} 250 251/* 252 * Pattern lists for include / exclude processing 253 */ 254struct pattern { 255 STAILQ_ENTRY(pattern) link; 256 char pattern[]; 257}; 258 259STAILQ_HEAD(pattern_list, pattern); 260static struct pattern_list include = STAILQ_HEAD_INITIALIZER(include); 261static struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude); 262 263/* 264 * Add an entry to a pattern list 265 */ 266static void 267add_pattern(struct pattern_list *list, const char *pattern) 268{ 269 struct pattern *entry; 270 size_t len; 271 272 debug("adding pattern '%s'\n", pattern); 273 len = strlen(pattern); 274 if ((entry = malloc(sizeof *entry + len + 1)) == NULL) { 275 errno = ENOMEM; 276 error("malloc()"); 277 } 278 memcpy(entry->pattern, pattern, len + 1); 279 STAILQ_INSERT_TAIL(list, entry, link); 280} 281 282/* 283 * Match a string against a list of patterns 284 */ 285static int 286match_pattern(struct pattern_list *list, const char *str) 287{ 288 struct pattern *entry; 289 290 STAILQ_FOREACH(entry, list, link) { 291 if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0) 292 return (1); 293 } 294 return (0); 295} 296 297/* 298 * Verify that a given pathname is in the include list and not in the 299 * exclude list. 300 */ 301static int 302accept_pathname(const char *pathname) 303{ 304 305 if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname)) 306 return (0); 307 if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname)) 308 return (0); 309 return (1); 310} 311 312/* 313 * Create the specified directory with the specified mode, taking certain 314 * precautions on they way. 315 */ 316static void 317make_dir(const char *path, int mode) 318{ 319 struct stat sb; 320 321 if (lstat(path, &sb) == 0) { 322 if (S_ISDIR(sb.st_mode)) 323 return; 324 /* 325 * Normally, we should either ask the user about removing 326 * the non-directory of the same name as a directory we 327 * wish to create, or respect the -n or -o command-line 328 * options. However, this may lead to a later failure or 329 * even compromise (if this non-directory happens to be a 330 * symlink to somewhere unsafe), so we don't. 331 */ 332 333 /* 334 * Don't check unlink() result; failure will cause mkdir() 335 * to fail later, which we will catch. 336 */ 337 (void)unlink(path); 338 } 339 if (mkdir(path, mode) != 0 && errno != EEXIST) 340 error("mkdir('%s')", path); 341} 342 343/* 344 * Ensure that all directories leading up to (but not including) the 345 * specified path exist. 346 * 347 * XXX inefficient + modifies the file in-place 348 */ 349static void 350make_parent(char *path) 351{ 352 struct stat sb; 353 char *sep; 354 355 sep = strrchr(path, '/'); 356 if (sep == NULL || sep == path) 357 return; 358 *sep = '\0'; 359 if (lstat(path, &sb) == 0) { 360 if (S_ISDIR(sb.st_mode)) { 361 *sep = '/'; 362 return; 363 } 364 unlink(path); 365 } 366 make_parent(path); 367 mkdir(path, 0755); 368 *sep = '/'; 369 370#if 0 371 for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) { 372 /* root in case of absolute d_arg */ 373 if (sep == path) 374 continue; 375 *sep = '\0'; 376 make_dir(path, 0755); 377 *sep = '/'; 378 } 379#endif 380} 381 382/* 383 * Extract a directory. 384 */ 385static void 386extract_dir(struct archive *a, struct archive_entry *e, const char *path) 387{ 388 int mode; 389 390 mode = archive_entry_mode(e) & 0777; 391 if (mode == 0) 392 mode = 0755; 393 394 /* 395 * Some zipfiles contain directories with weird permissions such 396 * as 0644 or 0444. This can cause strange issues such as being 397 * unable to extract files into the directory we just created, or 398 * the user being unable to remove the directory later without 399 * first manually changing its permissions. Therefore, we whack 400 * the permissions into shape, assuming that the user wants full 401 * access and that anyone who gets read access also gets execute 402 * access. 403 */ 404 mode |= 0700; 405 if (mode & 0040) 406 mode |= 0010; 407 if (mode & 0004) 408 mode |= 0001; 409 410 info("d %s\n", path); 411 make_dir(path, mode); 412 ac(archive_read_data_skip(a)); 413} 414 415static unsigned char buffer[8192]; 416static char spinner[] = { '|', '/', '-', '\\' }; 417 418static int 419handle_existing_file(char **path) 420{ 421 size_t alen; 422 ssize_t len; 423 char buf[4]; 424 425 for (;;) { 426 fprintf(stderr, 427 "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", 428 *path); 429 if (fgets(buf, sizeof(buf), stdin) == NULL) { 430 clearerr(stdin); 431 printf("NULL\n(EOF or read error, " 432 "treating as \"[N]one\"...)\n"); 433 n_opt = 1; 434 return -1; 435 } 436 switch (*buf) { 437 case 'A': 438 o_opt = 1; 439 /* FALLTHROUGH */ 440 case 'y': 441 case 'Y': 442 (void)unlink(*path); 443 return 1; 444 case 'N': 445 n_opt = 1; 446 /* FALLTHROUGH */ 447 case 'n': 448 return -1; 449 case 'r': 450 case 'R': 451 printf("New name: "); 452 fflush(stdout); 453 free(*path); 454 *path = NULL; 455 alen = 0; 456 len = getdelim(path, &alen, '\n', stdin); 457 if ((*path)[len - 1] == '\n') 458 (*path)[len - 1] = '\0'; 459 return 0; 460 default: 461 break; 462 } 463 } 464} 465 466/* 467 * Extract a regular file. 468 */ 469static void 470extract_file(struct archive *a, struct archive_entry *e, char **path) 471{ 472 int mode; 473 time_t mtime; 474 struct stat sb; 475 struct timeval tv[2]; 476 int cr, fd, text, warn, check; 477 ssize_t len; 478 unsigned char *p, *q, *end; 479 480 mode = archive_entry_mode(e) & 0777; 481 if (mode == 0) 482 mode = 0644; 483 mtime = archive_entry_mtime(e); 484 485 /* look for existing file of same name */ 486recheck: 487 if (lstat(*path, &sb) == 0) { 488 if (u_opt || f_opt) { 489 /* check if up-to-date */ 490 if (S_ISREG(sb.st_mode) && sb.st_mtime >= mtime) 491 return; 492 (void)unlink(*path); 493 } else if (o_opt) { 494 /* overwrite */ 495 (void)unlink(*path); 496 } else if (n_opt) { 497 /* do not overwrite */ 498 return; 499 } else { 500 check = handle_existing_file(path); 501 if (check == 0) 502 goto recheck; 503 if (check == -1) 504 return; /* do not overwrite */ 505 } 506 } else { 507 if (f_opt) 508 return; 509 } 510 511 if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) 512 error("open('%s')", *path); 513 514 /* loop over file contents and write to disk */ 515 info(" extracting: %s", *path); 516 text = a_opt; 517 warn = 0; 518 cr = 0; 519 for (int n = 0; ; n++) { 520 if (tty && (n % 4) == 0) 521 info(" %c\b\b", spinner[(n / 4) % sizeof spinner]); 522 523 len = archive_read_data(a, buffer, sizeof buffer); 524 525 if (len < 0) 526 ac(len); 527 528 /* left over CR from previous buffer */ 529 if (a_opt && cr) { 530 if (len == 0 || buffer[0] != '\n') 531 if (write(fd, "\r", 1) != 1) 532 error("write('%s')", *path); 533 cr = 0; 534 } 535 536 /* EOF */ 537 if (len == 0) 538 break; 539 end = buffer + len; 540 541 /* 542 * Detect whether this is a text file. The correct way to 543 * do this is to check the least significant bit of the 544 * "internal file attributes" field of the corresponding 545 * file header in the central directory, but libarchive 546 * does not read the central directory, so we have to 547 * guess by looking for non-ASCII characters in the 548 * buffer. Hopefully we won't guess wrong. If we do 549 * guess wrong, we print a warning message later. 550 */ 551 if (a_opt && n == 0) { 552 for (p = buffer; p < end; ++p) { 553 if (!isascii((unsigned char)*p)) { 554 text = 0; 555 break; 556 } 557 } 558 } 559 560 /* simple case */ 561 if (!a_opt || !text) { 562 if (write(fd, buffer, len) != len) 563 error("write('%s')", *path); 564 continue; 565 } 566 567 /* hard case: convert \r\n to \n (sigh...) */ 568 for (p = buffer; p < end; p = q + 1) { 569 for (q = p; q < end; q++) { 570 if (!warn && !isascii(*q)) { 571 warningx("%s may be corrupted due" 572 " to weak text file detection" 573 " heuristic", *path); 574 warn = 1; 575 } 576 if (q[0] != '\r') 577 continue; 578 if (&q[1] == end) { 579 cr = 1; 580 break; 581 } 582 if (q[1] == '\n') 583 break; 584 } 585 if (write(fd, p, q - p) != q - p) 586 error("write('%s')", *path); 587 } 588 } 589 if (tty) 590 info(" \b\b"); 591 if (text) 592 info(" (text)"); 593 info("\n"); 594 595 /* set access and modification time */ 596 tv[0].tv_sec = now; 597 tv[0].tv_usec = 0; 598 tv[1].tv_sec = mtime; 599 tv[1].tv_usec = 0; 600 if (futimes(fd, tv) != 0) 601 error("utimes('%s')", *path); 602 if (close(fd) != 0) 603 error("close('%s')", *path); 604} 605 606/* 607 * Extract a zipfile entry: first perform some sanity checks to ensure 608 * that it is either a directory or a regular file and that the path is 609 * not absolute and does not try to break out of the current directory; 610 * then call either extract_dir() or extract_file() as appropriate. 611 * 612 * This is complicated a bit by the various ways in which we need to 613 * manipulate the path name. Case conversion (if requested by the -L 614 * option) happens first, but the include / exclude patterns are applied 615 * to the full converted path name, before the directory part of the path 616 * is removed in accordance with the -j option. Sanity checks are 617 * intentionally done earlier than they need to be, so the user will get a 618 * warning about insecure paths even for files or directories which 619 * wouldn't be extracted anyway. 620 */ 621static void 622extract(struct archive *a, struct archive_entry *e) 623{ 624 char *pathname, *realpathname; 625 mode_t filetype; 626 char *p, *q; 627 628 pathname = pathdup(archive_entry_pathname(e)); 629 filetype = archive_entry_filetype(e); 630 631 /* sanity checks */ 632 if (pathname[0] == '/' || 633 strncmp(pathname, "../", 3) == 0 || 634 strstr(pathname, "/../") != NULL) { 635 warningx("skipping insecure entry '%s'", pathname); 636 ac(archive_read_data_skip(a)); 637 free(pathname); 638 return; 639 } 640 641 /* I don't think this can happen in a zipfile.. */ 642 if (!S_ISDIR(filetype) && !S_ISREG(filetype)) { 643 warningx("skipping non-regular entry '%s'", pathname); 644 ac(archive_read_data_skip(a)); 645 free(pathname); 646 return; 647 } 648 649 /* skip directories in -j case */ 650 if (S_ISDIR(filetype) && j_opt) { 651 ac(archive_read_data_skip(a)); 652 free(pathname); 653 return; 654 } 655 656 /* apply include / exclude patterns */ 657 if (!accept_pathname(pathname)) { 658 ac(archive_read_data_skip(a)); 659 free(pathname); 660 return; 661 } 662 663 /* apply -j and -d */ 664 if (j_opt) { 665 for (p = q = pathname; *p; ++p) 666 if (*p == '/') 667 q = p + 1; 668 realpathname = pathcat(d_arg, q); 669 } else { 670 realpathname = pathcat(d_arg, pathname); 671 } 672 673 /* ensure that parent directory exists */ 674 make_parent(realpathname); 675 676 if (S_ISDIR(filetype)) 677 extract_dir(a, e, realpathname); 678 else 679 extract_file(a, e, &realpathname); 680 681 free(realpathname); 682 free(pathname); 683} 684 685static void 686extract_stdout(struct archive *a, struct archive_entry *e) 687{ 688 char *pathname; 689 mode_t filetype; 690 int cr, text, warn; 691 ssize_t len; 692 unsigned char *p, *q, *end; 693 694 pathname = pathdup(archive_entry_pathname(e)); 695 filetype = archive_entry_filetype(e); 696 697 /* I don't think this can happen in a zipfile.. */ 698 if (!S_ISDIR(filetype) && !S_ISREG(filetype)) { 699 warningx("skipping non-regular entry '%s'", pathname); 700 ac(archive_read_data_skip(a)); 701 free(pathname); 702 return; 703 } 704 705 /* skip directories in -j case */ 706 if (S_ISDIR(filetype)) { 707 ac(archive_read_data_skip(a)); 708 free(pathname); 709 return; 710 } 711 712 /* apply include / exclude patterns */ 713 if (!accept_pathname(pathname)) { 714 ac(archive_read_data_skip(a)); 715 free(pathname); 716 return; 717 } 718 719 if (c_opt) 720 info("x %s\n", pathname); 721 722 text = a_opt; 723 warn = 0; 724 cr = 0; 725 for (int n = 0; ; n++) { 726 len = archive_read_data(a, buffer, sizeof buffer); 727 728 if (len < 0) 729 ac(len); 730 731 /* left over CR from previous buffer */ 732 if (a_opt && cr) { 733 if (len == 0 || buffer[0] != '\n') { 734 if (fwrite("\r", 1, 1, stderr) != 1) 735 error("write('%s')", pathname); 736 } 737 cr = 0; 738 } 739 740 /* EOF */ 741 if (len == 0) 742 break; 743 end = buffer + len; 744 745 /* 746 * Detect whether this is a text file. The correct way to 747 * do this is to check the least significant bit of the 748 * "internal file attributes" field of the corresponding 749 * file header in the central directory, but libarchive 750 * does not read the central directory, so we have to 751 * guess by looking for non-ASCII characters in the 752 * buffer. Hopefully we won't guess wrong. If we do 753 * guess wrong, we print a warning message later. 754 */ 755 if (a_opt && n == 0) { 756 for (p = buffer; p < end; ++p) { 757 if (!isascii((unsigned char)*p)) { 758 text = 0; 759 break; 760 } 761 } 762 } 763 764 /* simple case */ 765 if (!a_opt || !text) { 766 if (fwrite(buffer, 1, len, stdout) != (size_t)len) 767 error("write('%s')", pathname); 768 continue; 769 } 770 771 /* hard case: convert \r\n to \n (sigh...) */ 772 for (p = buffer; p < end; p = q + 1) { 773 for (q = p; q < end; q++) { 774 if (!warn && !isascii(*q)) { 775 warningx("%s may be corrupted due" 776 " to weak text file detection" 777 " heuristic", pathname); 778 warn = 1; 779 } 780 if (q[0] != '\r') 781 continue; 782 if (&q[1] == end) { 783 cr = 1; 784 break; 785 } 786 if (q[1] == '\n') 787 break; 788 } 789 if (fwrite(p, 1, q - p, stdout) != (size_t)(q - p)) 790 error("write('%s')", pathname); 791 } 792 } 793 794 free(pathname); 795} 796 797/* 798 * Print the name of an entry to stdout. 799 */ 800static void 801list(struct archive *a, struct archive_entry *e) 802{ 803 char buf[20]; 804 time_t mtime; 805 806 mtime = archive_entry_mtime(e); 807 strftime(buf, sizeof(buf), "%m-%d-%g %R", localtime(&mtime)); 808 809 if (!zipinfo_mode) { 810 if (v_opt == 1) { 811 printf(" %8ju %s %s\n", 812 (uintmax_t)archive_entry_size(e), 813 buf, archive_entry_pathname(e)); 814 } else if (v_opt == 2) { 815 printf("%8ju Stored %7ju 0%% %s %08x %s\n", 816 (uintmax_t)archive_entry_size(e), 817 (uintmax_t)archive_entry_size(e), 818 buf, 819 0U, 820 archive_entry_pathname(e)); 821 } 822 } else { 823 if (Z1_opt) 824 printf("%s\n",archive_entry_pathname(e)); 825 } 826 ac(archive_read_data_skip(a)); 827} 828 829/* 830 * Extract to memory to check CRC 831 */ 832static int 833test(struct archive *a, struct archive_entry *e) 834{ 835 ssize_t len; 836 int error_count; 837 838 error_count = 0; 839 if (S_ISDIR(archive_entry_filetype(e))) 840 return 0; 841 842 info(" testing: %s\t", archive_entry_pathname(e)); 843 while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0) 844 /* nothing */; 845 if (len < 0) { 846 info(" %s\n", archive_error_string(a)); 847 ++error_count; 848 } else { 849 info(" OK\n"); 850 } 851 852 /* shouldn't be necessary, but it doesn't hurt */ 853 ac(archive_read_data_skip(a)); 854 855 return error_count; 856} 857 858 859/* 860 * Main loop: open the zipfile, iterate over its contents and decide what 861 * to do with each entry. 862 */ 863static void 864unzip(const char *fn) 865{ 866 struct archive *a; 867 struct archive_entry *e; 868 int fd, ret; 869 uintmax_t total_size, file_count, error_count; 870 871 if (strcmp(fn, "-") == 0) 872 fd = STDIN_FILENO; 873 else if ((fd = open(fn, O_RDONLY)) < 0) 874 error("%s", fn); 875 876 if ((a = archive_read_new()) == NULL) 877 error("archive_read_new failed"); 878 879 ac(archive_read_support_format_zip(a)); 880 ac(archive_read_open_fd(a, fd, 8192)); 881 882 if (!zipinfo_mode) { 883 if (!p_opt && !q_opt) 884 printf("Archive: %s\n", fn); 885 if (v_opt == 1) { 886 printf(" Length Date Time Name\n"); 887 printf(" -------- ---- ---- ----\n"); 888 } else if (v_opt == 2) { 889 printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); 890 printf("-------- ------ ------- ----- ---- ---- ------ ----\n"); 891 } 892 } 893 894 total_size = 0; 895 file_count = 0; 896 error_count = 0; 897 for (;;) { 898 ret = archive_read_next_header(a, &e); 899 if (ret == ARCHIVE_EOF) 900 break; 901 ac(ret); 902 if (!zipinfo_mode) { 903 if (t_opt) 904 error_count += test(a, e); 905 else if (v_opt) 906 list(a, e); 907 else if (p_opt || c_opt) 908 extract_stdout(a, e); 909 else 910 extract(a, e); 911 } else { 912 if (Z1_opt) 913 list(a, e); 914 } 915 916 total_size += archive_entry_size(e); 917 ++file_count; 918 } 919 920 if (zipinfo_mode) { 921 if (v_opt == 1) { 922 printf(" -------- -------\n"); 923 printf(" %8ju %ju file%s\n", 924 total_size, file_count, file_count != 1 ? "s" : ""); 925 } else if (v_opt == 2) { 926 printf("-------- ------- --- -------\n"); 927 printf("%8ju %7ju 0%% %ju file%s\n", 928 total_size, total_size, file_count, 929 file_count != 1 ? "s" : ""); 930 } 931 } 932 933 ac(archive_read_close(a)); 934 (void)archive_read_finish(a); 935 936 if (fd != STDIN_FILENO && close(fd) != 0) 937 error("%s", fn); 938 939 if (t_opt) { 940 if (error_count > 0) { 941 errorx("%d checksum error(s) found.", error_count); 942 } 943 else { 944 printf("No errors detected in compressed data of %s.\n", 945 fn); 946 } 947 } 948} 949 950static void 951usage(void) 952{ 953 954 fprintf(stderr, "usage: unzip [-aCcfjLlnopqtuvZ1] [-d dir] [-x pattern] zipfile\n"); 955 exit(1); 956} 957 958static int 959getopts(int argc, char *argv[]) 960{ 961 int opt; 962 963 optreset = optind = 1; 964 while ((opt = getopt(argc, argv, "aCcd:fjLlnopqtuvx:Z1")) != -1) 965 switch (opt) { 966 case '1': 967 Z1_opt = 1; 968 break; 969 case 'a': 970 a_opt = 1; 971 break; 972 case 'C': 973 C_opt = 1; 974 break; 975 case 'c': 976 c_opt = 1; 977 break; 978 case 'd': 979 d_arg = optarg; 980 break; 981 case 'f': 982 f_opt = 1; 983 break; 984 case 'j': 985 j_opt = 1; 986 break; 987 case 'L': 988 L_opt = 1; 989 break; 990 case 'l': 991 if (v_opt == 0) 992 v_opt = 1; 993 break; 994 case 'n': 995 n_opt = 1; 996 break; 997 case 'o': 998 o_opt = 1; 999 q_opt = 1; 1000 break; 1001 case 'p': 1002 p_opt = 1; 1003 break; 1004 case 'q': 1005 q_opt = 1; 1006 break; 1007 case 't': 1008 t_opt = 1; 1009 break; 1010 case 'u': 1011 u_opt = 1; 1012 break; 1013 case 'v': 1014 v_opt = 2; 1015 break; 1016 case 'x': 1017 add_pattern(&exclude, optarg); 1018 break; 1019 case 'Z': 1020 zipinfo_mode = 1; 1021 break; 1022 default: 1023 usage(); 1024 } 1025 1026 return (optind); 1027} 1028 1029int 1030main(int argc, char *argv[]) 1031{ 1032 const char *zipfile; 1033 int nopts; 1034 1035 if (isatty(STDOUT_FILENO)) 1036 tty = 1; 1037 1038 if (getenv("UNZIP_DEBUG") != NULL) 1039 unzip_debug = 1; 1040 for (int i = 0; i < argc; ++i) 1041 debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n'); 1042 1043 /* 1044 * Info-ZIP's unzip(1) expects certain options to come before the 1045 * zipfile name, and others to come after - though it does not 1046 * enforce this. For simplicity, we accept *all* options both 1047 * before and after the zipfile name. 1048 */ 1049 nopts = getopts(argc, argv); 1050 1051 /* 1052 * When more of the zipinfo mode options are implemented, this 1053 * will need to change. 1054 */ 1055 if (zipinfo_mode && !Z1_opt) { 1056 printf("Zipinfo mode needs additional options\n"); 1057 exit(1); 1058 } 1059 1060 if (argc <= nopts) 1061 usage(); 1062 zipfile = argv[nopts++]; 1063 1064 while (nopts < argc && *argv[nopts] != '-') 1065 add_pattern(&include, argv[nopts++]); 1066 1067 nopts--; /* fake argv[0] */ 1068 nopts += getopts(argc - nopts, argv + nopts); 1069 1070 if (n_opt + o_opt + u_opt > 1) 1071 errorx("-n, -o and -u are contradictory"); 1072 1073 time(&now); 1074 1075 unzip(zipfile); 1076 1077 exit(0); 1078} 1079