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:
Sevan Janiyan 2018-11-06 01:19:40 +00:00
parent 6924b5ca1f
commit 9b86debe4a
3 changed files with 142 additions and 174 deletions

View File

@ -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
DPADD+= ${LIBARCHIVE} ${LIBZ} ${LIBBZ2}
LDADD+= -larchive -lz -lbz2
DPADD+= ${LIBARCHIVE} ${LIBZ} ${LIBBZ2} ${LIBCRYPTO}
LDADD+= -larchive -lz -lbz2 -lcrypto
#LDADD+= -lcrypto
#DPADD+= ${LIBCRYPTO}
COPTS.unzip.c += -Wno-format-y2k

View File

@ -25,9 +25,9 @@
.\" SUCH DAMAGE.
.\"
.\" $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
.Os
.Sh NAME
@ -142,8 +142,8 @@ option should only affect files which are marked as text files in the
zipfile's central directory.
Since the
.Xr archive 3
library reads zipfiles sequentially, and does not use the central
directory, that information is not available to the
library does not provide access to that information, it is not available
to the
.Nm
utility.
Instead, the

View File

@ -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>
@ -37,7 +37,7 @@
*/
#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/stat.h>
@ -131,7 +131,6 @@ errorx(const char *fmt, ...)
exit(1);
}
#if 0
/* non-fatal error message + errno */
__printflike(1, 2) static void
warning(const char *fmt, ...)
@ -147,7 +146,7 @@ warning(const char *fmt, ...)
va_end(ap);
fprintf(stderr, ": %s\n", strerror(errno));
}
#endif
/* non-fatal error message, no errno */
__printflike(1, 2) static void
warningx(const char *fmt, ...)
@ -486,6 +485,92 @@ check_binary(const unsigned char *buf, size_t len)
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.
*/
@ -496,9 +581,8 @@ extract_file(struct archive *a, struct archive_entry *e, char **path)
time_t mtime;
struct stat sb;
struct timeval tv[2];
int cr, fd, text, warn, check;
ssize_t len;
unsigned char *p, *q, *end;
int fd, check, text;
const char *linkname;
mode = archive_entry_mode(e) & 0777;
if (mode == 0)
@ -531,80 +615,40 @@ recheck:
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)
error("open('%s')", *path);
/* loop over file contents and write to disk */
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)
info(" \b\b");
if (text)
@ -612,10 +656,6 @@ recheck:
info("\n");
/* 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)
error("utimes('%s')", *path);
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.. */
if (!S_ISDIR(filetype) && !S_ISREG(filetype)) {
if (!S_ISDIR(filetype) && !S_ISREG(filetype) && !S_ISLNK(filetype)) {
warningx("skipping non-regular entry '%s'", pathname);
ac(archive_read_data_skip(a));
free(pathname);
@ -706,15 +746,12 @@ extract_stdout(struct archive *a, struct archive_entry *e)
{
char *pathname;
mode_t filetype;
int cr, text, warn;
ssize_t len;
unsigned char *p, *q, *end;
pathname = pathdup(archive_entry_pathname(e));
filetype = archive_entry_filetype(e);
/* 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);
ac(archive_read_data_skip(a));
free(pathname);
@ -738,77 +775,7 @@ extract_stdout(struct archive *a, struct archive_entry *e)
if (c_opt)
info("x %s\n", pathname);
text = a_opt;
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);
}
}
(void)extract2fd(a, pathname, STDOUT_FILENO);
free(pathname);
}
@ -874,7 +841,6 @@ test(struct archive *a, struct archive_entry *e)
return error_count;
}
/*
* Main loop: open the zipfile, iterate over its contents and decide what
* to do with each entry.
@ -884,15 +850,14 @@ unzip(const char *fn)
{
struct archive *a;
struct archive_entry *e;
int fd, ret;
int ret;
uintmax_t total_size, file_count, error_count;
if ((fd = open(fn, O_RDONLY)) < 0)
error("%s", fn);
if ((a = archive_read_new()) == NULL)
error("archive_read_new failed");
a = archive_read_new();
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)
printf("Archive: %s\n", fn);
@ -937,11 +902,7 @@ unzip(const char *fn)
file_count != 1 ? "s" : "");
}
ac(archive_read_close(a));
(void)archive_read_finish(a);
if (close(fd) != 0)
error("%s", fn);
ac(archive_read_free(a));
if (t_opt) {
if (error_count > 0) {
@ -1057,6 +1018,9 @@ main(int argc, char *argv[])
usage();
zipfile = argv[nopts++];
if (strcmp(zipfile, "-") == 0)
zipfile = NULL; /* STDIN */
while (nopts < argc && *argv[nopts] != '-')
add_pattern(&include, argv[nopts++]);