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