unzip.c revision 175154
1175154Sdes/*- 2175154Sdes * Copyright (c) 2007-2008 Dag-Erling Co�dan Sm�rgrav 3175154Sdes * All rights reserved. 4175154Sdes * 5175154Sdes * Redistribution and use in source and binary forms, with or without 6175154Sdes * modification, are permitted provided that the following conditions 7175154Sdes * are met: 8175154Sdes * 1. Redistributions of source code must retain the above copyright 9175154Sdes * notice, this list of conditions and the following disclaimer 10175154Sdes * in this position and unchanged. 11175154Sdes * 2. Redistributions in binary form must reproduce the above copyright 12175154Sdes * notice, this list of conditions and the following disclaimer in the 13175154Sdes * documentation and/or other materials provided with the distribution. 14175154Sdes * 15175154Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16175154Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17175154Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18175154Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19175154Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20175154Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21175154Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22175154Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23175154Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24175154Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25175154Sdes * SUCH DAMAGE. 26175154Sdes * 27175154Sdes * $FreeBSD: head/usr.bin/unzip/unzip.c 175154 2008-01-08 08:00:06Z des $ 28175154Sdes * 29175154Sdes * This file would be much shorter if we didn't care about command-line 30175154Sdes * compatibility with Info-ZIP's UnZip, which requires us to duplicate 31175154Sdes * parts of libarchive in order to gain more detailed control of its 32175154Sdes * behaviour for the purpose of implementing the -n, -o, -L and -a 33175154Sdes * options. 34175154Sdes */ 35175154Sdes 36175154Sdes#include <sys/queue.h> 37175154Sdes#include <sys/stat.h> 38175154Sdes 39175154Sdes#include <ctype.h> 40175154Sdes#include <errno.h> 41175154Sdes#include <fcntl.h> 42175154Sdes#include <fnmatch.h> 43175154Sdes#include <stdarg.h> 44175154Sdes#include <stdio.h> 45175154Sdes#include <stdlib.h> 46175154Sdes#include <string.h> 47175154Sdes#include <unistd.h> 48175154Sdes 49175154Sdes#include <archive.h> 50175154Sdes#include <archive_entry.h> 51175154Sdes 52175154Sdes/* command-line options */ 53175154Sdesstatic int a_opt; /* convert EOL */ 54175154Sdesstatic const char *d_arg; /* directory */ 55175154Sdesstatic int j_opt; /* junk directories */ 56175154Sdesstatic int L_opt; /* lowercase names */ 57175154Sdesstatic int l_opt; /* list */ 58175154Sdesstatic int n_opt; /* never overwrite */ 59175154Sdesstatic int o_opt; /* always overwrite */ 60175154Sdesstatic int q_opt; /* quiet */ 61175154Sdesstatic int u_opt; /* update */ 62175154Sdes 63175154Sdes/* time when unzip started */ 64175154Sdesstatic time_t now; 65175154Sdes 66175154Sdes/* debug flag */ 67175154Sdesstatic int unzip_debug; 68175154Sdes 69175154Sdes/* running on tty? */ 70175154Sdesstatic int tty; 71175154Sdes 72175154Sdes/* convenience macro */ 73175154Sdes/* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */ 74175154Sdes#define ac(call) \ 75175154Sdes do { \ 76175154Sdes int acret = (call); \ 77175154Sdes if (acret != ARCHIVE_OK) \ 78175154Sdes errorx("%s", archive_error_string(a)); \ 79175154Sdes } while (0) 80175154Sdes 81175154Sdes/* 82175154Sdes * Indicates that last info() did not end with EOL. This helps error() et 83175154Sdes * al. avoid printing an error message on the same line as an incomplete 84175154Sdes * informational message. 85175154Sdes */ 86175154Sdesstatic int noeol; 87175154Sdes 88175154Sdes/* fatal error message + errno */ 89175154Sdesstatic void 90175154Sdeserror(const char *fmt, ...) 91175154Sdes{ 92175154Sdes va_list ap; 93175154Sdes 94175154Sdes if (noeol) 95175154Sdes fprintf(stdout, "\n"); 96175154Sdes fflush(stdout); 97175154Sdes fprintf(stderr, "unzip: "); 98175154Sdes va_start(ap, fmt); 99175154Sdes vfprintf(stderr, fmt, ap); 100175154Sdes va_end(ap); 101175154Sdes fprintf(stderr, ": %s\n", strerror(errno)); 102175154Sdes exit(1); 103175154Sdes} 104175154Sdes 105175154Sdes/* fatal error message, no errno */ 106175154Sdesstatic void 107175154Sdeserrorx(const char *fmt, ...) 108175154Sdes{ 109175154Sdes va_list ap; 110175154Sdes 111175154Sdes if (noeol) 112175154Sdes fprintf(stdout, "\n"); 113175154Sdes fflush(stdout); 114175154Sdes fprintf(stderr, "unzip: "); 115175154Sdes va_start(ap, fmt); 116175154Sdes vfprintf(stderr, fmt, ap); 117175154Sdes va_end(ap); 118175154Sdes fprintf(stderr, "\n"); 119175154Sdes exit(1); 120175154Sdes} 121175154Sdes 122175154Sdes#if 0 123175154Sdes/* non-fatal error message + errno */ 124175154Sdesstatic void 125175154Sdeswarning(const char *fmt, ...) 126175154Sdes{ 127175154Sdes va_list ap; 128175154Sdes 129175154Sdes if (noeol) 130175154Sdes fprintf(stdout, "\n"); 131175154Sdes fflush(stdout); 132175154Sdes fprintf(stderr, "unzip: "); 133175154Sdes va_start(ap, fmt); 134175154Sdes vfprintf(stderr, fmt, ap); 135175154Sdes va_end(ap); 136175154Sdes fprintf(stderr, ": %s\n", strerror(errno)); 137175154Sdes} 138175154Sdes#endif 139175154Sdes 140175154Sdes/* non-fatal error message, no errno */ 141175154Sdesstatic void 142175154Sdeswarningx(const char *fmt, ...) 143175154Sdes{ 144175154Sdes va_list ap; 145175154Sdes 146175154Sdes if (noeol) 147175154Sdes fprintf(stdout, "\n"); 148175154Sdes fflush(stdout); 149175154Sdes fprintf(stderr, "unzip: "); 150175154Sdes va_start(ap, fmt); 151175154Sdes vfprintf(stderr, fmt, ap); 152175154Sdes va_end(ap); 153175154Sdes fprintf(stderr, "\n"); 154175154Sdes} 155175154Sdes 156175154Sdes/* informational message (if not -q) */ 157175154Sdesstatic void 158175154Sdesinfo(const char *fmt, ...) 159175154Sdes{ 160175154Sdes va_list ap; 161175154Sdes int i; 162175154Sdes 163175154Sdes if (q_opt && !unzip_debug) 164175154Sdes return; 165175154Sdes va_start(ap, fmt); 166175154Sdes vfprintf(stdout, fmt, ap); 167175154Sdes va_end(ap); 168175154Sdes fflush(stdout); 169175154Sdes 170175154Sdes for (i = 0; fmt[i] != '\0'; ++i) 171175154Sdes /* nothing */ ; 172175154Sdes noeol = !(i && fmt[i - 1] == '\n'); 173175154Sdes} 174175154Sdes 175175154Sdes/* debug message (if unzip_debug) */ 176175154Sdesstatic void 177175154Sdesdebug(const char *fmt, ...) 178175154Sdes{ 179175154Sdes va_list ap; 180175154Sdes int i; 181175154Sdes 182175154Sdes if (!unzip_debug) 183175154Sdes return; 184175154Sdes va_start(ap, fmt); 185175154Sdes vfprintf(stderr, fmt, ap); 186175154Sdes va_end(ap); 187175154Sdes fflush(stderr); 188175154Sdes 189175154Sdes for (i = 0; fmt[i] != '\0'; ++i) 190175154Sdes /* nothing */ ; 191175154Sdes noeol = !(i && fmt[i - 1] == '\n'); 192175154Sdes} 193175154Sdes 194175154Sdes/* duplicate a path name, possibly converting to lower case */ 195175154Sdesstatic char * 196175154Sdespathdup(const char *path) 197175154Sdes{ 198175154Sdes char *str; 199175154Sdes int len; 200175154Sdes 201175154Sdes len = strlen(path); 202175154Sdes while (len && path[len - 1] == '/') 203175154Sdes len--; 204175154Sdes if ((str = malloc(len + 1)) == NULL) { 205175154Sdes errno = ENOMEM; 206175154Sdes error("malloc()"); 207175154Sdes } 208175154Sdes for (int i = 0; i < len; ++i) 209175154Sdes str[i] = L_opt ? tolower(path[i]) : path[i]; 210175154Sdes str[len] = '\0'; 211175154Sdes 212175154Sdes return (str); 213175154Sdes} 214175154Sdes 215175154Sdes/* concatenate two path names */ 216175154Sdesstatic char * 217175154Sdespathcat(const char *prefix, const char *path) 218175154Sdes{ 219175154Sdes char *str; 220175154Sdes int prelen, len; 221175154Sdes 222175154Sdes prelen = prefix ? strlen(prefix) + 1 : 0; 223175154Sdes len = strlen(path) + 1; 224175154Sdes if ((str = malloc(prelen + len)) == NULL) { 225175154Sdes errno = ENOMEM; 226175154Sdes error("malloc()"); 227175154Sdes } 228175154Sdes if (prefix) { 229175154Sdes memcpy(str, prefix, prelen); /* includes zero */ 230175154Sdes str[prelen - 1] = '/'; /* splat zero */ 231175154Sdes } 232175154Sdes memcpy(str + prelen, path, len); /* includes zero */ 233175154Sdes 234175154Sdes return (str); 235175154Sdes} 236175154Sdes 237175154Sdes/* 238175154Sdes * Pattern lists for include / exclude processing 239175154Sdes */ 240175154Sdesstruct pattern { 241175154Sdes STAILQ_ENTRY(pattern) link; 242175154Sdes char pattern[]; 243175154Sdes}; 244175154Sdes 245175154SdesSTAILQ_HEAD(pattern_list, pattern); 246175154Sdesstatic struct pattern_list include = STAILQ_HEAD_INITIALIZER(include); 247175154Sdesstatic struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude); 248175154Sdes 249175154Sdes/* 250175154Sdes * Add an entry to a pattern list 251175154Sdes */ 252175154Sdesstatic void 253175154Sdesadd_pattern(struct pattern_list *list, const char *pattern) 254175154Sdes{ 255175154Sdes struct pattern *entry; 256175154Sdes int len; 257175154Sdes 258175154Sdes debug("adding pattern '%s'\n", pattern); 259175154Sdes len = strlen(pattern); 260175154Sdes if ((entry = malloc(sizeof *entry + len + 1)) == NULL) { 261175154Sdes errno = ENOMEM; 262175154Sdes error("malloc()"); 263175154Sdes } 264175154Sdes memset(&entry->link, 0, sizeof entry->link); 265175154Sdes memcpy(entry->pattern, pattern, len + 1); 266175154Sdes STAILQ_INSERT_TAIL(list, entry, link); 267175154Sdes} 268175154Sdes 269175154Sdes/* 270175154Sdes * Match a string against a list of patterns 271175154Sdes */ 272175154Sdesstatic int 273175154Sdesmatch_pattern(struct pattern_list *list, const char *str) 274175154Sdes{ 275175154Sdes struct pattern *entry; 276175154Sdes 277175154Sdes STAILQ_FOREACH(entry, list, link) { 278175154Sdes if (fnmatch(entry->pattern, str, 0) == 0) 279175154Sdes return (1); 280175154Sdes } 281175154Sdes return (0); 282175154Sdes} 283175154Sdes 284175154Sdes/* 285175154Sdes * Verify that a given pathname is in the include list and not in the 286175154Sdes * exclude list. 287175154Sdes */ 288175154Sdesstatic int 289175154Sdesaccept_pathname(const char *pathname) 290175154Sdes{ 291175154Sdes 292175154Sdes if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname)) 293175154Sdes return (0); 294175154Sdes if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname)) 295175154Sdes return (0); 296175154Sdes return (1); 297175154Sdes} 298175154Sdes 299175154Sdes/* 300175154Sdes * Create the specified directory with the specified mode, taking certain 301175154Sdes * precautions on they way. 302175154Sdes */ 303175154Sdesstatic void 304175154Sdesmake_dir(const char *path, int mode) 305175154Sdes{ 306175154Sdes struct stat sb; 307175154Sdes 308175154Sdes if (lstat(path, &sb) == 0) { 309175154Sdes if (S_ISDIR(sb.st_mode)) 310175154Sdes return; 311175154Sdes /* 312175154Sdes * Normally, we should either ask the user about removing 313175154Sdes * the non-directory of the same name as a directory we 314175154Sdes * wish to create, or respect the -n or -o command-line 315175154Sdes * options. However, this may lead to a later failure or 316175154Sdes * even compromise (if this non-directory happens to be a 317175154Sdes * symlink to somewhere unsafe), so we don't. 318175154Sdes */ 319175154Sdes 320175154Sdes /* 321175154Sdes * Don't check unlink() result; failure will cause mkdir() 322175154Sdes * to fail later, which we will catch. 323175154Sdes */ 324175154Sdes (void)unlink(path); 325175154Sdes } 326175154Sdes if (mkdir(path, mode) != 0 && errno != EEXIST) 327175154Sdes error("mkdir('%s')", path); 328175154Sdes} 329175154Sdes 330175154Sdes/* 331175154Sdes * Ensure that all directories leading up to (but not including) the 332175154Sdes * specified path exist. 333175154Sdes * 334175154Sdes * XXX inefficient. 335175154Sdes */ 336175154Sdesstatic void 337175154Sdesmake_parent(char *path) 338175154Sdes{ 339175154Sdes struct stat sb; 340175154Sdes char *sep; 341175154Sdes 342175154Sdes sep = strrchr(path, '/'); 343175154Sdes if (sep == NULL || sep == path) 344175154Sdes return; 345175154Sdes *sep = '\0'; 346175154Sdes if (lstat(path, &sb) == 0) { 347175154Sdes if (S_ISDIR(sb.st_mode)) { 348175154Sdes *sep = '/'; 349175154Sdes return; 350175154Sdes } 351175154Sdes unlink(path); 352175154Sdes } 353175154Sdes make_parent(path); 354175154Sdes mkdir(path, 0755); 355175154Sdes *sep = '/'; 356175154Sdes 357175154Sdes#if 0 358175154Sdes for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) { 359175154Sdes /* root in case of absolute d_arg */ 360175154Sdes if (sep == path) 361175154Sdes continue; 362175154Sdes *sep = '\0'; 363175154Sdes make_dir(path, 0755); 364175154Sdes *sep = '/'; 365175154Sdes } 366175154Sdes#endif 367175154Sdes} 368175154Sdes 369175154Sdes/* 370175154Sdes * Extract a directory. 371175154Sdes */ 372175154Sdesstatic void 373175154Sdesextract_dir(struct archive *a, struct archive_entry *e, const char *path) 374175154Sdes{ 375175154Sdes int mode; 376175154Sdes 377175154Sdes mode = archive_entry_filetype(e) & 0777; 378175154Sdes if (mode == 0) 379175154Sdes mode = 0755; 380175154Sdes 381175154Sdes /* 382175154Sdes * Some zipfiles contain directories with weird permissions such 383175154Sdes * as 0644 or 0444. This can cause strange issues such as being 384175154Sdes * unable to extract files into the directory we just created, or 385175154Sdes * the user being unable to remove the directory later without 386175154Sdes * first manually changing its permissions. Therefore, we whack 387175154Sdes * the permissions into shape, assuming that the user wants full 388175154Sdes * access and that anyone who gets read access also gets execute 389175154Sdes * access. 390175154Sdes */ 391175154Sdes mode |= 0700; 392175154Sdes if (mode & 0040) 393175154Sdes mode |= 0010; 394175154Sdes if (mode & 0004) 395175154Sdes mode |= 0001; 396175154Sdes 397175154Sdes info("d %s\n", path); 398175154Sdes make_dir(path, mode); 399175154Sdes ac(archive_read_data_skip(a)); 400175154Sdes} 401175154Sdes 402175154Sdesstatic unsigned char buffer[8192]; 403175154Sdesstatic char spinner[] = { '|', '/', '-', '\\' }; 404175154Sdes 405175154Sdes/* 406175154Sdes * Extract a regular file. 407175154Sdes */ 408175154Sdesstatic void 409175154Sdesextract_file(struct archive *a, struct archive_entry *e, const char *path) 410175154Sdes{ 411175154Sdes int mode; 412175154Sdes time_t mtime; 413175154Sdes struct stat sb; 414175154Sdes struct timeval tv[2]; 415175154Sdes int cr, fd, text, warn; 416175154Sdes ssize_t len; 417175154Sdes unsigned char *p, *q, *end; 418175154Sdes 419175154Sdes mode = archive_entry_filetype(e) & 0777; 420175154Sdes if (mode == 0) 421175154Sdes mode = 0644; 422175154Sdes mtime = archive_entry_mtime(e); 423175154Sdes 424175154Sdes /* look for existing file of same name */ 425175154Sdes if (lstat(path, &sb) == 0) { 426175154Sdes if (u_opt) { 427175154Sdes /* check if up-to-date */ 428175154Sdes if (S_ISREG(sb.st_mode) && sb.st_mtime > mtime) 429175154Sdes return; 430175154Sdes (void)unlink(path); 431175154Sdes } else if (o_opt) { 432175154Sdes /* overwrite */ 433175154Sdes (void)unlink(path); 434175154Sdes } else if (n_opt) { 435175154Sdes /* do not overwrite */ 436175154Sdes return; 437175154Sdes } else { 438175154Sdes /* XXX ask user */ 439175154Sdes errorx("not implemented"); 440175154Sdes } 441175154Sdes } 442175154Sdes 443175154Sdes if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) 444175154Sdes error("open('%s')", path); 445175154Sdes 446175154Sdes /* loop over file contents and write to disk */ 447175154Sdes info("x %s", path); 448175154Sdes text = a_opt; 449175154Sdes warn = 0; 450175154Sdes cr = 0; 451175154Sdes for (int n = 0; ; n++) { 452175154Sdes if (tty && (n % 4) == 0) 453175154Sdes info(" %c\b\b", spinner[(n / 4) % sizeof spinner]); 454175154Sdes 455175154Sdes len = archive_read_data(a, buffer, sizeof buffer); 456175154Sdes 457175154Sdes if (len < 0) 458175154Sdes ac(len); 459175154Sdes 460175154Sdes /* left over CR from previous buffer */ 461175154Sdes if (a_opt && cr) { 462175154Sdes if (len == 0 || buffer[0] != '\n') 463175154Sdes if (write(fd, "\r", 1) != 1) 464175154Sdes error("write('%s')", path); 465175154Sdes cr = 0; 466175154Sdes } 467175154Sdes 468175154Sdes /* EOF */ 469175154Sdes if (len == 0) 470175154Sdes break; 471175154Sdes end = buffer + len; 472175154Sdes 473175154Sdes /* 474175154Sdes * Detect whether this is a text file. The correct way to 475175154Sdes * do this is to check the least significant bit of the 476175154Sdes * "internal file attributes" field of the corresponding 477175154Sdes * file header in the central directory, but libarchive 478175154Sdes * does not read the central directory, so we have to 479175154Sdes * guess by looking for non-ASCII characters in the 480175154Sdes * buffer. Hopefully we won't guess wrong. If we do 481175154Sdes * guess wrong, we print a warning message later. 482175154Sdes */ 483175154Sdes if (a_opt && n == 0) { 484175154Sdes for (p = buffer; p < end; ++p) { 485175154Sdes if (!isascii((unsigned char)*p)) { 486175154Sdes text = 0; 487175154Sdes break; 488175154Sdes } 489175154Sdes } 490175154Sdes } 491175154Sdes 492175154Sdes /* simple case */ 493175154Sdes if (!a_opt || !text) { 494175154Sdes if (write(fd, buffer, len) != len) 495175154Sdes error("write('%s')", path); 496175154Sdes continue; 497175154Sdes } 498175154Sdes 499175154Sdes /* hard case: convert \r\n to \n (sigh...) */ 500175154Sdes for (p = buffer; p < end; p = q + 1) { 501175154Sdes for (q = p; q < end; q++) { 502175154Sdes if (!warn && !isascii(*q)) { 503175154Sdes warningx("%s may be corrupted due" 504175154Sdes " to weak text file detection" 505175154Sdes " heuristic", path); 506175154Sdes warn = 1; 507175154Sdes } 508175154Sdes if (q[0] != '\r') 509175154Sdes continue; 510175154Sdes if (&q[1] == end) { 511175154Sdes cr = 1; 512175154Sdes break; 513175154Sdes } 514175154Sdes if (q[1] == '\n') 515175154Sdes break; 516175154Sdes } 517175154Sdes if (write(fd, p, q - p) != q - p) 518175154Sdes error("write('%s')", path); 519175154Sdes } 520175154Sdes } 521175154Sdes if (tty) 522175154Sdes info(" \b\b"); 523175154Sdes if (text) 524175154Sdes info(" (text)"); 525175154Sdes info("\n"); 526175154Sdes 527175154Sdes /* set access and modification time */ 528175154Sdes tv[0].tv_sec = now; 529175154Sdes tv[0].tv_usec = 0; 530175154Sdes tv[1].tv_sec = mtime; 531175154Sdes tv[1].tv_usec = 0; 532175154Sdes if (futimes(fd, tv) != 0) 533175154Sdes error("utimes('%s')", path); 534175154Sdes if (close(fd) != 0) 535175154Sdes error("close('%s')", path); 536175154Sdes} 537175154Sdes 538175154Sdes/* 539175154Sdes * Extract a zipfile entry: first perform some sanity checks to ensure 540175154Sdes * that it is either a directory or a regular file and that the path is 541175154Sdes * not absolute and does not try to break out of the current directory; 542175154Sdes * then call either extract_dir() or extract_file() as appropriate. 543175154Sdes * 544175154Sdes * This is complicated a bit by the various ways in which we need to 545175154Sdes * manipulate the path name. Case conversion (if requested by the -L 546175154Sdes * option) happens first, but the include / exclude patterns are applied 547175154Sdes * to the full converted path name, before the directory part of the path 548175154Sdes * is removed in accordance with the -j option. Sanity checks are 549175154Sdes * intentionally done earlier than they need to be, so the user will get a 550175154Sdes * warning about insecure paths even for files or directories which 551175154Sdes * wouldn't be extracted anyway. 552175154Sdes */ 553175154Sdesstatic void 554175154Sdesextract(struct archive *a, struct archive_entry *e) 555175154Sdes{ 556175154Sdes char *pathname, *realpathname; 557175154Sdes mode_t filetype; 558175154Sdes char *p, *q; 559175154Sdes 560175154Sdes pathname = pathdup(archive_entry_pathname(e)); 561175154Sdes filetype = archive_entry_filetype(e); 562175154Sdes 563175154Sdes /* sanity checks */ 564175154Sdes if (pathname[0] == '/' || 565175154Sdes strncmp(pathname, "../", 3) == 0 || 566175154Sdes strstr(pathname, "/../") != NULL) { 567175154Sdes warningx("skipping insecure entry '%s'", pathname); 568175154Sdes ac(archive_read_data_skip(a)); 569175154Sdes free(pathname); 570175154Sdes return; 571175154Sdes } 572175154Sdes 573175154Sdes /* I don't think this can happen in a zipfile.. */ 574175154Sdes if (!S_ISDIR(filetype) && !S_ISREG(filetype)) { 575175154Sdes warningx("skipping non-regular entry '%s'", pathname); 576175154Sdes ac(archive_read_data_skip(a)); 577175154Sdes free(pathname); 578175154Sdes return; 579175154Sdes } 580175154Sdes 581175154Sdes /* skip directories in -j case */ 582175154Sdes if (S_ISDIR(filetype) && j_opt) { 583175154Sdes ac(archive_read_data_skip(a)); 584175154Sdes free(pathname); 585175154Sdes return; 586175154Sdes } 587175154Sdes 588175154Sdes /* apply include / exclude patterns */ 589175154Sdes if (!accept_pathname(pathname)) { 590175154Sdes ac(archive_read_data_skip(a)); 591175154Sdes free(pathname); 592175154Sdes return; 593175154Sdes } 594175154Sdes 595175154Sdes /* apply -j and -d */ 596175154Sdes if (j_opt) { 597175154Sdes for (p = q = pathname; *p; ++p) 598175154Sdes if (*p == '/') 599175154Sdes q = p + 1; 600175154Sdes realpathname = pathcat(d_arg, q); 601175154Sdes } else { 602175154Sdes realpathname = pathcat(d_arg, pathname); 603175154Sdes } 604175154Sdes 605175154Sdes /* ensure that parent directory exists */ 606175154Sdes make_parent(realpathname); 607175154Sdes 608175154Sdes if (S_ISDIR(filetype)) 609175154Sdes extract_dir(a, e, realpathname); 610175154Sdes else 611175154Sdes extract_file(a, e, realpathname); 612175154Sdes 613175154Sdes free(realpathname); 614175154Sdes free(pathname); 615175154Sdes} 616175154Sdes 617175154Sdes/* 618175154Sdes * Print the name of an entry to stdout. 619175154Sdes */ 620175154Sdesstatic void 621175154Sdeslist(struct archive *a, struct archive_entry *e) 622175154Sdes{ 623175154Sdes 624175154Sdes printf("%s\n", archive_entry_pathname(e)); 625175154Sdes ac(archive_read_data_skip(a)); 626175154Sdes} 627175154Sdes 628175154Sdes/* 629175154Sdes * Main loop: open the zipfile, iterate over its contents and decide what 630175154Sdes * to do with each entry. 631175154Sdes */ 632175154Sdesstatic void 633175154Sdesunzip(const char *fn) 634175154Sdes{ 635175154Sdes struct archive *a; 636175154Sdes struct archive_entry *e; 637175154Sdes int fd, ret; 638175154Sdes 639175154Sdes if ((fd = open(fn, O_RDONLY)) < 0) 640175154Sdes error("%s", fn); 641175154Sdes 642175154Sdes a = archive_read_new(); 643175154Sdes ac(archive_read_support_format_zip(a)); 644175154Sdes ac(archive_read_open_fd(a, fd, 8192)); 645175154Sdes 646175154Sdes for (;;) { 647175154Sdes ret = archive_read_next_header(a, &e); 648175154Sdes if (ret == ARCHIVE_EOF) 649175154Sdes break; 650175154Sdes ac(ret); 651175154Sdes if (l_opt) 652175154Sdes list(a, e); 653175154Sdes else 654175154Sdes extract(a, e); 655175154Sdes } 656175154Sdes 657175154Sdes ac(archive_read_close(a)); 658175154Sdes (void)archive_read_finish(a); 659175154Sdes if (close(fd) != 0) 660175154Sdes error("%s", fn); 661175154Sdes} 662175154Sdes 663175154Sdesstatic void 664175154Sdesusage(void) 665175154Sdes{ 666175154Sdes 667175154Sdes fprintf(stderr, "usage: unzip [-ajLlnoqu] [-d dir] zipfile\n"); 668175154Sdes exit(1); 669175154Sdes} 670175154Sdes 671175154Sdesstatic int 672175154Sdesgetopts(int argc, char *argv[]) 673175154Sdes{ 674175154Sdes int opt; 675175154Sdes 676175154Sdes optreset = optind = 1; 677175154Sdes while ((opt = getopt(argc, argv, "ad:jLlnoqux:")) != -1) 678175154Sdes switch (opt) { 679175154Sdes case 'a': 680175154Sdes a_opt = 1; 681175154Sdes break; 682175154Sdes case 'd': 683175154Sdes d_arg = optarg; 684175154Sdes break; 685175154Sdes case 'j': 686175154Sdes j_opt = 1; 687175154Sdes break; 688175154Sdes case 'L': 689175154Sdes L_opt = 1; 690175154Sdes break; 691175154Sdes case 'l': 692175154Sdes l_opt = 1; 693175154Sdes break; 694175154Sdes case 'n': 695175154Sdes n_opt = 1; 696175154Sdes break; 697175154Sdes case 'o': 698175154Sdes o_opt = 1; 699175154Sdes break; 700175154Sdes case 'q': 701175154Sdes q_opt = 1; 702175154Sdes break; 703175154Sdes case 'u': 704175154Sdes u_opt = 1; 705175154Sdes break; 706175154Sdes case 'x': 707175154Sdes add_pattern(&exclude, optarg); 708175154Sdes break; 709175154Sdes default: 710175154Sdes usage(); 711175154Sdes } 712175154Sdes 713175154Sdes return (optind); 714175154Sdes} 715175154Sdes 716175154Sdesint 717175154Sdesmain(int argc, char *argv[]) 718175154Sdes{ 719175154Sdes const char *zipfile; 720175154Sdes int nopts; 721175154Sdes 722175154Sdes if (isatty(STDOUT_FILENO)) 723175154Sdes tty = 1; 724175154Sdes 725175154Sdes if (getenv("UNZIP_DEBUG") != NULL) 726175154Sdes unzip_debug = 1; 727175154Sdes for (int i = 0; i < argc; ++i) 728175154Sdes debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n'); 729175154Sdes 730175154Sdes /* 731175154Sdes * Info-ZIP's unzip(1) expects certain options to come before the 732175154Sdes * zipfile name, and others to come after - though it does not 733175154Sdes * enforce this. For simplicity, we accept *all* options both 734175154Sdes * before and after the zipfile name. 735175154Sdes */ 736175154Sdes nopts = getopts(argc, argv); 737175154Sdes 738175154Sdes if (argc <= nopts) 739175154Sdes usage(); 740175154Sdes zipfile = argv[nopts++]; 741175154Sdes 742175154Sdes while (nopts < argc && *argv[nopts] != '-') 743175154Sdes add_pattern(&include, argv[nopts++]); 744175154Sdes 745175154Sdes nopts--; /* fake argv[0] */ 746175154Sdes nopts += getopts(argc - nopts, argv + nopts); 747175154Sdes 748175154Sdes if (n_opt + o_opt + u_opt > 1) 749175154Sdes errorx("-n, -o and -u are contradictory"); 750175154Sdes 751175154Sdes time(&now); 752175154Sdes 753175154Sdes unzip(zipfile); 754175154Sdes 755175154Sdes exit(0); 756175154Sdes} 757