1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org> 5 * Copyright (c) 2007-2008 Dag-Erling Smørgrav 6 * All rights reserved. 7 */ 8 9 #include "bsdunzip_platform.h" 10 11 #include "la_queue.h" 12 #include "la_getline.h" 13 #ifdef HAVE_SYS_STAT_H 14 #include <sys/stat.h> 15 #endif 16 17 #ifdef HAVE_CTYPE_H 18 #include <ctype.h> 19 #endif 20 #ifdef HAVE_ERRNO_H 21 #include <errno.h> 22 #endif 23 #ifdef HAVE_FCNTL_H 24 #include <fcntl.h> 25 #endif 26 #ifdef HAVE_FNMATCH_H 27 #include <fnmatch.h> 28 #endif 29 #ifdef HAVE_LOCALE_H 30 #include <locale.h> 31 #endif 32 #ifdef HAVE_SIGNAL_H 33 #include <signal.h> 34 #endif 35 #ifdef HAVE_STDARG_H 36 #include <stdarg.h> 37 #endif 38 #include <stdio.h> 39 #ifdef HAVE_STDLIB_H 40 #include <stdlib.h> 41 #endif 42 #ifdef HAVE_STRING_H 43 #include <string.h> 44 #endif 45 #ifdef HAVE_UNISTD_H 46 #include <unistd.h> 47 #endif 48 #if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \ 49 (!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES))) 50 #ifdef HAVE_SYS_TIME_H 51 #include <sys/time.h> 52 #endif 53 #endif 54 #ifdef HAVE_GETOPT_OPTRESET 55 #include <getopt.h> 56 #endif 57 58 #include "bsdunzip.h" 59 #include "passphrase.h" 60 #include "lafe_err.h" 61 62 /* command-line options */ 63 static int a_opt; /* convert EOL */ 64 static int C_opt; /* match case-insensitively */ 65 static int c_opt; /* extract to stdout */ 66 static const char *d_arg; /* directory */ 67 static int f_opt; /* update existing files only */ 68 static const char *O_arg; /* encoding */ 69 static int j_opt; /* junk directories */ 70 static int L_opt; /* lowercase names */ 71 static int n_opt; /* never overwrite */ 72 static int o_opt; /* always overwrite */ 73 static int p_opt; /* extract to stdout, quiet */ 74 static const char *P_arg; /* passphrase */ 75 static int q_opt; /* quiet */ 76 static int t_opt; /* test */ 77 static int u_opt; /* update */ 78 static int v_opt; /* verbose/list */ 79 static const char *y_str = ""; /* 4 digit year */ 80 static int Z1_opt; /* zipinfo mode list files only */ 81 static int version_opt; /* version string */ 82 83 /* debug flag */ 84 static int unzip_debug; 85 86 /* zipinfo mode */ 87 static int zipinfo_mode; 88 89 /* running on tty? */ 90 static int tty; 91 92 /* processing exclude list */ 93 static int unzip_exclude_mode = 0; 94 95 int bsdunzip_optind; 96 97 /* convenience macro */ 98 /* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */ 99 #define ac(call) \ 100 do { \ 101 int acret = (call); \ 102 if (acret != ARCHIVE_OK) \ 103 errorx("%s", archive_error_string(a)); \ 104 } while (0) 105 106 /* 107 * Indicates that last info() did not end with EOL. This helps error() et 108 * al. avoid printing an error message on the same line as an incomplete 109 * informational message. 110 */ 111 static int noeol; 112 113 /* for an interactive passphrase input */ 114 static char *passphrase_buf; 115 116 /* fatal error message + errno */ 117 static void __LA_NORETURN 118 error(const char *fmt, ...) 119 { 120 va_list ap; 121 122 if (noeol) 123 fprintf(stdout, "\n"); 124 fflush(stdout); 125 fprintf(stderr, "unzip: "); 126 va_start(ap, fmt); 127 vfprintf(stderr, fmt, ap); 128 va_end(ap); 129 fprintf(stderr, ": %s\n", strerror(errno)); 130 exit(EXIT_FAILURE); 131 } 132 133 /* fatal error message, no errno */ 134 static void __LA_NORETURN 135 errorx(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, "\n"); 147 exit(EXIT_FAILURE); 148 } 149 150 /* non-fatal error message + errno */ 151 static void 152 warning(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, ": %s\n", strerror(errno)); 164 } 165 166 /* non-fatal error message, no errno */ 167 static void 168 warningx(const char *fmt, ...) 169 { 170 va_list ap; 171 172 if (noeol) 173 fprintf(stdout, "\n"); 174 fflush(stdout); 175 fprintf(stderr, "unzip: "); 176 va_start(ap, fmt); 177 vfprintf(stderr, fmt, ap); 178 va_end(ap); 179 fprintf(stderr, "\n"); 180 } 181 182 /* informational message (if not -q) */ 183 static void 184 info(const char *fmt, ...) 185 { 186 va_list ap; 187 188 if (q_opt && !unzip_debug) 189 return; 190 va_start(ap, fmt); 191 vfprintf(stdout, fmt, ap); 192 va_end(ap); 193 fflush(stdout); 194 195 if (*fmt == '\0') 196 noeol = 1; 197 else 198 noeol = fmt[strlen(fmt) - 1] != '\n'; 199 } 200 201 /* debug message (if unzip_debug) */ 202 static void 203 debug(const char *fmt, ...) 204 { 205 va_list ap; 206 207 if (!unzip_debug) 208 return; 209 va_start(ap, fmt); 210 vfprintf(stderr, fmt, ap); 211 va_end(ap); 212 fflush(stderr); 213 214 if (*fmt == '\0') 215 noeol = 1; 216 else 217 noeol = fmt[strlen(fmt) - 1] != '\n'; 218 } 219 220 /* duplicate a path name, possibly converting to lower case */ 221 static char * 222 pathdup(const char *path) 223 { 224 char *str; 225 size_t i, len; 226 227 if (path == NULL || path[0] == '\0') 228 return (NULL); 229 230 len = strlen(path); 231 while (len && path[len - 1] == '/') 232 len--; 233 if ((str = malloc(len + 1)) == NULL) { 234 errno = ENOMEM; 235 error("malloc()"); 236 } 237 if (L_opt) { 238 for (i = 0; i < len; ++i) 239 str[i] = (char)tolower((unsigned char)path[i]); 240 } else { 241 memcpy(str, path, len); 242 } 243 str[len] = '\0'; 244 245 return (str); 246 } 247 248 /* concatenate two path names */ 249 static char * 250 pathcat(const char *prefix, const char *path) 251 { 252 char *str; 253 size_t prelen, len; 254 255 prelen = prefix ? strlen(prefix) + 1 : 0; 256 len = strlen(path) + 1; 257 if ((str = malloc(prelen + len)) == NULL) { 258 errno = ENOMEM; 259 error("malloc()"); 260 } 261 if (prefix) { 262 memcpy(str, prefix, prelen); /* includes zero */ 263 str[prelen - 1] = '/'; /* splat zero */ 264 } 265 memcpy(str + prelen, path, len); /* includes zero */ 266 267 return (str); 268 } 269 270 /* 271 * Pattern lists for include / exclude processing 272 */ 273 struct pattern { 274 STAILQ_ENTRY(pattern) link; 275 char pattern[]; 276 }; 277 278 STAILQ_HEAD(pattern_list, pattern); 279 static struct pattern_list include = STAILQ_HEAD_INITIALIZER(include); 280 static struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude); 281 282 /* 283 * Add an entry to a pattern list 284 */ 285 static void 286 add_pattern(struct pattern_list *list, const char *pattern) 287 { 288 struct pattern *entry; 289 size_t len; 290 291 debug("adding pattern '%s'\n", pattern); 292 len = strlen(pattern); 293 if ((entry = malloc(sizeof *entry + len + 1)) == NULL) { 294 errno = ENOMEM; 295 error("malloc()"); 296 } 297 memcpy(entry->pattern, pattern, len + 1); 298 STAILQ_INSERT_TAIL(list, entry, link); 299 } 300 301 /* 302 * Match a string against a list of patterns 303 */ 304 static int 305 match_pattern(struct pattern_list *list, const char *str) 306 { 307 struct pattern *entry; 308 309 STAILQ_FOREACH(entry, list, link) { 310 #ifdef HAVE_FNMATCH 311 if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0) 312 return (1); 313 #else 314 #error "Unsupported platform: fnmatch() is required" 315 #endif 316 } 317 return (0); 318 } 319 320 /* 321 * Verify that a given pathname is in the include list and not in the 322 * exclude list. 323 */ 324 static int 325 accept_pathname(const char *pathname) 326 { 327 328 if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname)) 329 return (0); 330 if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname)) 331 return (0); 332 return (1); 333 } 334 335 /* 336 * Create the specified directory with the specified mode, taking certain 337 * precautions on they way. 338 */ 339 static void 340 make_dir(const char *path, int mode) 341 { 342 struct stat sb; 343 344 if (lstat(path, &sb) == 0) { 345 if (S_ISDIR(sb.st_mode)) 346 return; 347 /* 348 * Normally, we should either ask the user about removing 349 * the non-directory of the same name as a directory we 350 * wish to create, or respect the -n or -o command-line 351 * options. However, this may lead to a later failure or 352 * even compromise (if this non-directory happens to be a 353 * symlink to somewhere unsafe), so we don't. 354 */ 355 356 /* 357 * Don't check unlink() result; failure will cause mkdir() 358 * to fail later, which we will catch. 359 */ 360 (void)unlink(path); 361 } 362 if (mkdir(path, (mode_t)mode) != 0 && errno != EEXIST) 363 error("mkdir('%s')", path); 364 } 365 366 /* 367 * Ensure that all directories leading up to (but not including) the 368 * specified path exist. 369 * 370 * XXX inefficient + modifies the file in-place 371 */ 372 static void 373 make_parent(char *path) 374 { 375 struct stat sb; 376 char *sep; 377 378 sep = strrchr(path, '/'); 379 if (sep == NULL || sep == path) 380 return; 381 *sep = '\0'; 382 if (lstat(path, &sb) == 0) { 383 if (S_ISDIR(sb.st_mode)) { 384 *sep = '/'; 385 return; 386 } 387 unlink(path); 388 } 389 make_parent(path); 390 mkdir(path, 0755); 391 *sep = '/'; 392 393 #if 0 394 for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) { 395 /* root in case of absolute d_arg */ 396 if (sep == path) 397 continue; 398 *sep = '\0'; 399 make_dir(path, 0755); 400 *sep = '/'; 401 } 402 #endif 403 } 404 405 /* 406 * Extract a directory. 407 */ 408 static void 409 extract_dir(struct archive *a, struct archive_entry *e, const char *path) 410 { 411 int mode; 412 413 /* 414 * Dropbox likes to create '/' directory entries, just ignore 415 * such junk. 416 */ 417 if (*path == '\0') 418 return; 419 420 mode = archive_entry_mode(e) & 0777; 421 if (mode == 0) 422 mode = 0755; 423 424 /* 425 * Some zipfiles contain directories with weird permissions such 426 * as 0644 or 0444. This can cause strange issues such as being 427 * unable to extract files into the directory we just created, or 428 * the user being unable to remove the directory later without 429 * first manually changing its permissions. Therefore, we whack 430 * the permissions into shape, assuming that the user wants full 431 * access and that anyone who gets read access also gets execute 432 * access. 433 */ 434 mode |= 0700; 435 if (mode & 0040) 436 mode |= 0010; 437 if (mode & 0004) 438 mode |= 0001; 439 440 info(" creating: %s/\n", path); 441 make_dir(path, mode); 442 ac(archive_read_data_skip(a)); 443 } 444 445 static unsigned char buffer[8192]; 446 static char spinner[] = { '|', '/', '-', '\\' }; 447 448 static int 449 handle_existing_file(char **path) 450 { 451 size_t alen; 452 ssize_t len; 453 char buf[4]; 454 455 for (;;) { 456 fprintf(stderr, 457 "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", 458 *path); 459 if (fgets(buf, sizeof(buf), stdin) == NULL) 460 goto stdin_err; 461 switch (*buf) { 462 case 'A': 463 o_opt = 1; 464 /* FALLTHROUGH */ 465 case 'y': 466 case 'Y': 467 (void)unlink(*path); 468 return 1; 469 case 'N': 470 n_opt = 1; 471 /* FALLTHROUGH */ 472 case 'n': 473 return -1; 474 case 'r': 475 case 'R': 476 printf("New name: "); 477 fflush(stdout); 478 free(*path); 479 *path = NULL; 480 alen = 0; 481 len = getline(path, &alen, stdin); 482 if (len < 1) 483 goto stdin_err; 484 if ((*path)[len - 1] == '\n') 485 (*path)[len - 1] = '\0'; 486 return 0; 487 default: 488 break; 489 } 490 } 491 stdin_err: 492 clearerr(stdin); 493 printf("NULL\n(EOF or read error, " 494 "treating as \"[N]one\"...)\n"); 495 n_opt = 1; 496 return -1; 497 } 498 499 /* 500 * Detect binary files by a combination of character white list and 501 * black list. NUL bytes and other control codes without use in text files 502 * result directly in switching the file to binary mode. Otherwise, at least 503 * one white-listed byte has to be found. 504 * 505 * Black-listed: 0..6, 14..25, 28..31 506 * 0xf3ffc07f = 11110011111111111100000001111111b 507 * White-listed: 9..10, 13, >= 32 508 * 0x00002600 = 00000000000000000010011000000000b 509 * 510 * See the proginfo/txtvsbin.txt in the zip sources for a detailed discussion. 511 */ 512 #define BYTE_IS_BINARY(x) ((x) < 32 && (0xf3ffc07fU & (1U << (x)))) 513 #define BYTE_IS_TEXT(x) ((x) >= 32 || (0x00002600U & (1U << (x)))) 514 515 static int 516 check_binary(const unsigned char *buf, size_t len) 517 { 518 int rv; 519 for (rv = 1; len--; ++buf) { 520 if (BYTE_IS_BINARY(*buf)) 521 return 1; 522 if (BYTE_IS_TEXT(*buf)) 523 rv = 0; 524 } 525 526 return rv; 527 } 528 529 /* 530 * Extract to a file descriptor 531 */ 532 static int 533 extract2fd(struct archive *a, char *pathname, int fd) 534 { 535 int cr, text, warn; 536 ssize_t len; 537 unsigned char *p, *q, *end; 538 539 text = a_opt; 540 warn = 0; 541 cr = 0; 542 543 /* loop over file contents and write to fd */ 544 for (int n = 0; ; n++) { 545 if (fd != STDOUT_FILENO) 546 if (tty && (n % 4) == 0) 547 info(" %c\b\b", spinner[(n / 4) % sizeof spinner]); 548 549 len = archive_read_data(a, buffer, sizeof buffer); 550 551 if (len < 0) 552 ac(len); 553 554 /* left over CR from previous buffer */ 555 if (a_opt && cr) { 556 if (len == 0 || buffer[0] != '\n') 557 if (write(fd, "\r", 1) != 1) 558 error("write('%s')", pathname); 559 cr = 0; 560 } 561 562 /* EOF */ 563 if (len == 0) 564 break; 565 end = buffer + len; 566 567 /* 568 * Detect whether this is a text file. The correct way to 569 * do this is to check the least significant bit of the 570 * "internal file attributes" field of the corresponding 571 * file header in the central directory, but libarchive 572 * does not provide access to this field, so we have to 573 * guess by looking for non-ASCII characters in the 574 * buffer. Hopefully we won't guess wrong. If we do 575 * guess wrong, we print a warning message later. 576 */ 577 if (a_opt && n == 0) { 578 if (check_binary(buffer, len)) 579 text = 0; 580 } 581 582 /* simple case */ 583 if (!a_opt || !text) { 584 if (write(fd, buffer, len) != len) 585 error("write('%s')", pathname); 586 continue; 587 } 588 589 /* hard case: convert \r\n to \n (sigh...) */ 590 for (p = buffer; p < end; p = q + 1) { 591 for (q = p; q < end; q++) { 592 if (!warn && BYTE_IS_BINARY(*q)) { 593 warningx("%s may be corrupted due" 594 " to weak text file detection" 595 " heuristic", pathname); 596 warn = 1; 597 } 598 if (q[0] != '\r') 599 continue; 600 if (&q[1] == end) { 601 cr = 1; 602 break; 603 } 604 if (q[1] == '\n') 605 break; 606 } 607 if (write(fd, p, q - p) != q - p) 608 error("write('%s')", pathname); 609 } 610 } 611 612 return text; 613 } 614 615 /* 616 * Extract a regular file. 617 */ 618 static void 619 extract_file(struct archive *a, struct archive_entry *e, char **path) 620 { 621 int mode; 622 struct timespec mtime; 623 struct stat sb; 624 int fd, check, text; 625 const char *linkname; 626 #if defined(HAVE_UTIMENSAT) || defined(HAVE_FUTIMENS) 627 struct timespec ts[2]; 628 #endif 629 #if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \ 630 (!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES))) 631 struct timeval times[2]; 632 #endif 633 634 mode = archive_entry_mode(e) & 0777; 635 if (mode == 0) 636 mode = 0644; 637 mtime.tv_sec = archive_entry_mtime(e); 638 mtime.tv_nsec = archive_entry_mtime_nsec(e); 639 640 /* look for existing file of same name */ 641 recheck: 642 if (lstat(*path, &sb) == 0) { 643 if (u_opt || f_opt) { 644 /* check if up-to-date */ 645 if (S_ISREG(sb.st_mode) && ( 646 #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 647 sb.st_mtimespec.tv_sec > mtime.tv_sec || 648 (sb.st_mtimespec.tv_sec == mtime.tv_sec && 649 sb.st_mtimespec.tv_nsec >= mtime.tv_nsec) 650 #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 651 sb.st_mtim.tv_sec > mtime.tv_sec || 652 (sb.st_mtim.tv_sec == mtime.tv_sec && 653 sb.st_mtim.tv_nsec >= mtime.tv_nsec) 654 #elif HAVE_STRUCT_STAT_ST_MTIME_N 655 sb.st_mtime > mtime.tv_sec || 656 (sb.st_mtime == mtime.tv_sec && 657 sb.st_mtime_n => mtime.tv_nsec) 658 #elif HAVE_STRUCT_STAT_ST_MTIME_USEC 659 sb.st_mtime > mtime.tv_sec || 660 (sb.st_mtime == mtime.tv_sec && 661 sb.st_mtime_usec => mtime.tv_nsec / 1000) 662 #else 663 sb.st_mtime > mtime.tv_sec 664 #endif 665 )) 666 return; 667 (void)unlink(*path); 668 } else if (o_opt) { 669 /* overwrite */ 670 (void)unlink(*path); 671 } else if (n_opt) { 672 /* do not overwrite */ 673 return; 674 } else { 675 check = handle_existing_file(path); 676 if (check == 0) 677 goto recheck; 678 if (check == -1) 679 return; /* do not overwrite */ 680 } 681 } else { 682 if (f_opt) 683 return; 684 } 685 686 #if defined(HAVE_UTIMENSAT) || defined(HAVE_FUTIMENS) 687 ts[0].tv_sec = 0; 688 ts[0].tv_nsec = UTIME_NOW; 689 ts[1] = mtime; 690 #endif 691 #if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \ 692 (!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES))) 693 times[0].tv_sec = 0; 694 times[0].tv_usec = -1; 695 times[1].tv_sec = mtime.tv_sec; 696 times[1].tv_usec = mtime.tv_nsec / 1000; 697 #endif 698 699 /* process symlinks */ 700 linkname = archive_entry_symlink(e); 701 if (linkname != NULL) { 702 if (symlink(linkname, *path) != 0) 703 error("symlink('%s')", *path); 704 info(" extracting: %s -> %s\n", *path, linkname); 705 #ifdef HAVE_LCHMOD 706 if (lchmod(*path, (mode_t)mode) != 0) 707 warning("Cannot set mode for '%s'", *path); 708 #endif 709 /* set access and modification time */ 710 #if defined(HAVE_UTIMENSAT) 711 if (utimensat(AT_FDCWD, *path, ts, AT_SYMLINK_NOFOLLOW) != 0) 712 warning("utimensat('%s')", *path); 713 #elif defined(HAVE_LUTIMES) 714 gettimeofday(×[0], NULL); 715 if (lutimes(*path, times) != 0) 716 warning("lutimes('%s')", *path); 717 #endif 718 return; 719 } 720 721 if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) 722 error("open('%s')", *path); 723 724 info(" extracting: %s", *path); 725 726 text = extract2fd(a, *path, fd); 727 728 if (tty) 729 info(" \b\b"); 730 if (text) 731 info(" (text)"); 732 info("\n"); 733 734 /* set access and modification time */ 735 #if defined(HAVE_FUTIMENS) 736 if (futimens(fd, ts) != 0) 737 error("futimens('%s')", *path); 738 #elif defined(HAVE_FUTIMES) 739 gettimeofday(×[0], NULL); 740 if (futimes(fd, times) != 0) 741 error("futimes('%s')", *path); 742 #endif 743 if (close(fd) != 0) 744 error("close('%s')", *path); 745 } 746 747 /* 748 * Extract a zipfile entry: first perform some sanity checks to ensure 749 * that it is either a directory or a regular file and that the path is 750 * not absolute and does not try to break out of the current directory; 751 * then call either extract_dir() or extract_file() as appropriate. 752 * 753 * This is complicated a bit by the various ways in which we need to 754 * manipulate the path name. Case conversion (if requested by the -L 755 * option) happens first, but the include / exclude patterns are applied 756 * to the full converted path name, before the directory part of the path 757 * is removed in accordance with the -j option. Sanity checks are 758 * intentionally done earlier than they need to be, so the user will get a 759 * warning about insecure paths even for files or directories which 760 * wouldn't be extracted anyway. 761 */ 762 static void 763 extract(struct archive *a, struct archive_entry *e) 764 { 765 char *pathname, *realpathname; 766 mode_t filetype; 767 char *p, *q; 768 769 if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) { 770 warningx("skipping empty or unreadable filename entry"); 771 ac(archive_read_data_skip(a)); 772 return; 773 } 774 filetype = archive_entry_filetype(e); 775 776 /* sanity checks */ 777 if (pathname[0] == '/' || 778 strncmp(pathname, "../", 3) == 0 || 779 strstr(pathname, "/../") != NULL) { 780 warningx("skipping insecure entry '%s'", pathname); 781 ac(archive_read_data_skip(a)); 782 free(pathname); 783 return; 784 } 785 786 /* I don't think this can happen in a zipfile.. */ 787 if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { 788 warningx("skipping non-regular entry '%s'", pathname); 789 ac(archive_read_data_skip(a)); 790 free(pathname); 791 return; 792 } 793 794 /* skip directories in -j case */ 795 if (S_ISDIR(filetype) && j_opt) { 796 ac(archive_read_data_skip(a)); 797 free(pathname); 798 return; 799 } 800 801 /* apply include / exclude patterns */ 802 if (!accept_pathname(pathname)) { 803 ac(archive_read_data_skip(a)); 804 free(pathname); 805 return; 806 } 807 808 /* apply -j and -d */ 809 if (j_opt) { 810 for (p = q = pathname; *p; ++p) 811 if (*p == '/') 812 q = p + 1; 813 realpathname = pathcat(d_arg, q); 814 } else { 815 realpathname = pathcat(d_arg, pathname); 816 } 817 818 /* ensure that parent directory exists */ 819 make_parent(realpathname); 820 821 if (S_ISDIR(filetype)) 822 extract_dir(a, e, realpathname); 823 else 824 extract_file(a, e, &realpathname); 825 826 free(realpathname); 827 free(pathname); 828 } 829 830 static void 831 extract_stdout(struct archive *a, struct archive_entry *e) 832 { 833 char *pathname; 834 mode_t filetype; 835 836 if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) { 837 warningx("skipping empty or unreadable filename entry"); 838 ac(archive_read_data_skip(a)); 839 return; 840 } 841 filetype = archive_entry_filetype(e); 842 843 /* I don't think this can happen in a zipfile.. */ 844 if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) { 845 warningx("skipping non-regular entry '%s'", pathname); 846 ac(archive_read_data_skip(a)); 847 free(pathname); 848 return; 849 } 850 851 /* skip directories in -j case */ 852 if (S_ISDIR(filetype)) { 853 ac(archive_read_data_skip(a)); 854 free(pathname); 855 return; 856 } 857 858 /* apply include / exclude patterns */ 859 if (!accept_pathname(pathname)) { 860 ac(archive_read_data_skip(a)); 861 free(pathname); 862 return; 863 } 864 865 if (c_opt) 866 info("x %s\n", pathname); 867 868 (void)extract2fd(a, pathname, STDOUT_FILENO); 869 870 free(pathname); 871 } 872 873 /* 874 * Print the name of an entry to stdout. 875 */ 876 static void 877 list(struct archive *a, struct archive_entry *e) 878 { 879 char buf[20]; 880 time_t mtime; 881 struct tm *tm; 882 const char *pathname; 883 884 mtime = archive_entry_mtime(e); 885 tm = localtime(&mtime); 886 if (*y_str) 887 strftime(buf, sizeof(buf), "%m-%d-%G %R", tm); 888 else 889 strftime(buf, sizeof(buf), "%m-%d-%g %R", tm); 890 891 pathname = archive_entry_pathname(e); 892 if (!pathname) 893 pathname = ""; 894 if (!zipinfo_mode) { 895 if (v_opt == 1) { 896 printf(" %8ju %s %s\n", 897 (uintmax_t)archive_entry_size(e), 898 buf, pathname); 899 } else if (v_opt == 2) { 900 printf("%8ju Stored %7ju 0%% %s %08x %s\n", 901 (uintmax_t)archive_entry_size(e), 902 (uintmax_t)archive_entry_size(e), 903 buf, 904 0U, 905 pathname); 906 } 907 } else { 908 if (Z1_opt) 909 printf("%s\n", pathname); 910 } 911 ac(archive_read_data_skip(a)); 912 } 913 914 /* 915 * Extract to memory to check CRC 916 */ 917 static int 918 test(struct archive *a, struct archive_entry *e) 919 { 920 ssize_t len; 921 int error_count; 922 923 error_count = 0; 924 if (S_ISDIR(archive_entry_filetype(e))) 925 return 0; 926 927 info(" testing: %s\t", archive_entry_pathname(e)); 928 while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0) 929 /* nothing */; 930 if (len < 0) { 931 info(" %s\n", archive_error_string(a)); 932 ++error_count; 933 } else { 934 info(" OK\n"); 935 } 936 937 /* shouldn't be necessary, but it doesn't hurt */ 938 ac(archive_read_data_skip(a)); 939 940 return error_count; 941 } 942 943 /* 944 * Callback function for reading passphrase. 945 * Originally from cpio.c and passphrase.c, libarchive. 946 */ 947 #define PPBUFF_SIZE 1024 948 static const char * 949 passphrase_callback(struct archive *a, void *_client_data) 950 { 951 char *p; 952 953 (void)a; /* UNUSED */ 954 (void)_client_data; /* UNUSED */ 955 956 if (passphrase_buf == NULL) { 957 passphrase_buf = malloc(PPBUFF_SIZE); 958 if (passphrase_buf == NULL) { 959 errno = ENOMEM; 960 error("malloc()"); 961 } 962 } 963 964 p = lafe_readpassphrase("\nEnter password: ", passphrase_buf, 965 PPBUFF_SIZE); 966 967 if (p == NULL && errno != EINTR) 968 error("Error reading password"); 969 970 return p; 971 } 972 973 /* 974 * Main loop: open the zipfile, iterate over its contents and decide what 975 * to do with each entry. 976 */ 977 static void 978 unzip(const char *fn) 979 { 980 struct archive *a; 981 struct archive_entry *e; 982 int ret; 983 uintmax_t total_size, file_count, error_count; 984 985 if ((a = archive_read_new()) == NULL) 986 error("archive_read_new failed"); 987 988 ac(archive_read_support_format_zip(a)); 989 990 if (O_arg) 991 ac(archive_read_set_format_option(a, "zip", "hdrcharset", O_arg)); 992 993 if (P_arg) 994 archive_read_add_passphrase(a, P_arg); 995 else 996 archive_read_set_passphrase_callback(a, NULL, 997 &passphrase_callback); 998 999 ac(archive_read_open_filename(a, fn, 8192)); 1000 1001 if (!zipinfo_mode) { 1002 if (!p_opt && !q_opt) 1003 printf("Archive: %s\n", fn); 1004 if (v_opt == 1) { 1005 printf(" Length %sDate Time Name\n", y_str); 1006 printf(" -------- %s---- ---- ----\n", y_str); 1007 } else if (v_opt == 2) { 1008 printf(" Length Method Size Ratio %sDate Time CRC-32 Name\n", y_str); 1009 printf("-------- ------ ------- ----- %s---- ---- ------ ----\n", y_str); 1010 } 1011 } 1012 1013 total_size = 0; 1014 file_count = 0; 1015 error_count = 0; 1016 for (;;) { 1017 ret = archive_read_next_header(a, &e); 1018 if (ret == ARCHIVE_EOF) 1019 break; 1020 ac(ret); 1021 if (!zipinfo_mode) { 1022 if (t_opt) 1023 error_count += test(a, e); 1024 else if (v_opt) 1025 list(a, e); 1026 else if (p_opt || c_opt) 1027 extract_stdout(a, e); 1028 else 1029 extract(a, e); 1030 } else { 1031 if (Z1_opt) 1032 list(a, e); 1033 } 1034 1035 total_size += archive_entry_size(e); 1036 ++file_count; 1037 } 1038 1039 if (zipinfo_mode) { 1040 if (v_opt == 1) { 1041 printf(" -------- %s-------\n", y_str); 1042 printf(" %8ju %s%ju file%s\n", 1043 total_size, y_str, file_count, file_count != 1 ? "s" : ""); 1044 } else if (v_opt == 2) { 1045 printf("-------- ------- --- %s-------\n", y_str); 1046 printf("%8ju %7ju 0%% %s%ju file%s\n", 1047 total_size, total_size, y_str, file_count, 1048 file_count != 1 ? "s" : ""); 1049 } 1050 } 1051 1052 ac(archive_read_free(a)); 1053 1054 if (passphrase_buf != NULL) { 1055 memset(passphrase_buf, 0, PPBUFF_SIZE); 1056 free(passphrase_buf); 1057 } 1058 1059 if (t_opt) { 1060 if (error_count > 0) { 1061 errorx("%ju checksum error(s) found.", error_count); 1062 } 1063 else { 1064 printf("No errors detected in compressed data of %s.\n", 1065 fn); 1066 } 1067 } 1068 } 1069 1070 static void 1071 usage(void) 1072 { 1073 1074 fprintf(stderr, 1075 "Usage: unzip [-aCcfjLlnopqtuvyZ1] [{-O|-I} encoding] [-d dir] [-x pattern] [-P password] zipfile\n" 1076 " [member ...]\n"); 1077 exit(EXIT_FAILURE); 1078 } 1079 1080 static void 1081 version(void) 1082 { 1083 printf("bsdunzip %s - %s \n", 1084 BSDUNZIP_VERSION_STRING, 1085 archive_version_details()); 1086 exit(0); 1087 } 1088 1089 static int 1090 getopts(int argc, char *argv[]) 1091 { 1092 struct bsdunzip *bsdunzip, bsdunzip_storage; 1093 int opt; 1094 bsdunzip_optind = 1; 1095 1096 bsdunzip = &bsdunzip_storage; 1097 memset(bsdunzip, 0, sizeof(*bsdunzip)); 1098 1099 bsdunzip->argv = argv; 1100 bsdunzip->argc = argc; 1101 1102 while ((opt = bsdunzip_getopt(bsdunzip)) != -1) { 1103 unzip_exclude_mode = 0; 1104 switch (opt) { 1105 case 'a': 1106 a_opt = 1; 1107 break; 1108 case 'C': 1109 C_opt = 1; 1110 break; 1111 case 'c': 1112 c_opt = 1; 1113 break; 1114 case 'd': 1115 d_arg = bsdunzip->argument; 1116 break; 1117 case 'f': 1118 f_opt = 1; 1119 break; 1120 case 'I': 1121 case 'O': 1122 O_arg = bsdunzip->argument; 1123 break; 1124 case 'j': 1125 j_opt = 1; 1126 break; 1127 case 'L': 1128 L_opt = 1; 1129 break; 1130 case 'l': 1131 if (v_opt == 0) 1132 v_opt = 1; 1133 break; 1134 case 'n': 1135 n_opt = 1; 1136 break; 1137 case 'o': 1138 o_opt = 1; 1139 q_opt = 1; 1140 break; 1141 case 'p': 1142 p_opt = 1; 1143 break; 1144 case 'P': 1145 P_arg = bsdunzip->argument; 1146 break; 1147 case 'q': 1148 q_opt = 1; 1149 break; 1150 case 't': 1151 t_opt = 1; 1152 break; 1153 case 'u': 1154 u_opt = 1; 1155 break; 1156 case 'v': 1157 v_opt = 2; 1158 break; 1159 case 'x': 1160 add_pattern(&exclude, bsdunzip->argument); 1161 unzip_exclude_mode = 1; 1162 break; 1163 case 'y': 1164 y_str = " "; 1165 break; 1166 case 'Z': 1167 zipinfo_mode = 1; 1168 if (bsdunzip->argument != NULL && 1169 strcmp(bsdunzip->argument, "1") == 0) { 1170 Z1_opt = 1; 1171 } 1172 break; 1173 case OPTION_VERSION: 1174 version_opt = 1; 1175 break; 1176 case OPTION_NONE: 1177 break; 1178 default: 1179 usage(); 1180 } 1181 if (opt == OPTION_NONE) 1182 break; 1183 } 1184 return (bsdunzip_optind); 1185 } 1186 1187 int 1188 main(int argc, char *argv[]) 1189 { 1190 const char *zipfile; 1191 int nopts; 1192 1193 #if defined(HAVE_SIGACTION) && defined(SIGCHLD) 1194 { /* Do not ignore SIGCHLD. */ 1195 struct sigaction sa; 1196 sa.sa_handler = SIG_DFL; 1197 sigemptyset(&sa.sa_mask); 1198 sa.sa_flags = 0; 1199 sigaction(SIGCHLD, &sa, NULL); 1200 } 1201 #endif 1202 1203 lafe_setprogname(*argv, "bsdunzip"); 1204 1205 #if HAVE_SETLOCALE 1206 if (setlocale(LC_ALL, "") == NULL) 1207 lafe_warnc(0, "Failed to set default locale"); 1208 #endif 1209 1210 if (isatty(STDOUT_FILENO)) 1211 tty = 1; 1212 1213 if (getenv("UNZIP_DEBUG") != NULL) 1214 unzip_debug = 1; 1215 for (int i = 0; i < argc; ++i) 1216 debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n'); 1217 1218 #ifdef __GLIBC__ 1219 /* Prevent GNU getopt(3) from rearranging options. */ 1220 setenv("POSIXLY_CORRECT", "", 1); 1221 #endif 1222 /* 1223 * Info-ZIP's unzip(1) expects certain options to come before the 1224 * zipfile name, and others to come after - though it does not 1225 * enforce this. For simplicity, we accept *all* options both 1226 * before and after the zipfile name. 1227 */ 1228 nopts = getopts(argc, argv); 1229 1230 if (version_opt == 1) 1231 version(); 1232 1233 /* 1234 * When more of the zipinfo mode options are implemented, this 1235 * will need to change. 1236 */ 1237 if (zipinfo_mode && !Z1_opt) { 1238 printf("Zipinfo mode needs additional options\n"); 1239 exit(EXIT_FAILURE); 1240 } 1241 1242 if (argc <= nopts) 1243 usage(); 1244 zipfile = argv[nopts++]; 1245 1246 if (strcmp(zipfile, "-") == 0) 1247 zipfile = NULL; /* STDIN */ 1248 1249 unzip_exclude_mode = 0; 1250 1251 while (nopts < argc && *argv[nopts] != '-') 1252 add_pattern(&include, argv[nopts++]); 1253 1254 nopts--; /* fake argv[0] */ 1255 nopts += getopts(argc - nopts, argv + nopts); 1256 1257 /* 1258 * For compatibility with Info-ZIP's unzip(1) we need to treat 1259 * non-option arguments following an -x after the zipfile as 1260 * exclude list members. 1261 */ 1262 if (unzip_exclude_mode) { 1263 while (nopts < argc && *argv[nopts] != '-') 1264 add_pattern(&exclude, argv[nopts++]); 1265 nopts--; /* fake argv[0] */ 1266 nopts += getopts(argc - nopts, argv + nopts); 1267 } 1268 1269 /* There may be residual arguments if we encountered -- */ 1270 while (nopts < argc) 1271 add_pattern(&include, argv[nopts++]); 1272 1273 if (n_opt + o_opt + u_opt > 1) 1274 errorx("-n, -o and -u are contradictory"); 1275 1276 unzip(zipfile); 1277 1278 exit(EXIT_SUCCESS); 1279 } 1280