]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/unzip/unzip.c
This commit was generated by cvs2svn to compensate for changes in r179404,
[FreeBSD/FreeBSD.git] / usr.bin / unzip / unzip.c
1 /*-
2  * Copyright (c) 2007-2008 Dag-Erling Coïdan Smørgrav
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  *
29  * This file would be much shorter if we didn't care about command-line
30  * compatibility with Info-ZIP's UnZip, which requires us to duplicate
31  * parts of libarchive in order to gain more detailed control of its
32  * behaviour for the purpose of implementing the -n, -o, -L and -a
33  * options.
34  */
35
36 #include <sys/queue.h>
37 #include <sys/stat.h>
38
39 #include <ctype.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <fnmatch.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #include <archive.h>
50 #include <archive_entry.h>
51
52 /* command-line options */
53 static int               a_opt;         /* convert EOL */
54 static const char       *d_arg;         /* directory */
55 static int               j_opt;         /* junk directories */
56 static int               L_opt;         /* lowercase names */
57 static int               l_opt;         /* list */
58 static int               n_opt;         /* never overwrite */
59 static int               o_opt;         /* always overwrite */
60 static int               q_opt;         /* quiet */
61 static int               u_opt;         /* update */
62
63 /* time when unzip started */
64 static time_t            now;
65
66 /* debug flag */
67 static int               unzip_debug;
68
69 /* running on tty? */
70 static int               tty;
71
72 /* convenience macro */
73 /* XXX should differentiate between ARCHIVE_{WARN,FAIL,RETRY} */
74 #define ac(call)                                                \
75         do {                                                    \
76                 int acret = (call);                             \
77                 if (acret != ARCHIVE_OK)                        \
78                         errorx("%s", archive_error_string(a));  \
79         } while (0)
80
81 /*
82  * Indicates that last info() did not end with EOL.  This helps error() et
83  * al. avoid printing an error message on the same line as an incomplete
84  * informational message.
85  */
86 static int noeol;
87
88 /* fatal error message + errno */
89 static void
90 error(const char *fmt, ...)
91 {
92         va_list ap;
93
94         if (noeol)
95                 fprintf(stdout, "\n");
96         fflush(stdout);
97         fprintf(stderr, "unzip: ");
98         va_start(ap, fmt);
99         vfprintf(stderr, fmt, ap);
100         va_end(ap);
101         fprintf(stderr, ": %s\n", strerror(errno));
102         exit(1);
103 }
104
105 /* fatal error message, no errno */
106 static void
107 errorx(const char *fmt, ...)
108 {
109         va_list ap;
110
111         if (noeol)
112                 fprintf(stdout, "\n");
113         fflush(stdout);
114         fprintf(stderr, "unzip: ");
115         va_start(ap, fmt);
116         vfprintf(stderr, fmt, ap);
117         va_end(ap);
118         fprintf(stderr, "\n");
119         exit(1);
120 }
121
122 #if 0
123 /* non-fatal error message + errno */
124 static void
125 warning(const char *fmt, ...)
126 {
127         va_list ap;
128
129         if (noeol)
130                 fprintf(stdout, "\n");
131         fflush(stdout);
132         fprintf(stderr, "unzip: ");
133         va_start(ap, fmt);
134         vfprintf(stderr, fmt, ap);
135         va_end(ap);
136         fprintf(stderr, ": %s\n", strerror(errno));
137 }
138 #endif
139
140 /* non-fatal error message, no errno */
141 static void
142 warningx(const char *fmt, ...)
143 {
144         va_list ap;
145
146         if (noeol)
147                 fprintf(stdout, "\n");
148         fflush(stdout);
149         fprintf(stderr, "unzip: ");
150         va_start(ap, fmt);
151         vfprintf(stderr, fmt, ap);
152         va_end(ap);
153         fprintf(stderr, "\n");
154 }
155
156 /* informational message (if not -q) */
157 static void
158 info(const char *fmt, ...)
159 {
160         va_list ap;
161         int i;
162
163         if (q_opt && !unzip_debug)
164                 return;
165         va_start(ap, fmt);
166         vfprintf(stdout, fmt, ap);
167         va_end(ap);
168         fflush(stdout);
169
170         for (i = 0; fmt[i] != '\0'; ++i)
171                 /* nothing */ ;
172         noeol = !(i && fmt[i - 1] == '\n');
173 }
174
175 /* debug message (if unzip_debug) */
176 static void
177 debug(const char *fmt, ...)
178 {
179         va_list ap;
180         int i;
181
182         if (!unzip_debug)
183                 return;
184         va_start(ap, fmt);
185         vfprintf(stderr, fmt, ap);
186         va_end(ap);
187         fflush(stderr);
188
189         for (i = 0; fmt[i] != '\0'; ++i)
190                 /* nothing */ ;
191         noeol = !(i && fmt[i - 1] == '\n');
192 }
193
194 /* duplicate a path name, possibly converting to lower case */
195 static char *
196 pathdup(const char *path)
197 {
198         char *str;
199         int len;
200
201         len = strlen(path);
202         while (len && path[len - 1] == '/')
203                 len--;
204         if ((str = malloc(len + 1)) == NULL) {
205                 errno = ENOMEM;
206                 error("malloc()");
207         }
208         for (int i = 0; i < len; ++i)
209                 str[i] = L_opt ? tolower(path[i]) : path[i];
210         str[len] = '\0';
211
212         return (str);
213 }
214
215 /* concatenate two path names */
216 static char *
217 pathcat(const char *prefix, const char *path)
218 {
219         char *str;
220         int prelen, len;
221
222         prelen = prefix ? strlen(prefix) + 1 : 0;
223         len = strlen(path) + 1;
224         if ((str = malloc(prelen + len)) == NULL) {
225                 errno = ENOMEM;
226                 error("malloc()");
227         }
228         if (prefix) {
229                 memcpy(str, prefix, prelen);    /* includes zero */
230                 str[prelen - 1] = '/';          /* splat zero */
231         }
232         memcpy(str + prelen, path, len);        /* includes zero */
233
234         return (str);
235 }
236
237 /*
238  * Pattern lists for include / exclude processing
239  */
240 struct pattern {
241         STAILQ_ENTRY(pattern) link;
242         char pattern[];
243 };
244
245 STAILQ_HEAD(pattern_list, pattern);
246 static struct pattern_list include = STAILQ_HEAD_INITIALIZER(include);
247 static struct pattern_list exclude = STAILQ_HEAD_INITIALIZER(exclude);
248
249 /*
250  * Add an entry to a pattern list
251  */
252 static void
253 add_pattern(struct pattern_list *list, const char *pattern)
254 {
255         struct pattern *entry;
256         int len;
257
258         debug("adding pattern '%s'\n", pattern);
259         len = strlen(pattern);
260         if ((entry = malloc(sizeof *entry + len + 1)) == NULL) {
261                 errno = ENOMEM;
262                 error("malloc()");
263         }
264         memset(&entry->link, 0, sizeof entry->link);
265         memcpy(entry->pattern, pattern, len + 1);
266         STAILQ_INSERT_TAIL(list, entry, link);
267 }
268
269 /*
270  * Match a string against a list of patterns
271  */
272 static int
273 match_pattern(struct pattern_list *list, const char *str)
274 {
275         struct pattern *entry;
276
277         STAILQ_FOREACH(entry, list, link) {
278                 if (fnmatch(entry->pattern, str, 0) == 0)
279                         return (1);
280         }
281         return (0);
282 }
283
284 /*
285  * Verify that a given pathname is in the include list and not in the
286  * exclude list.
287  */
288 static int
289 accept_pathname(const char *pathname)
290 {
291
292         if (!STAILQ_EMPTY(&include) && !match_pattern(&include, pathname))
293                 return (0);
294         if (!STAILQ_EMPTY(&exclude) && match_pattern(&exclude, pathname))
295                 return (0);
296         return (1);
297 }
298
299 /*
300  * Create the specified directory with the specified mode, taking certain
301  * precautions on they way.
302  */
303 static void
304 make_dir(const char *path, int mode)
305 {
306         struct stat sb;
307
308         if (lstat(path, &sb) == 0) {
309                 if (S_ISDIR(sb.st_mode))
310                         return;
311                 /*
312                  * Normally, we should either ask the user about removing
313                  * the non-directory of the same name as a directory we
314                  * wish to create, or respect the -n or -o command-line
315                  * options.  However, this may lead to a later failure or
316                  * even compromise (if this non-directory happens to be a
317                  * symlink to somewhere unsafe), so we don't.
318                  */
319
320                 /*
321                  * Don't check unlink() result; failure will cause mkdir()
322                  * to fail later, which we will catch.
323                  */
324                 (void)unlink(path);
325         }
326         if (mkdir(path, mode) != 0 && errno != EEXIST)
327                 error("mkdir('%s')", path);
328 }
329
330 /*
331  * Ensure that all directories leading up to (but not including) the
332  * specified path exist.
333  *
334  * XXX inefficient.
335  */
336 static void
337 make_parent(char *path)
338 {
339         struct stat sb;
340         char *sep;
341
342         sep = strrchr(path, '/');
343         if (sep == NULL || sep == path)
344                 return;
345         *sep = '\0';
346         if (lstat(path, &sb) == 0) {
347                 if (S_ISDIR(sb.st_mode)) {
348                         *sep = '/';
349                         return;
350                 }
351                 unlink(path);
352         }
353         make_parent(path);
354         mkdir(path, 0755);
355         *sep = '/';
356
357 #if 0
358         for (sep = path; (sep = strchr(sep, '/')) != NULL; sep++) {
359                 /* root in case of absolute d_arg */
360                 if (sep == path)
361                         continue;
362                 *sep = '\0';
363                 make_dir(path, 0755);
364                 *sep = '/';
365         }
366 #endif
367 }
368
369 /*
370  * Extract a directory.
371  */
372 static void
373 extract_dir(struct archive *a, struct archive_entry *e, const char *path)
374 {
375         int mode;
376
377         mode = archive_entry_filetype(e) & 0777;
378         if (mode == 0)
379                 mode = 0755;
380
381         /*
382          * Some zipfiles contain directories with weird permissions such
383          * as 0644 or 0444.  This can cause strange issues such as being
384          * unable to extract files into the directory we just created, or
385          * the user being unable to remove the directory later without
386          * first manually changing its permissions.  Therefore, we whack
387          * the permissions into shape, assuming that the user wants full
388          * access and that anyone who gets read access also gets execute
389          * access.
390          */
391         mode |= 0700;
392         if (mode & 0040)
393                 mode |= 0010;
394         if (mode & 0004)
395                 mode |= 0001;
396
397         info("d %s\n", path);
398         make_dir(path, mode);
399         ac(archive_read_data_skip(a));
400 }
401
402 static unsigned char buffer[8192];
403 static char spinner[] = { '|', '/', '-', '\\' };
404
405 /*
406  * Extract a regular file.
407  */
408 static void
409 extract_file(struct archive *a, struct archive_entry *e, const char *path)
410 {
411         int mode;
412         time_t mtime;
413         struct stat sb;
414         struct timeval tv[2];
415         int cr, fd, text, warn;
416         ssize_t len;
417         unsigned char *p, *q, *end;
418
419         mode = archive_entry_filetype(e) & 0777;
420         if (mode == 0)
421                 mode = 0644;
422         mtime = archive_entry_mtime(e);
423
424         /* look for existing file of same name */
425         if (lstat(path, &sb) == 0) {
426                 if (u_opt) {
427                         /* check if up-to-date */
428                         if (S_ISREG(sb.st_mode) && sb.st_mtime > mtime)
429                                 return;
430                         (void)unlink(path);
431                 } else if (o_opt) {
432                         /* overwrite */
433                         (void)unlink(path);
434                 } else if (n_opt) {
435                         /* do not overwrite */
436                         return;
437                 } else {
438                         /* XXX ask user */
439                         errorx("not implemented");
440                 }
441         }
442
443         if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
444                 error("open('%s')", path);
445
446         /* loop over file contents and write to disk */
447         info("x %s", path);
448         text = a_opt;
449         warn = 0;
450         cr = 0;
451         for (int n = 0; ; n++) {
452                 if (tty && (n % 4) == 0)
453                         info(" %c\b\b", spinner[(n / 4) % sizeof spinner]);
454
455                 len = archive_read_data(a, buffer, sizeof buffer);
456
457                 if (len < 0)
458                         ac(len);
459
460                 /* left over CR from previous buffer */
461                 if (a_opt && cr) {
462                         if (len == 0 || buffer[0] != '\n')
463                                 if (write(fd, "\r", 1) != 1)
464                                         error("write('%s')", path);
465                         cr = 0;
466                 }
467
468                 /* EOF */
469                 if (len == 0)
470                         break;
471                 end = buffer + len;
472
473                 /*
474                  * Detect whether this is a text file.  The correct way to
475                  * do this is to check the least significant bit of the
476                  * "internal file attributes" field of the corresponding
477                  * file header in the central directory, but libarchive
478                  * does not read the central directory, so we have to
479                  * guess by looking for non-ASCII characters in the
480                  * buffer.  Hopefully we won't guess wrong.  If we do
481                  * guess wrong, we print a warning message later.
482                  */
483                 if (a_opt && n == 0) {
484                         for (p = buffer; p < end; ++p) {
485                                 if (!isascii((unsigned char)*p)) {
486                                         text = 0;
487                                         break;
488                                 }
489                         }
490                 }
491
492                 /* simple case */
493                 if (!a_opt || !text) {
494                         if (write(fd, buffer, len) != len)
495                                 error("write('%s')", path);
496                         continue;
497                 }
498
499                 /* hard case: convert \r\n to \n (sigh...) */
500                 for (p = buffer; p < end; p = q + 1) {
501                         for (q = p; q < end; q++) {
502                                 if (!warn && !isascii(*q)) {
503                                         warningx("%s may be corrupted due"
504                                             " to weak text file detection"
505                                             " heuristic", path);
506                                         warn = 1;
507                                 }
508                                 if (q[0] != '\r')
509                                         continue;
510                                 if (&q[1] == end) {
511                                         cr = 1;
512                                         break;
513                                 }
514                                 if (q[1] == '\n')
515                                         break;
516                         }
517                         if (write(fd, p, q - p) != q - p)
518                                 error("write('%s')", path);
519                 }
520         }
521         if (tty)
522                 info("  \b\b");
523         if (text)
524                 info(" (text)");
525         info("\n");
526
527         /* set access and modification time */
528         tv[0].tv_sec = now;
529         tv[0].tv_usec = 0;
530         tv[1].tv_sec = mtime;
531         tv[1].tv_usec = 0;
532         if (futimes(fd, tv) != 0)
533                 error("utimes('%s')", path);
534         if (close(fd) != 0)
535                 error("close('%s')", path);
536 }
537
538 /*
539  * Extract a zipfile entry: first perform some sanity checks to ensure
540  * that it is either a directory or a regular file and that the path is
541  * not absolute and does not try to break out of the current directory;
542  * then call either extract_dir() or extract_file() as appropriate.
543  *
544  * This is complicated a bit by the various ways in which we need to
545  * manipulate the path name.  Case conversion (if requested by the -L
546  * option) happens first, but the include / exclude patterns are applied
547  * to the full converted path name, before the directory part of the path
548  * is removed in accordance with the -j option.  Sanity checks are
549  * intentionally done earlier than they need to be, so the user will get a
550  * warning about insecure paths even for files or directories which
551  * wouldn't be extracted anyway.
552  */
553 static void
554 extract(struct archive *a, struct archive_entry *e)
555 {
556         char *pathname, *realpathname;
557         mode_t filetype;
558         char *p, *q;
559
560         pathname = pathdup(archive_entry_pathname(e));
561         filetype = archive_entry_filetype(e);
562
563         /* sanity checks */
564         if (pathname[0] == '/' ||
565             strncmp(pathname, "../", 3) == 0 ||
566             strstr(pathname, "/../") != NULL) {
567                 warningx("skipping insecure entry '%s'", pathname);
568                 ac(archive_read_data_skip(a));
569                 free(pathname);
570                 return;
571         }
572
573         /* I don't think this can happen in a zipfile.. */
574         if (!S_ISDIR(filetype) && !S_ISREG(filetype)) {
575                 warningx("skipping non-regular entry '%s'", pathname);
576                 ac(archive_read_data_skip(a));
577                 free(pathname);
578                 return;
579         }
580
581         /* skip directories in -j case */
582         if (S_ISDIR(filetype) && j_opt) {
583                 ac(archive_read_data_skip(a));
584                 free(pathname);
585                 return;
586         }
587
588         /* apply include / exclude patterns */
589         if (!accept_pathname(pathname)) {
590                 ac(archive_read_data_skip(a));
591                 free(pathname);
592                 return;
593         }
594
595         /* apply -j and -d */
596         if (j_opt) {
597                 for (p = q = pathname; *p; ++p)
598                         if (*p == '/')
599                                 q = p + 1;
600                 realpathname = pathcat(d_arg, q);
601         } else {
602                 realpathname = pathcat(d_arg, pathname);
603         }
604
605         /* ensure that parent directory exists */
606         make_parent(realpathname);
607
608         if (S_ISDIR(filetype))
609                 extract_dir(a, e, realpathname);
610         else
611                 extract_file(a, e, realpathname);
612
613         free(realpathname);
614         free(pathname);
615 }
616
617 /*
618  * Print the name of an entry to stdout.
619  */
620 static void
621 list(struct archive *a, struct archive_entry *e)
622 {
623
624         printf("%s\n", archive_entry_pathname(e));
625         ac(archive_read_data_skip(a));
626 }
627
628 /*
629  * Main loop: open the zipfile, iterate over its contents and decide what
630  * to do with each entry.
631  */
632 static void
633 unzip(const char *fn)
634 {
635         struct archive *a;
636         struct archive_entry *e;
637         int fd, ret;
638
639         if ((fd = open(fn, O_RDONLY)) < 0)
640                 error("%s", fn);
641
642         a = archive_read_new();
643         ac(archive_read_support_format_zip(a));
644         ac(archive_read_open_fd(a, fd, 8192));
645
646         for (;;) {
647                 ret = archive_read_next_header(a, &e);
648                 if (ret == ARCHIVE_EOF)
649                         break;
650                 ac(ret);
651                 if (l_opt)
652                         list(a, e);
653                 else
654                         extract(a, e);
655         }
656
657         ac(archive_read_close(a));
658         (void)archive_read_finish(a);
659         if (close(fd) != 0)
660                 error("%s", fn);
661 }
662
663 static void
664 usage(void)
665 {
666
667         fprintf(stderr, "usage: unzip [-ajLlnoqu] [-d dir] zipfile\n");
668         exit(1);
669 }
670
671 static int
672 getopts(int argc, char *argv[])
673 {
674         int opt;
675
676         optreset = optind = 1;
677         while ((opt = getopt(argc, argv, "ad:jLlnoqux:")) != -1)
678                 switch (opt) {
679                 case 'a':
680                         a_opt = 1;
681                         break;
682                 case 'd':
683                         d_arg = optarg;
684                         break;
685                 case 'j':
686                         j_opt = 1;
687                         break;
688                 case 'L':
689                         L_opt = 1;
690                         break;
691                 case 'l':
692                         l_opt = 1;
693                         break;
694                 case 'n':
695                         n_opt = 1;
696                         break;
697                 case 'o':
698                         o_opt = 1;
699                         break;
700                 case 'q':
701                         q_opt = 1;
702                         break;
703                 case 'u':
704                         u_opt = 1;
705                         break;
706                 case 'x':
707                         add_pattern(&exclude, optarg);
708                         break;
709                 default:
710                         usage();
711                 }
712
713         return (optind);
714 }
715
716 int
717 main(int argc, char *argv[])
718 {
719         const char *zipfile;
720         int nopts;
721
722         if (isatty(STDOUT_FILENO))
723                 tty = 1;
724
725         if (getenv("UNZIP_DEBUG") != NULL)
726                 unzip_debug = 1;
727         for (int i = 0; i < argc; ++i)
728                 debug("%s%c", argv[i], (i < argc - 1) ? ' ' : '\n');
729
730         /*
731          * Info-ZIP's unzip(1) expects certain options to come before the
732          * zipfile name, and others to come after - though it does not
733          * enforce this.  For simplicity, we accept *all* options both
734          * before and after the zipfile name.
735          */
736         nopts = getopts(argc, argv);
737
738         if (argc <= nopts)
739                 usage();
740         zipfile = argv[nopts++];
741
742         while (nopts < argc && *argv[nopts] != '-')
743                 add_pattern(&include, argv[nopts++]);
744
745         nopts--; /* fake argv[0] */
746         nopts += getopts(argc - nopts, argv + nopts);
747
748         if (n_opt + o_opt + u_opt > 1)
749                 errorx("-n, -o and -u are contradictory");
750
751         time(&now);
752
753         unzip(zipfile);
754
755         exit(0);
756 }