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