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