Path: blob/main/contrib/libarchive/unzip/bsdunzip.c
107715 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2009, 2010 Joerg Sonnenberger <[email protected]>4* Copyright (c) 2007-2008 Dag-Erling Smørgrav5* All rights reserved.6*/78#include "bsdunzip_platform.h"910#include "la_queue.h"11#include "la_getline.h"12#ifdef HAVE_SYS_STAT_H13#include <sys/stat.h>14#endif1516#ifdef HAVE_CTYPE_H17#include <ctype.h>18#endif19#ifdef HAVE_ERRNO_H20#include <errno.h>21#endif22#ifdef HAVE_FCNTL_H23#include <fcntl.h>24#endif25#ifdef HAVE_FNMATCH_H26#include <fnmatch.h>27#endif28#ifdef HAVE_LOCALE_H29#include <locale.h>30#endif31#ifdef HAVE_SIGNAL_H32#include <signal.h>33#endif34#ifdef HAVE_STDARG_H35#include <stdarg.h>36#endif37#include <stdio.h>38#ifdef HAVE_STDLIB_H39#include <stdlib.h>40#endif41#ifdef HAVE_STRING_H42#include <string.h>43#endif44#ifdef HAVE_UNISTD_H45#include <unistd.h>46#endif47#if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \48(!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES)))49#ifdef HAVE_SYS_TIME_H50#include <sys/time.h>51#endif52#endif53#ifdef HAVE_GETOPT_OPTRESET54#include <getopt.h>55#endif5657#include "bsdunzip.h"58#include "passphrase.h"59#include "lafe_err.h"6061/* command-line options */62static int a_opt; /* convert EOL */63static int C_opt; /* match case-insensitively */64static int c_opt; /* extract to stdout */65static const char *d_arg; /* directory */66static int f_opt; /* update existing files only */67static const char *O_arg; /* encoding */68static int j_opt; /* junk directories */69static int L_opt; /* lowercase names */70static int n_opt; /* never overwrite */71static int o_opt; /* always overwrite */72static int p_opt; /* extract to stdout, quiet */73static const char *P_arg; /* passphrase */74static int q_opt; /* quiet */75static int t_opt; /* test */76static int u_opt; /* update */77static int v_opt; /* verbose/list */78static const char *y_str = ""; /* 4 digit year */79static int Z1_opt; /* zipinfo mode list files only */80static int version_opt; /* version string */8182/* debug flag */83static int unzip_debug;8485/* zipinfo mode */86static int zipinfo_mode;8788/* running on tty? */89static int tty;9091/* processing exclude list */92static int unzip_exclude_mode = 0;9394int bsdunzip_optind;9596/* convenience macro */97/* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */98#define ac(call) \99do { \100int acret = (call); \101if (acret != ARCHIVE_OK) \102errorx("%s", archive_error_string(a)); \103} while (0)104105/*106* Indicates that last info() did not end with EOL. This helps error() et107* al. avoid printing an error message on the same line as an incomplete108* informational message.109*/110static int noeol;111112/* for an interactive passphrase input */113static char *passphrase_buf;114115/* fatal error message + errno */116static void __LA_NORETURN117error(const char *fmt, ...)118{119va_list ap;120121if (noeol)122fprintf(stdout, "\n");123fflush(stdout);124fprintf(stderr, "unzip: ");125va_start(ap, fmt);126vfprintf(stderr, fmt, ap);127va_end(ap);128fprintf(stderr, ": %s\n", strerror(errno));129exit(EXIT_FAILURE);130}131132/* fatal error message, no errno */133static void __LA_NORETURN134errorx(const char *fmt, ...)135{136va_list ap;137138if (noeol)139fprintf(stdout, "\n");140fflush(stdout);141fprintf(stderr, "unzip: ");142va_start(ap, fmt);143vfprintf(stderr, fmt, ap);144va_end(ap);145fprintf(stderr, "\n");146exit(EXIT_FAILURE);147}148149/* non-fatal error message + errno */150static void151warning(const char *fmt, ...)152{153va_list ap;154155if (noeol)156fprintf(stdout, "\n");157fflush(stdout);158fprintf(stderr, "unzip: ");159va_start(ap, fmt);160vfprintf(stderr, fmt, ap);161va_end(ap);162fprintf(stderr, ": %s\n", strerror(errno));163}164165/* non-fatal error message, no errno */166static void167warningx(const char *fmt, ...)168{169va_list ap;170171if (noeol)172fprintf(stdout, "\n");173fflush(stdout);174fprintf(stderr, "unzip: ");175va_start(ap, fmt);176vfprintf(stderr, fmt, ap);177va_end(ap);178fprintf(stderr, "\n");179}180181/* informational message (if not -q) */182static void183info(const char *fmt, ...)184{185va_list ap;186187if (q_opt && !unzip_debug)188return;189va_start(ap, fmt);190vfprintf(stdout, fmt, ap);191va_end(ap);192fflush(stdout);193194if (*fmt == '\0')195noeol = 1;196else197noeol = fmt[strlen(fmt) - 1] != '\n';198}199200/* debug message (if unzip_debug) */201static void202debug(const char *fmt, ...)203{204va_list ap;205206if (!unzip_debug)207return;208va_start(ap, fmt);209vfprintf(stderr, fmt, ap);210va_end(ap);211fflush(stderr);212213if (*fmt == '\0')214noeol = 1;215else216noeol = fmt[strlen(fmt) - 1] != '\n';217}218219/* duplicate a path name, possibly converting to lower case */220static char *221pathdup(const char *path)222{223char *str;224size_t i, len;225226if (path == NULL || path[0] == '\0')227return (NULL);228229len = strlen(path);230while (len && path[len - 1] == '/')231len--;232if ((str = malloc(len + 1)) == NULL) {233errno = ENOMEM;234error("malloc()");235}236if (L_opt) {237for (i = 0; i < len; ++i)238str[i] = (char)tolower((unsigned char)path[i]);239} else {240memcpy(str, path, len);241}242str[len] = '\0';243244return (str);245}246247/* concatenate two path names */248static char *249pathcat(const char *prefix, const char *path)250{251char *str;252size_t prelen, len;253254prelen = prefix ? strlen(prefix) + 1 : 0;255len = strlen(path) + 1;256if ((str = malloc(prelen + len)) == NULL) {257errno = ENOMEM;258error("malloc()");259}260if (prefix) {261memcpy(str, prefix, prelen); /* includes zero */262str[prelen - 1] = '/'; /* splat zero */263}264memcpy(str + prelen, path, len); /* includes zero */265266return (str);267}268269/*270* Pattern lists for include / exclude processing271*/272struct pattern {273STAILQ_ENTRY(pattern) link;274char pattern[];275};276277STAILQ_HEAD(pattern_list, pattern);278static struct pattern_list include = STAILQ_HEAD_INITIALIZER(include);279static struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude);280281/*282* Add an entry to a pattern list283*/284static void285add_pattern(struct pattern_list *list, const char *pattern)286{287struct pattern *entry;288size_t len;289290debug("adding pattern '%s'\n", pattern);291len = strlen(pattern);292if ((entry = malloc(sizeof *entry + len + 1)) == NULL) {293errno = ENOMEM;294error("malloc()");295}296memcpy(entry->pattern, pattern, len + 1);297STAILQ_INSERT_TAIL(list, entry, link);298}299300/*301* Match a string against a list of patterns302*/303static int304match_pattern(struct pattern_list *list, const char *str)305{306struct pattern *entry;307308STAILQ_FOREACH(entry, list, link) {309#ifdef HAVE_FNMATCH310if (fnmatch(entry->pattern, str, C_opt ? FNM_CASEFOLD : 0) == 0)311return (1);312#else313#error "Unsupported platform: fnmatch() is required"314#endif315}316return (0);317}318319/*320* Verify that a given pathname is in the include list and not in the321* exclude list.322*/323static int324accept_pathname(const char *pathname)325{326327if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname))328return (0);329if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname))330return (0);331return (1);332}333334/*335* Create the specified directory with the specified mode, taking certain336* precautions on they way.337*/338static void339make_dir(const char *path, int mode)340{341struct stat sb;342343if (lstat(path, &sb) == 0) {344if (S_ISDIR(sb.st_mode))345return;346/*347* Normally, we should either ask the user about removing348* the non-directory of the same name as a directory we349* wish to create, or respect the -n or -o command-line350* options. However, this may lead to a later failure or351* even compromise (if this non-directory happens to be a352* symlink to somewhere unsafe), so we don't.353*/354355/*356* Don't check unlink() result; failure will cause mkdir()357* to fail later, which we will catch.358*/359(void)unlink(path);360}361if (mkdir(path, (mode_t)mode) != 0 && errno != EEXIST)362error("mkdir('%s')", path);363}364365/*366* Ensure that all directories leading up to (but not including) the367* specified path exist.368*369* XXX inefficient + modifies the file in-place370*/371static void372make_parent(char *path)373{374struct stat sb;375char *sep;376377sep = strrchr(path, '/');378if (sep == NULL || sep == path)379return;380*sep = '\0';381if (lstat(path, &sb) == 0) {382if (S_ISDIR(sb.st_mode)) {383*sep = '/';384return;385}386unlink(path);387}388make_parent(path);389mkdir(path, 0755);390*sep = '/';391392#if 0393for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) {394/* root in case of absolute d_arg */395if (sep == path)396continue;397*sep = '\0';398make_dir(path, 0755);399*sep = '/';400}401#endif402}403404/*405* Extract a directory.406*/407static void408extract_dir(struct archive *a, struct archive_entry *e, const char *path)409{410int mode;411412/*413* Dropbox likes to create '/' directory entries, just ignore414* such junk.415*/416if (*path == '\0')417return;418419mode = archive_entry_mode(e) & 0777;420if (mode == 0)421mode = 0755;422423/*424* Some zipfiles contain directories with weird permissions such425* as 0644 or 0444. This can cause strange issues such as being426* unable to extract files into the directory we just created, or427* the user being unable to remove the directory later without428* first manually changing its permissions. Therefore, we whack429* the permissions into shape, assuming that the user wants full430* access and that anyone who gets read access also gets execute431* access.432*/433mode |= 0700;434if (mode & 0040)435mode |= 0010;436if (mode & 0004)437mode |= 0001;438439info(" creating: %s/\n", path);440make_dir(path, mode);441ac(archive_read_data_skip(a));442}443444static unsigned char buffer[8192];445static char spinner[] = { '|', '/', '-', '\\' };446447static int448handle_existing_file(char **path)449{450size_t alen;451ssize_t len;452char buf[4];453454for (;;) {455fprintf(stderr,456"replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ",457*path);458if (fgets(buf, sizeof(buf), stdin) == NULL)459goto stdin_err;460switch (*buf) {461case 'A':462o_opt = 1;463/* FALLTHROUGH */464case 'y':465case 'Y':466(void)unlink(*path);467return 1;468case 'N':469n_opt = 1;470/* FALLTHROUGH */471case 'n':472return -1;473case 'r':474case 'R':475printf("New name: ");476fflush(stdout);477free(*path);478*path = NULL;479alen = 0;480len = getline(path, &alen, stdin);481if (len < 1)482goto stdin_err;483if ((*path)[len - 1] == '\n')484(*path)[len - 1] = '\0';485return 0;486default:487break;488}489}490stdin_err:491clearerr(stdin);492printf("NULL\n(EOF or read error, "493"treating as \"[N]one\"...)\n");494n_opt = 1;495return -1;496}497498/*499* Detect binary files by a combination of character white list and500* black list. NUL bytes and other control codes without use in text files501* result directly in switching the file to binary mode. Otherwise, at least502* one white-listed byte has to be found.503*504* Black-listed: 0..6, 14..25, 28..31505* 0xf3ffc07f = 11110011111111111100000001111111b506* White-listed: 9..10, 13, >= 32507* 0x00002600 = 00000000000000000010011000000000b508*509* See the proginfo/txtvsbin.txt in the zip sources for a detailed discussion.510*/511#define BYTE_IS_BINARY(x) ((x) < 32 && (0xf3ffc07fU & (1U << (x))))512#define BYTE_IS_TEXT(x) ((x) >= 32 || (0x00002600U & (1U << (x))))513514static int515check_binary(const unsigned char *buf, size_t len)516{517int rv;518for (rv = 1; len--; ++buf) {519if (BYTE_IS_BINARY(*buf))520return 1;521if (BYTE_IS_TEXT(*buf))522rv = 0;523}524525return rv;526}527528/*529* Extract to a file descriptor530*/531static int532extract2fd(struct archive *a, char *pathname, int fd)533{534int cr, text, warn;535ssize_t len;536unsigned char *p, *q, *end;537538text = a_opt;539warn = 0;540cr = 0;541542/* loop over file contents and write to fd */543for (int n = 0; ; n++) {544if (fd != STDOUT_FILENO)545if (tty && (n % 4) == 0)546info(" %c\b\b", spinner[(n / 4) % sizeof spinner]);547548len = archive_read_data(a, buffer, sizeof buffer);549550if (len < 0)551ac(len);552553/* left over CR from previous buffer */554if (a_opt && cr) {555if (len == 0 || buffer[0] != '\n')556if (write(fd, "\r", 1) != 1)557error("write('%s')", pathname);558cr = 0;559}560561/* EOF */562if (len == 0)563break;564end = buffer + len;565566/*567* Detect whether this is a text file. The correct way to568* do this is to check the least significant bit of the569* "internal file attributes" field of the corresponding570* file header in the central directory, but libarchive571* does not provide access to this field, so we have to572* guess by looking for non-ASCII characters in the573* buffer. Hopefully we won't guess wrong. If we do574* guess wrong, we print a warning message later.575*/576if (a_opt && n == 0) {577if (check_binary(buffer, len))578text = 0;579}580581/* simple case */582if (!a_opt || !text) {583if (write(fd, buffer, len) != len)584error("write('%s')", pathname);585continue;586}587588/* hard case: convert \r\n to \n (sigh...) */589for (p = buffer; p < end; p = q + 1) {590for (q = p; q < end; q++) {591if (!warn && BYTE_IS_BINARY(*q)) {592warningx("%s may be corrupted due"593" to weak text file detection"594" heuristic", pathname);595warn = 1;596}597if (q[0] != '\r')598continue;599if (&q[1] == end) {600cr = 1;601break;602}603if (q[1] == '\n')604break;605}606if (write(fd, p, q - p) != q - p)607error("write('%s')", pathname);608}609}610611return text;612}613614/*615* Extract a regular file.616*/617static void618extract_file(struct archive *a, struct archive_entry *e, char **path)619{620int mode;621struct timespec mtime;622struct stat sb;623int fd, check, text;624const char *linkname;625#if defined(HAVE_UTIMENSAT) || defined(HAVE_FUTIMENS)626struct timespec ts[2];627#endif628#if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \629(!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES)))630struct timeval times[2];631#endif632633mode = archive_entry_mode(e) & 0777;634if (mode == 0)635mode = 0644;636mtime.tv_sec = archive_entry_mtime(e);637mtime.tv_nsec = archive_entry_mtime_nsec(e);638639/* look for existing file of same name */640recheck:641if (lstat(*path, &sb) == 0) {642if (u_opt || f_opt) {643/* check if up-to-date */644if (S_ISREG(sb.st_mode) && (645#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC646sb.st_mtimespec.tv_sec > mtime.tv_sec ||647(sb.st_mtimespec.tv_sec == mtime.tv_sec &&648sb.st_mtimespec.tv_nsec >= mtime.tv_nsec)649#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC650sb.st_mtim.tv_sec > mtime.tv_sec ||651(sb.st_mtim.tv_sec == mtime.tv_sec &&652sb.st_mtim.tv_nsec >= mtime.tv_nsec)653#elif HAVE_STRUCT_STAT_ST_MTIME_N654sb.st_mtime > mtime.tv_sec ||655(sb.st_mtime == mtime.tv_sec &&656sb.st_mtime_n >= mtime.tv_nsec)657#elif HAVE_STRUCT_STAT_ST_MTIME_USEC658sb.st_mtime > mtime.tv_sec ||659(sb.st_mtime == mtime.tv_sec &&660sb.st_mtime_usec >= mtime.tv_nsec / 1000)661#else662sb.st_mtime > mtime.tv_sec663#endif664))665return;666(void)unlink(*path);667} else if (o_opt) {668/* overwrite */669(void)unlink(*path);670} else if (n_opt) {671/* do not overwrite */672return;673} else {674check = handle_existing_file(path);675if (check == 0)676goto recheck;677if (check == -1)678return; /* do not overwrite */679}680} else {681if (f_opt)682return;683}684685#if defined(HAVE_UTIMENSAT) || defined(HAVE_FUTIMENS)686ts[0].tv_sec = 0;687ts[0].tv_nsec = UTIME_NOW;688ts[1] = mtime;689#endif690#if ((!defined(HAVE_UTIMENSAT) && defined(HAVE_LUTIMES)) || \691(!defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES)))692times[0].tv_sec = 0;693times[0].tv_usec = -1;694times[1].tv_sec = mtime.tv_sec;695times[1].tv_usec = mtime.tv_nsec / 1000;696#endif697698/* process symlinks */699linkname = archive_entry_symlink(e);700if (linkname != NULL) {701if (symlink(linkname, *path) != 0)702error("symlink('%s')", *path);703info(" extracting: %s -> %s\n", *path, linkname);704#ifdef HAVE_LCHMOD705if (lchmod(*path, (mode_t)mode) != 0)706warning("Cannot set mode for '%s'", *path);707#endif708/* set access and modification time */709#if defined(HAVE_UTIMENSAT)710if (utimensat(AT_FDCWD, *path, ts, AT_SYMLINK_NOFOLLOW) != 0)711warning("utimensat('%s')", *path);712#elif defined(HAVE_LUTIMES)713gettimeofday(×[0], NULL);714if (lutimes(*path, times) != 0)715warning("lutimes('%s')", *path);716#endif717return;718}719720if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)721error("open('%s')", *path);722723info(" extracting: %s", *path);724725text = extract2fd(a, *path, fd);726727if (tty)728info(" \b\b");729if (text)730info(" (text)");731info("\n");732733/* set access and modification time */734#if defined(HAVE_FUTIMENS)735if (futimens(fd, ts) != 0)736error("futimens('%s')", *path);737#elif defined(HAVE_FUTIMES)738gettimeofday(×[0], NULL);739if (futimes(fd, times) != 0)740error("futimes('%s')", *path);741#endif742if (close(fd) != 0)743error("close('%s')", *path);744}745746/*747* Extract a zipfile entry: first perform some sanity checks to ensure748* that it is either a directory or a regular file and that the path is749* not absolute and does not try to break out of the current directory;750* then call either extract_dir() or extract_file() as appropriate.751*752* This is complicated a bit by the various ways in which we need to753* manipulate the path name. Case conversion (if requested by the -L754* option) happens first, but the include / exclude patterns are applied755* to the full converted path name, before the directory part of the path756* is removed in accordance with the -j option. Sanity checks are757* intentionally done earlier than they need to be, so the user will get a758* warning about insecure paths even for files or directories which759* wouldn't be extracted anyway.760*/761static void762extract(struct archive *a, struct archive_entry *e)763{764char *pathname, *realpathname;765mode_t filetype;766char *p, *q;767768if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) {769warningx("skipping empty or unreadable filename entry");770ac(archive_read_data_skip(a));771return;772}773filetype = archive_entry_filetype(e);774775/* sanity checks */776if (pathname[0] == '/' ||777strncmp(pathname, "../", 3) == 0 ||778strstr(pathname, "/../") != NULL) {779warningx("skipping insecure entry '%s'", pathname);780ac(archive_read_data_skip(a));781free(pathname);782return;783}784785/* I don't think this can happen in a zipfile.. */786if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {787warningx("skipping non-regular entry '%s'", pathname);788ac(archive_read_data_skip(a));789free(pathname);790return;791}792793/* skip directories in -j case */794if (S_ISDIR(filetype) && j_opt) {795ac(archive_read_data_skip(a));796free(pathname);797return;798}799800/* apply include / exclude patterns */801if (!accept_pathname(pathname)) {802ac(archive_read_data_skip(a));803free(pathname);804return;805}806807/* apply -j and -d */808if (j_opt) {809for (p = q = pathname; *p; ++p)810if (*p == '/')811q = p + 1;812realpathname = pathcat(d_arg, q);813} else {814realpathname = pathcat(d_arg, pathname);815}816817/* ensure that parent directory exists */818make_parent(realpathname);819820if (S_ISDIR(filetype))821extract_dir(a, e, realpathname);822else823extract_file(a, e, &realpathname);824825free(realpathname);826free(pathname);827}828829static void830extract_stdout(struct archive *a, struct archive_entry *e)831{832char *pathname;833mode_t filetype;834835if ((pathname = pathdup(archive_entry_pathname(e))) == NULL) {836warningx("skipping empty or unreadable filename entry");837ac(archive_read_data_skip(a));838return;839}840filetype = archive_entry_filetype(e);841842/* I don't think this can happen in a zipfile.. */843if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {844warningx("skipping non-regular entry '%s'", pathname);845ac(archive_read_data_skip(a));846free(pathname);847return;848}849850/* skip directories in -j case */851if (S_ISDIR(filetype)) {852ac(archive_read_data_skip(a));853free(pathname);854return;855}856857/* apply include / exclude patterns */858if (!accept_pathname(pathname)) {859ac(archive_read_data_skip(a));860free(pathname);861return;862}863864if (c_opt)865info("x %s\n", pathname);866867(void)extract2fd(a, pathname, STDOUT_FILENO);868869free(pathname);870}871872/*873* Print the name of an entry to stdout.874*/875static void876list(struct archive *a, struct archive_entry *e)877{878char buf[20];879time_t mtime;880struct tm *tm;881const char *pathname;882883mtime = archive_entry_mtime(e);884tm = localtime(&mtime);885if (*y_str)886strftime(buf, sizeof(buf), "%m-%d-%G %R", tm);887else888strftime(buf, sizeof(buf), "%m-%d-%g %R", tm);889890pathname = archive_entry_pathname(e);891if (!pathname)892pathname = "";893if (!zipinfo_mode) {894if (v_opt == 1) {895printf(" %8ju %s %s\n",896(uintmax_t)archive_entry_size(e),897buf, pathname);898} else if (v_opt == 2) {899printf("%8ju Stored %7ju 0%% %s %08x %s\n",900(uintmax_t)archive_entry_size(e),901(uintmax_t)archive_entry_size(e),902buf,9030U,904pathname);905}906} else {907if (Z1_opt)908printf("%s\n", pathname);909}910ac(archive_read_data_skip(a));911}912913/*914* Extract to memory to check CRC915*/916static int917test(struct archive *a, struct archive_entry *e)918{919ssize_t len;920int error_count;921922error_count = 0;923if (S_ISDIR(archive_entry_filetype(e)))924return 0;925926info(" testing: %s\t", archive_entry_pathname(e));927while ((len = archive_read_data(a, buffer, sizeof buffer)) > 0)928/* nothing */;929if (len < 0) {930info(" %s\n", archive_error_string(a));931++error_count;932} else {933info(" OK\n");934}935936/* shouldn't be necessary, but it doesn't hurt */937ac(archive_read_data_skip(a));938939return error_count;940}941942/*943* Callback function for reading passphrase.944* Originally from cpio.c and passphrase.c, libarchive.945*/946#define PPBUFF_SIZE 1024947static const char *948passphrase_callback(struct archive *a, void *_client_data)949{950char *p;951952(void)a; /* UNUSED */953(void)_client_data; /* UNUSED */954955if (passphrase_buf == NULL) {956passphrase_buf = malloc(PPBUFF_SIZE);957if (passphrase_buf == NULL) {958errno = ENOMEM;959error("malloc()");960}961}962963p = lafe_readpassphrase("\nEnter password: ", passphrase_buf,964PPBUFF_SIZE);965966if (p == NULL && errno != EINTR)967error("Error reading password");968969return p;970}971972/*973* Main loop: open the zipfile, iterate over its contents and decide what974* to do with each entry.975*/976static void977unzip(const char *fn)978{979struct archive *a;980struct archive_entry *e;981int ret;982uintmax_t total_size, file_count, error_count;983984if ((a = archive_read_new()) == NULL)985error("archive_read_new failed");986987ac(archive_read_support_format_zip(a));988989if (O_arg)990ac(archive_read_set_format_option(a, "zip", "hdrcharset", O_arg));991992if (P_arg)993archive_read_add_passphrase(a, P_arg);994else995archive_read_set_passphrase_callback(a, NULL,996&passphrase_callback);997998ac(archive_read_open_filename(a, fn, 8192));9991000if (!zipinfo_mode) {1001if (!p_opt && !q_opt)1002printf("Archive: %s\n", fn);1003if (v_opt == 1) {1004printf(" Length %sDate Time Name\n", y_str);1005printf(" -------- %s---- ---- ----\n", y_str);1006} else if (v_opt == 2) {1007printf(" Length Method Size Ratio %sDate Time CRC-32 Name\n", y_str);1008printf("-------- ------ ------- ----- %s---- ---- ------ ----\n", y_str);1009}1010}10111012total_size = 0;1013file_count = 0;1014error_count = 0;1015for (;;) {1016ret = archive_read_next_header(a, &e);1017if (ret == ARCHIVE_EOF)1018break;1019ac(ret);1020if (!zipinfo_mode) {1021if (t_opt)1022error_count += test(a, e);1023else if (v_opt)1024list(a, e);1025else if (p_opt || c_opt)1026extract_stdout(a, e);1027else1028extract(a, e);1029} else {1030if (Z1_opt)1031list(a, e);1032}10331034total_size += archive_entry_size(e);1035++file_count;1036}10371038if (zipinfo_mode) {1039if (v_opt == 1) {1040printf(" -------- %s-------\n", y_str);1041printf(" %8ju %s%ju file%s\n",1042total_size, y_str, file_count, file_count != 1 ? "s" : "");1043} else if (v_opt == 2) {1044printf("-------- ------- --- %s-------\n", y_str);1045printf("%8ju %7ju 0%% %s%ju file%s\n",1046total_size, total_size, y_str, file_count,1047file_count != 1 ? "s" : "");1048}1049}10501051ac(archive_read_free(a));10521053if (passphrase_buf != NULL) {1054memset(passphrase_buf, 0, PPBUFF_SIZE);1055free(passphrase_buf);1056}10571058if (t_opt) {1059if (error_count > 0) {1060errorx("%ju checksum error(s) found.", error_count);1061}1062else {1063printf("No errors detected in compressed data of %s.\n",1064fn);1065}1066}1067}10681069static void1070usage(void)1071{10721073fprintf(stderr,1074"Usage: unzip [-aCcfjLlnopqtuvyZ1] [{-O|-I} encoding] [-d dir] [-x pattern] [-P password] zipfile\n"1075" [member ...]\n");1076exit(EXIT_FAILURE);1077}10781079static void1080version(void)1081{1082printf("bsdunzip %s - %s \n",1083BSDUNZIP_VERSION_STRING,1084archive_version_details());1085exit(0);1086}10871088static int1089getopts(int argc, char *argv[])1090{1091struct bsdunzip *bsdunzip, bsdunzip_storage;1092int opt;1093bsdunzip_optind = 1;10941095bsdunzip = &bsdunzip_storage;1096memset(bsdunzip, 0, sizeof(*bsdunzip));10971098bsdunzip->argv = argv;1099bsdunzip->argc = argc;11001101while ((opt = bsdunzip_getopt(bsdunzip)) != -1) {1102unzip_exclude_mode = 0;1103switch (opt) {1104case 'a':1105a_opt = 1;1106break;1107case 'C':1108C_opt = 1;1109break;1110case 'c':1111c_opt = 1;1112break;1113case 'd':1114d_arg = bsdunzip->argument;1115break;1116case 'f':1117f_opt = 1;1118break;1119case 'I':1120case 'O':1121O_arg = bsdunzip->argument;1122break;1123case 'j':1124j_opt = 1;1125break;1126case 'L':1127L_opt = 1;1128break;1129case 'l':1130if (v_opt == 0)1131v_opt = 1;1132break;1133case 'n':1134n_opt = 1;1135break;1136case 'o':1137o_opt = 1;1138q_opt = 1;1139break;1140case 'p':1141p_opt = 1;1142break;1143case 'P':1144P_arg = bsdunzip->argument;1145break;1146case 'q':1147q_opt = 1;1148break;1149case 't':1150t_opt = 1;1151break;1152case 'u':1153u_opt = 1;1154break;1155case 'v':1156v_opt = 2;1157break;1158case 'x':1159add_pattern(&exclude, bsdunzip->argument);1160unzip_exclude_mode = 1;1161break;1162case 'y':1163y_str = " ";1164break;1165case 'Z':1166zipinfo_mode = 1;1167if (bsdunzip->argument != NULL &&1168strcmp(bsdunzip->argument, "1") == 0) {1169Z1_opt = 1;1170}1171break;1172case OPTION_VERSION:1173version_opt = 1;1174break;1175case OPTION_NONE:1176break;1177default:1178usage();1179}1180if (opt == OPTION_NONE)1181break;1182}1183return (bsdunzip_optind);1184}11851186int1187main(int argc, char *argv[])1188{1189const char *zipfile;1190int nopts;11911192#if defined(HAVE_SIGACTION) && defined(SIGCHLD)1193{ /* Do not ignore SIGCHLD. */1194struct sigaction sa;1195sa.sa_handler = SIG_DFL;1196sigemptyset(&sa.sa_mask);1197sa.sa_flags = 0;1198sigaction(SIGCHLD, &sa, NULL);1199}1200#endif12011202lafe_setprogname(*argv, "bsdunzip");12031204#if HAVE_SETLOCALE1205if (setlocale(LC_ALL, "") == NULL)1206lafe_warnc(0, "Failed to set default locale");1207#endif12081209if (isatty(STDOUT_FILENO))1210tty = 1;12111212if (getenv("UNZIP_DEBUG") != NULL)1213unzip_debug = 1;1214for (int i = 0; i < argc; ++i)1215debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n');12161217#ifdef __GLIBC__1218/* Prevent GNU getopt(3) from rearranging options. */1219setenv("POSIXLY_CORRECT", "", 1);1220#endif1221/*1222* Info-ZIP's unzip(1) expects certain options to come before the1223* zipfile name, and others to come after - though it does not1224* enforce this. For simplicity, we accept *all* options both1225* before and after the zipfile name.1226*/1227nopts = getopts(argc, argv);12281229if (version_opt == 1)1230version();12311232/*1233* When more of the zipinfo mode options are implemented, this1234* will need to change.1235*/1236if (zipinfo_mode && !Z1_opt) {1237printf("Zipinfo mode needs additional options\n");1238exit(EXIT_FAILURE);1239}12401241if (argc <= nopts)1242usage();1243zipfile = argv[nopts++];12441245if (strcmp(zipfile, "-") == 0)1246zipfile = NULL; /* STDIN */12471248unzip_exclude_mode = 0;12491250while (nopts < argc && *argv[nopts] != '-')1251add_pattern(&include, argv[nopts++]);12521253nopts--; /* fake argv[0] */1254nopts += getopts(argc - nopts, argv + nopts);12551256/*1257* For compatibility with Info-ZIP's unzip(1) we need to treat1258* non-option arguments following an -x after the zipfile as1259* exclude list members.1260*/1261if (unzip_exclude_mode) {1262while (nopts < argc && *argv[nopts] != '-')1263add_pattern(&exclude, argv[nopts++]);1264nopts--; /* fake argv[0] */1265nopts += getopts(argc - nopts, argv + nopts);1266}12671268/* There may be residual arguments if we encountered -- */1269while (nopts < argc)1270add_pattern(&include, argv[nopts++]);12711272if (n_opt + o_opt + u_opt > 1)1273errorx("-n, -o and -u are contradictory");12741275unzip(zipfile);12761277exit(EXIT_SUCCESS);1278}127912801281