usr.bin/unzip - Sync with NetBSD-8
Needed for new version of libarchive.
Ignore malformed directory entries as created by Dropbox ("/").
This commit is contained in:
parent
6924b5ca1f
commit
9b86debe4a
|
|
@ -1,9 +1,13 @@
|
||||||
# $NetBSD: Makefile,v 1.2 2011/08/18 11:29:27 christos Exp $
|
# $NetBSD: Makefile,v 1.6 2017/05/21 15:28:43 riastradh Exp $
|
||||||
|
|
||||||
|
.include <bsd.own.mk>
|
||||||
PROG= unzip
|
PROG= unzip
|
||||||
|
|
||||||
DPADD+= ${LIBARCHIVE} ${LIBZ} ${LIBBZ2}
|
DPADD+= ${LIBARCHIVE} ${LIBZ} ${LIBBZ2} ${LIBCRYPTO}
|
||||||
LDADD+= -larchive -lz -lbz2
|
LDADD+= -larchive -lz -lbz2 -lcrypto
|
||||||
|
|
||||||
|
#LDADD+= -lcrypto
|
||||||
|
#DPADD+= ${LIBCRYPTO}
|
||||||
|
|
||||||
COPTS.unzip.c += -Wno-format-y2k
|
COPTS.unzip.c += -Wno-format-y2k
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,9 @@
|
||||||
.\" SUCH DAMAGE.
|
.\" SUCH DAMAGE.
|
||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD: revision 180125$
|
.\" $FreeBSD: revision 180125$
|
||||||
.\" $NetBSD: unzip.1,v 1.10 2014/03/18 18:20:45 riastradh Exp $
|
.\" $NetBSD: unzip.1,v 1.11 2015/12/21 17:17:02 christos Exp $
|
||||||
.\"
|
.\"
|
||||||
.Dd August 18, 2011
|
.Dd December 21, 2015
|
||||||
.Dt UNZIP 1
|
.Dt UNZIP 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
|
@ -142,8 +142,8 @@ option should only affect files which are marked as text files in the
|
||||||
zipfile's central directory.
|
zipfile's central directory.
|
||||||
Since the
|
Since the
|
||||||
.Xr archive 3
|
.Xr archive 3
|
||||||
library reads zipfiles sequentially, and does not use the central
|
library does not provide access to that information, it is not available
|
||||||
directory, that information is not available to the
|
to the
|
||||||
.Nm
|
.Nm
|
||||||
utility.
|
utility.
|
||||||
Instead, the
|
Instead, the
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* $NetBSD: unzip.c,v 1.19 2011/09/06 18:43:41 joerg Exp $ */
|
/* $NetBSD: unzip.c,v 1.23 2017/04/20 13:11:35 joerg Exp $ */
|
||||||
|
|
||||||
/*-
|
/*-
|
||||||
* Copyright (c) 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
|
* Copyright (c) 2009, 2010 Joerg Sonnenberger <joerg@NetBSD.org>
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
__RCSID("$NetBSD: unzip.c,v 1.19 2011/09/06 18:43:41 joerg Exp $");
|
__RCSID("$NetBSD: unzip.c,v 1.23 2017/04/20 13:11:35 joerg Exp $");
|
||||||
|
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
@ -131,7 +131,6 @@ errorx(const char *fmt, ...)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* non-fatal error message + errno */
|
/* non-fatal error message + errno */
|
||||||
__printflike(1, 2) static void
|
__printflike(1, 2) static void
|
||||||
warning(const char *fmt, ...)
|
warning(const char *fmt, ...)
|
||||||
|
|
@ -147,7 +146,7 @@ warning(const char *fmt, ...)
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
fprintf(stderr, ": %s\n", strerror(errno));
|
fprintf(stderr, ": %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
/* non-fatal error message, no errno */
|
/* non-fatal error message, no errno */
|
||||||
__printflike(1, 2) static void
|
__printflike(1, 2) static void
|
||||||
warningx(const char *fmt, ...)
|
warningx(const char *fmt, ...)
|
||||||
|
|
@ -486,6 +485,92 @@ check_binary(const unsigned char *buf, size_t len)
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract to a file descriptor
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
extract2fd(struct archive *a, char *pathname, int fd)
|
||||||
|
{
|
||||||
|
int cr, text, warn;
|
||||||
|
ssize_t len;
|
||||||
|
unsigned char *p, *q, *end;
|
||||||
|
|
||||||
|
text = a_opt;
|
||||||
|
warn = 0;
|
||||||
|
cr = 0;
|
||||||
|
|
||||||
|
/* loop over file contents and write to fd */
|
||||||
|
for (int n = 0; ; n++) {
|
||||||
|
if (fd != STDOUT_FILENO)
|
||||||
|
if (tty && (n % 4) == 0)
|
||||||
|
info(" %c\b\b", spinner[(n / 4) % sizeof spinner]);
|
||||||
|
|
||||||
|
len = archive_read_data(a, buffer, sizeof buffer);
|
||||||
|
|
||||||
|
if (len < 0)
|
||||||
|
ac(len);
|
||||||
|
|
||||||
|
/* left over CR from previous buffer */
|
||||||
|
if (a_opt && cr) {
|
||||||
|
if (len == 0 || buffer[0] != '\n')
|
||||||
|
if (write(fd, "\r", 1) != 1)
|
||||||
|
error("write('%s')", pathname);
|
||||||
|
cr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EOF */
|
||||||
|
if (len == 0)
|
||||||
|
break;
|
||||||
|
end = buffer + len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detect whether this is a text file. The correct way to
|
||||||
|
* do this is to check the least significant bit of the
|
||||||
|
* "internal file attributes" field of the corresponding
|
||||||
|
* file header in the central directory, but libarchive
|
||||||
|
* does not provide access to this field, so we have to
|
||||||
|
* guess by looking for non-ASCII characters in the
|
||||||
|
* buffer. Hopefully we won't guess wrong. If we do
|
||||||
|
* guess wrong, we print a warning message later.
|
||||||
|
*/
|
||||||
|
if (a_opt && n == 0) {
|
||||||
|
if (check_binary(buffer, len))
|
||||||
|
text = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* simple case */
|
||||||
|
if (!a_opt || !text) {
|
||||||
|
if (write(fd, buffer, len) != len)
|
||||||
|
error("write('%s')", pathname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hard case: convert \r\n to \n (sigh...) */
|
||||||
|
for (p = buffer; p < end; p = q + 1) {
|
||||||
|
for (q = p; q < end; q++) {
|
||||||
|
if (!warn && BYTE_IS_BINARY(*q)) {
|
||||||
|
warningx("%s may be corrupted due"
|
||||||
|
" to weak text file detection"
|
||||||
|
" heuristic", pathname);
|
||||||
|
warn = 1;
|
||||||
|
}
|
||||||
|
if (q[0] != '\r')
|
||||||
|
continue;
|
||||||
|
if (&q[1] == end) {
|
||||||
|
cr = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (q[1] == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (write(fd, p, q - p) != q - p)
|
||||||
|
error("write('%s')", pathname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract a regular file.
|
* Extract a regular file.
|
||||||
*/
|
*/
|
||||||
|
|
@ -496,9 +581,8 @@ extract_file(struct archive *a, struct archive_entry *e, char **path)
|
||||||
time_t mtime;
|
time_t mtime;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
struct timeval tv[2];
|
struct timeval tv[2];
|
||||||
int cr, fd, text, warn, check;
|
int fd, check, text;
|
||||||
ssize_t len;
|
const char *linkname;
|
||||||
unsigned char *p, *q, *end;
|
|
||||||
|
|
||||||
mode = archive_entry_mode(e) & 0777;
|
mode = archive_entry_mode(e) & 0777;
|
||||||
if (mode == 0)
|
if (mode == 0)
|
||||||
|
|
@ -531,80 +615,40 @@ recheck:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tv[0].tv_sec = now;
|
||||||
|
tv[0].tv_usec = 0;
|
||||||
|
tv[1].tv_sec = mtime;
|
||||||
|
tv[1].tv_usec = 0;
|
||||||
|
|
||||||
|
/* process symlinks */
|
||||||
|
linkname = archive_entry_symlink(e);
|
||||||
|
if (linkname != NULL) {
|
||||||
|
if (symlink(linkname, *path) == -1)
|
||||||
|
error("symlink('%s', '%s')", linkname, *path);
|
||||||
|
info(" extracting: %s -> %s\n", *path, linkname);
|
||||||
|
if (lchmod(*path, mode) == -1)
|
||||||
|
warning("Cannot set mode for '%s'", *path);
|
||||||
|
if (lutimes(*path, tv) == -1)
|
||||||
|
warning("utimes('%s')", *path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* process hardlinks */
|
||||||
|
linkname = archive_entry_hardlink(e);
|
||||||
|
if (linkname != NULL) {
|
||||||
|
if (link(linkname, *path) == -1)
|
||||||
|
error("link('%s', '%s')", linkname, *path);
|
||||||
|
info(" extracting: %s link to %s\n", *path, linkname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
|
if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
|
||||||
error("open('%s')", *path);
|
error("open('%s')", *path);
|
||||||
|
|
||||||
/* loop over file contents and write to disk */
|
|
||||||
info(" extracting: %s", *path);
|
info(" extracting: %s", *path);
|
||||||
text = a_opt;
|
|
||||||
warn = 0;
|
|
||||||
cr = 0;
|
|
||||||
for (int n = 0; ; n++) {
|
|
||||||
if (tty && (n % 4) == 0)
|
|
||||||
info(" %c\b\b", spinner[(n / 4) % sizeof spinner]);
|
|
||||||
|
|
||||||
len = archive_read_data(a, buffer, sizeof buffer);
|
text = extract2fd(a, *path, fd);
|
||||||
|
|
||||||
if (len < 0)
|
|
||||||
ac(len);
|
|
||||||
|
|
||||||
/* left over CR from previous buffer */
|
|
||||||
if (a_opt && cr) {
|
|
||||||
if (len == 0 || buffer[0] != '\n')
|
|
||||||
if (write(fd, "\r", 1) != 1)
|
|
||||||
error("write('%s')", *path);
|
|
||||||
cr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EOF */
|
|
||||||
if (len == 0)
|
|
||||||
break;
|
|
||||||
end = buffer + len;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Detect whether this is a text file. The correct way to
|
|
||||||
* do this is to check the least significant bit of the
|
|
||||||
* "internal file attributes" field of the corresponding
|
|
||||||
* file header in the central directory, but libarchive
|
|
||||||
* does not read the central directory, so we have to
|
|
||||||
* guess by looking for non-ASCII characters in the
|
|
||||||
* buffer. Hopefully we won't guess wrong. If we do
|
|
||||||
* guess wrong, we print a warning message later.
|
|
||||||
*/
|
|
||||||
if (a_opt && n == 0) {
|
|
||||||
if (check_binary(buffer, len))
|
|
||||||
text = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simple case */
|
|
||||||
if (!a_opt || !text) {
|
|
||||||
if (write(fd, buffer, len) != len)
|
|
||||||
error("write('%s')", *path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* hard case: convert \r\n to \n (sigh...) */
|
|
||||||
for (p = buffer; p < end; p = q + 1) {
|
|
||||||
for (q = p; q < end; q++) {
|
|
||||||
if (!warn && BYTE_IS_BINARY(*q)) {
|
|
||||||
warningx("%s may be corrupted due"
|
|
||||||
" to weak text file detection"
|
|
||||||
" heuristic", *path);
|
|
||||||
warn = 1;
|
|
||||||
}
|
|
||||||
if (q[0] != '\r')
|
|
||||||
continue;
|
|
||||||
if (&q[1] == end) {
|
|
||||||
cr = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (q[1] == '\n')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (write(fd, p, q - p) != q - p)
|
|
||||||
error("write('%s')", *path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tty)
|
if (tty)
|
||||||
info(" \b\b");
|
info(" \b\b");
|
||||||
if (text)
|
if (text)
|
||||||
|
|
@ -612,10 +656,6 @@ recheck:
|
||||||
info("\n");
|
info("\n");
|
||||||
|
|
||||||
/* set access and modification time */
|
/* set access and modification time */
|
||||||
tv[0].tv_sec = now;
|
|
||||||
tv[0].tv_usec = 0;
|
|
||||||
tv[1].tv_sec = mtime;
|
|
||||||
tv[1].tv_usec = 0;
|
|
||||||
if (futimes(fd, tv) != 0)
|
if (futimes(fd, tv) != 0)
|
||||||
error("utimes('%s')", *path);
|
error("utimes('%s')", *path);
|
||||||
if (close(fd) != 0)
|
if (close(fd) != 0)
|
||||||
|
|
@ -658,7 +698,7 @@ extract(struct archive *a, struct archive_entry *e)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* I don't think this can happen in a zipfile.. */
|
/* I don't think this can happen in a zipfile.. */
|
||||||
if (!S_ISDIR(filetype) && !S_ISREG(filetype)) {
|
if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {
|
||||||
warningx("skipping non-regular entry '%s'", pathname);
|
warningx("skipping non-regular entry '%s'", pathname);
|
||||||
ac(archive_read_data_skip(a));
|
ac(archive_read_data_skip(a));
|
||||||
free(pathname);
|
free(pathname);
|
||||||
|
|
@ -706,15 +746,12 @@ extract_stdout(struct archive *a, struct archive_entry *e)
|
||||||
{
|
{
|
||||||
char *pathname;
|
char *pathname;
|
||||||
mode_t filetype;
|
mode_t filetype;
|
||||||
int cr, text, warn;
|
|
||||||
ssize_t len;
|
|
||||||
unsigned char *p, *q, *end;
|
|
||||||
|
|
||||||
pathname = pathdup(archive_entry_pathname(e));
|
pathname = pathdup(archive_entry_pathname(e));
|
||||||
filetype = archive_entry_filetype(e);
|
filetype = archive_entry_filetype(e);
|
||||||
|
|
||||||
/* I don't think this can happen in a zipfile.. */
|
/* I don't think this can happen in a zipfile.. */
|
||||||
if (!S_ISDIR(filetype) && !S_ISREG(filetype)) {
|
if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {
|
||||||
warningx("skipping non-regular entry '%s'", pathname);
|
warningx("skipping non-regular entry '%s'", pathname);
|
||||||
ac(archive_read_data_skip(a));
|
ac(archive_read_data_skip(a));
|
||||||
free(pathname);
|
free(pathname);
|
||||||
|
|
@ -738,77 +775,7 @@ extract_stdout(struct archive *a, struct archive_entry *e)
|
||||||
if (c_opt)
|
if (c_opt)
|
||||||
info("x %s\n", pathname);
|
info("x %s\n", pathname);
|
||||||
|
|
||||||
text = a_opt;
|
(void)extract2fd(a, pathname, STDOUT_FILENO);
|
||||||
warn = 0;
|
|
||||||
cr = 0;
|
|
||||||
for (int n = 0; ; n++) {
|
|
||||||
len = archive_read_data(a, buffer, sizeof buffer);
|
|
||||||
|
|
||||||
if (len < 0)
|
|
||||||
ac(len);
|
|
||||||
|
|
||||||
/* left over CR from previous buffer */
|
|
||||||
if (a_opt && cr) {
|
|
||||||
if (len == 0 || buffer[0] != '\n') {
|
|
||||||
if (fwrite("\r", 1, 1, stderr) != 1)
|
|
||||||
error("write('%s')", pathname);
|
|
||||||
}
|
|
||||||
cr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* EOF */
|
|
||||||
if (len == 0)
|
|
||||||
break;
|
|
||||||
end = buffer + len;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Detect whether this is a text file. The correct way to
|
|
||||||
* do this is to check the least significant bit of the
|
|
||||||
* "internal file attributes" field of the corresponding
|
|
||||||
* file header in the central directory, but libarchive
|
|
||||||
* does not read the central directory, so we have to
|
|
||||||
* guess by looking for non-ASCII characters in the
|
|
||||||
* buffer. Hopefully we won't guess wrong. If we do
|
|
||||||
* guess wrong, we print a warning message later.
|
|
||||||
*/
|
|
||||||
if (a_opt && n == 0) {
|
|
||||||
for (p = buffer; p < end; ++p) {
|
|
||||||
if (!isascii((unsigned char)*p)) {
|
|
||||||
text = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simple case */
|
|
||||||
if (!a_opt || !text) {
|
|
||||||
if (fwrite(buffer, 1, len, stdout) != (size_t)len)
|
|
||||||
error("write('%s')", pathname);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* hard case: convert \r\n to \n (sigh...) */
|
|
||||||
for (p = buffer; p < end; p = q + 1) {
|
|
||||||
for (q = p; q < end; q++) {
|
|
||||||
if (!warn && !isascii(*q)) {
|
|
||||||
warningx("%s may be corrupted due"
|
|
||||||
" to weak text file detection"
|
|
||||||
" heuristic", pathname);
|
|
||||||
warn = 1;
|
|
||||||
}
|
|
||||||
if (q[0] != '\r')
|
|
||||||
continue;
|
|
||||||
if (&q[1] == end) {
|
|
||||||
cr = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (q[1] == '\n')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (fwrite(p, 1, q - p, stdout) != (size_t)(q - p))
|
|
||||||
error("write('%s')", pathname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(pathname);
|
free(pathname);
|
||||||
}
|
}
|
||||||
|
|
@ -874,7 +841,6 @@ test(struct archive *a, struct archive_entry *e)
|
||||||
return error_count;
|
return error_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Main loop: open the zipfile, iterate over its contents and decide what
|
* Main loop: open the zipfile, iterate over its contents and decide what
|
||||||
* to do with each entry.
|
* to do with each entry.
|
||||||
|
|
@ -884,15 +850,14 @@ unzip(const char *fn)
|
||||||
{
|
{
|
||||||
struct archive *a;
|
struct archive *a;
|
||||||
struct archive_entry *e;
|
struct archive_entry *e;
|
||||||
int fd, ret;
|
int ret;
|
||||||
uintmax_t total_size, file_count, error_count;
|
uintmax_t total_size, file_count, error_count;
|
||||||
|
|
||||||
if ((fd = open(fn, O_RDONLY)) < 0)
|
if ((a = archive_read_new()) == NULL)
|
||||||
error("%s", fn);
|
error("archive_read_new failed");
|
||||||
|
|
||||||
a = archive_read_new();
|
|
||||||
ac(archive_read_support_format_zip(a));
|
ac(archive_read_support_format_zip(a));
|
||||||
ac(archive_read_open_fd(a, fd, 8192));
|
ac(archive_read_open_filename(a, fn, 8192));
|
||||||
|
|
||||||
if (!q_opt && !p_opt)
|
if (!q_opt && !p_opt)
|
||||||
printf("Archive: %s\n", fn);
|
printf("Archive: %s\n", fn);
|
||||||
|
|
@ -937,11 +902,7 @@ unzip(const char *fn)
|
||||||
file_count != 1 ? "s" : "");
|
file_count != 1 ? "s" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
ac(archive_read_close(a));
|
ac(archive_read_free(a));
|
||||||
(void)archive_read_finish(a);
|
|
||||||
|
|
||||||
if (close(fd) != 0)
|
|
||||||
error("%s", fn);
|
|
||||||
|
|
||||||
if (t_opt) {
|
if (t_opt) {
|
||||||
if (error_count > 0) {
|
if (error_count > 0) {
|
||||||
|
|
@ -1057,6 +1018,9 @@ main(int argc, char *argv[])
|
||||||
usage();
|
usage();
|
||||||
zipfile = argv[nopts++];
|
zipfile = argv[nopts++];
|
||||||
|
|
||||||
|
if (strcmp(zipfile, "-") == 0)
|
||||||
|
zipfile = NULL; /* STDIN */
|
||||||
|
|
||||||
while (nopts < argc && *argv[nopts] != '-')
|
while (nopts < argc && *argv[nopts] != '-')
|
||||||
add_pattern(&include, argv[nopts++]);
|
add_pattern(&include, argv[nopts++]);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user