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