Annotation of src/usr.bin/compress/main.c, Revision 1.98
1.98 ! mortimer 1: /* $OpenBSD: main.c,v 1.97 2020/10/12 13:56:22 naddy Exp $ */
1.1 mickey 2:
1.78 deraadt 3: /*
4: * Copyright (c) 1992, 1993
5: * The Regents of the University of California. All rights reserved.
6: * Copyright (c) 1997-2002 Michael Shalayeff
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. Neither the name of the University nor the names of its contributors
17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23: * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
24: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26: * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29: * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30: * THE POSSIBILITY OF SUCH DAMAGE.
31: */
1.1 mickey 32:
33: #include <sys/time.h>
34: #include <sys/stat.h>
35:
1.18 mickey 36: #include <getopt.h>
1.1 mickey 37: #include <err.h>
38: #include <errno.h>
1.19 millert 39: #include <fts.h>
1.37 millert 40: #include <libgen.h>
1.1 mickey 41: #include <stdio.h>
42: #include <stdlib.h>
1.81 millert 43: #include <stdbool.h>
1.1 mickey 44: #include <string.h>
45: #include <unistd.h>
1.83 deraadt 46: #include <limits.h>
1.1 mickey 47: #include <fcntl.h>
48: #include <paths.h>
49: #include "compress.h"
50:
51: #define min(a,b) ((a) < (b)? (a) : (b))
1.98 ! mortimer 52:
! 53: enum program_mode pmode;
1.1 mickey 54:
1.70 millert 55: int cat, decomp, pipin, force, verbose, testmode, list, recurse, storename;
1.1 mickey 56: extern char *__progname;
57:
1.18 mickey 58: const struct compressor {
1.81 millert 59: const char *name;
60: const char *suffix;
61: const u_char *magic;
62: const char *comp_opts;
63: const char *decomp_opts;
64: const char *cat_opts;
1.92 tedu 65: void *(*ropen)(int, char *, int);
1.93 tedu 66: int (*read)(void *, char *, int);
67: #ifndef SMALL
1.92 tedu 68: void *(*wopen)(int, char *, int, u_int32_t);
1.17 millert 69: int (*write)(void *, const char *, int);
1.93 tedu 70: #endif
1.63 otto 71: int (*close)(void *, struct z_info *, const char *, struct stat *);
1.1 mickey 72: } c_table[] = {
1.45 tedu 73: #define M_DEFLATE (&c_table[0])
1.81 millert 74: {
75: "deflate",
76: ".gz",
77: "\037\213",
78: "123456789ab:cdfhLlNnOo:qrS:tVv",
79: "cfhLlNno:qrtVv",
80: "fhqr",
1.92 tedu 81: gz_ropen,
1.93 tedu 82: gz_read,
83: #ifndef SMALL
1.92 tedu 84: gz_wopen,
1.81 millert 85: gz_write,
1.93 tedu 86: #endif
1.81 millert 87: gz_close
88: },
1.45 tedu 89: #define M_COMPRESS (&c_table[1])
90: #ifndef SMALL
1.81 millert 91: {
92: "compress",
93: ".Z",
94: "\037\235",
95: "123456789ab:cdfghlNnOo:qrS:tv",
96: "cfhlNno:qrtv",
97: "fghqr",
1.92 tedu 98: z_ropen,
1.93 tedu 99: zread,
1.92 tedu 100: z_wopen,
1.81 millert 101: zwrite,
102: z_close
103: },
1.45 tedu 104: #endif /* SMALL */
1.1 mickey 105: { NULL }
106: };
107:
1.45 tedu 108: #ifndef SMALL
1.81 millert 109: const struct compressor null_method = {
110: "null",
111: ".nul",
112: "XX",
113: "123456789ab:cdfghlNnOo:qrS:tv",
114: "cfhlNno:qrtv",
115: "fghqr",
1.92 tedu 116: null_ropen,
1.93 tedu 117: null_read,
1.92 tedu 118: null_wopen,
1.81 millert 119: null_write,
120: null_close
121: };
1.45 tedu 122: #endif /* SMALL */
123:
1.18 mickey 124: int permission(const char *);
1.73 sobrado 125: __dead void usage(int);
1.49 henning 126: int docompress(const char *, char *, const struct compressor *,
1.25 deraadt 127: int, struct stat *);
1.92 tedu 128: int dodecompress(const char *, char *, struct stat *);
1.37 millert 129: const struct compressor *check_method(int);
1.36 millert 130: const char *check_suffix(const char *);
131: char *set_outfile(const char *, char *, size_t);
1.37 millert 132: void list_stats(const char *, const struct compressor *, struct z_info *);
133: void verbose_info(const char *, off_t, off_t, u_int32_t);
1.18 mickey 134:
135: const struct option longopts[] = {
1.45 tedu 136: #ifndef SMALL
1.18 mickey 137: { "ascii", no_argument, 0, 'a' },
138: { "stdout", no_argument, 0, 'c' },
139: { "to-stdout", no_argument, 0, 'c' },
140: { "decompress", no_argument, 0, 'd' },
141: { "uncompress", no_argument, 0, 'd' },
142: { "force", no_argument, 0, 'f' },
143: { "help", no_argument, 0, 'h' },
144: { "list", no_argument, 0, 'l' },
145: { "license", no_argument, 0, 'L' },
146: { "no-name", no_argument, 0, 'n' },
147: { "name", no_argument, 0, 'N' },
148: { "quiet", no_argument, 0, 'q' },
149: { "recursive", no_argument, 0, 'r' },
150: { "suffix", required_argument, 0, 'S' },
151: { "test", no_argument, 0, 't' },
152: { "verbose", no_argument, 0, 'v' },
153: { "version", no_argument, 0, 'V' },
154: { "fast", no_argument, 0, '1' },
155: { "best", no_argument, 0, '9' },
1.45 tedu 156: #endif /* SMALL */
1.18 mickey 157: { NULL }
158: };
1.1 mickey 159:
160: int
1.24 deraadt 161: main(int argc, char *argv[])
1.1 mickey 162: {
1.19 millert 163: FTS *ftsp;
164: FTSENT *entry;
1.18 mickey 165: const struct compressor *method;
1.81 millert 166: const char *optstr, *s;
1.36 millert 167: char *p, *infile;
1.83 deraadt 168: char outfile[PATH_MAX], _infile[PATH_MAX], suffix[16];
1.77 millert 169: int bits, ch, error, rc, cflag, oflag;
1.87 deraadt 170:
1.91 semarie 171: if (pledge("stdio rpath wpath cpath fattr chown", NULL) == -1)
1.89 deraadt 172: err(1, "pledge");
1.1 mickey 173:
1.73 sobrado 174: bits = cflag = oflag = 0;
1.70 millert 175: storename = -1;
1.1 mickey 176: p = __progname;
177: if (p[0] == 'g') {
178: method = M_DEFLATE;
1.18 mickey 179: bits = 6;
1.1 mickey 180: p++;
1.81 millert 181: } else {
1.45 tedu 182: #ifdef SMALL
183: method = M_DEFLATE;
184: #else
1.1 mickey 185: method = M_COMPRESS;
1.45 tedu 186: #endif /* SMALL */
1.81 millert 187: }
188: optstr = method->comp_opts;
1.1 mickey 189:
190: decomp = 0;
1.73 sobrado 191: pmode = MODE_COMP;
1.1 mickey 192: if (!strcmp(p, "zcat")) {
193: decomp++;
1.66 millert 194: cflag = 1;
1.73 sobrado 195: pmode = MODE_CAT;
1.1 mickey 196: } else {
197: if (p[0] == 'u' && p[1] == 'n') {
198: p += 2;
199: decomp++;
1.73 sobrado 200: pmode = MODE_DECOMP;
1.1 mickey 201: }
202:
1.2 mickey 203: if (strcmp(p, "zip") &&
204: strcmp(p, "compress"))
1.1 mickey 205: errx(1, "unknown program name");
206: }
207:
1.18 mickey 208: strlcpy(suffix, method->suffix, sizeof(suffix));
209:
1.68 millert 210: if (method == M_DEFLATE && (p = getenv("GZIP")) != NULL) {
1.77 millert 211: char *evbuf, *last, **nargv = NULL;
212: int argc_extra = 0, nargc = 0;
1.18 mickey 213:
1.77 millert 214: if ((evbuf = strdup(p)) == NULL)
215: err(1, NULL);
216: for ((p = strtok_r(evbuf, " ", &last)); p != NULL;
217: (p = strtok_r(NULL, " ", &last))) {
218: if (nargc + 1 >= argc_extra) {
219: argc_extra += 1024;
1.82 doug 220: nargv = reallocarray(nargv,
221: argc + argc_extra + 1, sizeof(char *));
1.77 millert 222: if (nargv == NULL)
223: err(1, NULL);
224: }
225: nargv[++nargc] = p;
226: }
227: if (nargv != NULL) {
228: nargv[0] = *argv++;
229: while ((nargv[++nargc] = *argv++))
230: ;
231: argv = nargv;
232: argc = nargc;
233: }
1.18 mickey 234: }
235:
1.81 millert 236: optstr += pmode;
237: while ((ch = getopt_long(argc, argv, optstr, longopts, NULL)) != -1)
1.78 deraadt 238: switch (ch) {
1.1 mickey 239: case '1':
240: case '2':
241: case '3':
242: case '4':
243: case '5':
244: case '6':
245: case '7':
246: case '8':
247: case '9':
248: method = M_DEFLATE;
1.18 mickey 249: strlcpy(suffix, method->suffix, sizeof(suffix));
1.1 mickey 250: bits = ch - '0';
251: break;
1.18 mickey 252: case 'a':
253: warnx("option -a is ignored on this system");
254: break;
1.1 mickey 255: case 'b':
256: bits = strtol(optarg, &p, 10);
1.7 denny 257: /*
258: * POSIX 1002.3 says 9 <= bits <= 14 for portable
259: * apps, but says the implementation may allow
260: * greater.
261: */
1.1 mickey 262: if (*p)
263: errx(1, "illegal bit count -- %s", optarg);
264: break;
265: case 'c':
1.66 millert 266: cflag = 1;
1.1 mickey 267: break;
268: case 'd': /* Backward compatible. */
269: decomp++;
270: break;
271: case 'f':
272: force++;
273: break;
274: case 'g':
275: method = M_DEFLATE;
1.18 mickey 276: strlcpy(suffix, method->suffix, sizeof(suffix));
277: bits = 6;
1.1 mickey 278: break;
279: case 'l':
280: list++;
1.71 millert 281: testmode = 1;
1.37 millert 282: decomp++;
1.1 mickey 283: break;
284: case 'n':
1.70 millert 285: storename = 0;
1.1 mickey 286: break;
287: case 'N':
1.70 millert 288: storename = 1;
1.1 mickey 289: break;
1.45 tedu 290: #ifndef SMALL
1.1 mickey 291: case 'O':
292: method = M_COMPRESS;
1.18 mickey 293: strlcpy(suffix, method->suffix, sizeof(suffix));
1.1 mickey 294: break;
1.45 tedu 295: #endif /* SMALL */
1.1 mickey 296: case 'o':
1.18 mickey 297: if (strlcpy(outfile, optarg,
298: sizeof(outfile)) >= sizeof(outfile))
299: errx(1, "-o argument is too long");
1.20 mickey 300: oflag = 1;
1.1 mickey 301: break;
302: case 'q':
303: verbose = -1;
304: break;
305: case 'S':
306: p = suffix;
307: if (optarg[0] != '.')
308: *p++ = '.';
1.18 mickey 309: strlcpy(p, optarg, sizeof(suffix) - (p - suffix));
310: p = optarg;
1.1 mickey 311: break;
312: case 't':
1.20 mickey 313: testmode = 1;
1.29 millert 314: decomp++;
1.1 mickey 315: break;
1.18 mickey 316: case 'V':
1.19 millert 317: exit (0);
1.1 mickey 318: case 'v':
319: verbose++;
320: break;
1.18 mickey 321: case 'L':
1.19 millert 322: exit (0);
1.18 mickey 323: case 'r':
1.19 millert 324: recurse++;
1.18 mickey 325: break;
326:
1.1 mickey 327: case 'h':
1.73 sobrado 328: usage(0);
1.31 millert 329: break;
1.1 mickey 330: default:
1.73 sobrado 331: usage(1);
1.1 mickey 332: }
333: argc -= optind;
334: argv += optind;
1.90 naddy 335:
336: if (cflag || testmode || (!oflag && argc == 0))
337: if (pledge("stdio rpath", NULL) == -1)
338: err(1, "pledge");
1.1 mickey 339:
1.19 millert 340: if (argc == 0) {
1.77 millert 341: argv = calloc(2, sizeof(char *));
342: if (argv == NULL)
343: err(1, NULL);
1.66 millert 344: argv[0] = "-";
1.77 millert 345: argc = 1;
1.19 millert 346: }
347: if (oflag && (recurse || argc > 1))
348: errx(1, "-o option may only be used with a single input file");
1.20 mickey 349:
1.34 mickey 350: if ((cat && argc) + testmode + oflag > 1)
1.19 millert 351: errx(1, "may not mix -o, -c, or -t options");
1.70 millert 352: /*
353: * By default, when compressing store the original name and timestamp
354: * in the header. Do not restore these when decompressing unless
355: * the -N option is given.
356: */
357: if (storename == -1)
358: storename = !decomp;
1.19 millert 359:
360: if ((ftsp = fts_open(argv, FTS_PHYSICAL|FTS_NOCHDIR, 0)) == NULL)
361: err(1, NULL);
1.37 millert 362: for (rc = SUCCESS; (entry = fts_read(ftsp)) != NULL;) {
1.66 millert 363: cat = cflag;
364: pipin = 0;
1.19 millert 365: infile = entry->fts_path;
1.67 otto 366: if (infile[0] == '-' && infile[1] == '\0') {
367: infile = "stdin";
368: pipin++;
369: if (!oflag)
370: cat = 1;
371: }
372: else
373: switch (entry->fts_info) {
374: case FTS_D:
375: if (!recurse) {
376: warnx("%s is a directory: ignored",
377: infile);
378: fts_set(ftsp, entry, FTS_SKIP);
1.66 millert 379: }
1.67 otto 380: continue;
381: case FTS_DP:
382: continue;
383: case FTS_NS:
384: /*
385: * If file does not exist and has no suffix,
386: * tack on the default suffix and try that.
387: */
388: if (entry->fts_errno == ENOENT) {
389: p = strrchr(entry->fts_accpath, '.');
390: if ((p == NULL ||
391: strcmp(p, suffix) != 0) &&
392: snprintf(_infile, sizeof(_infile),
393: "%s%s", infile, suffix) <
394: sizeof(_infile) &&
395: stat(_infile, entry->fts_statp) ==
396: 0 &&
397: S_ISREG(entry->fts_statp->st_mode)) {
398: infile = _infile;
399: break;
400: }
1.66 millert 401: }
1.67 otto 402: case FTS_ERR:
403: case FTS_DNR:
404: warnx("%s: %s", infile,
405: strerror(entry->fts_errno));
1.37 millert 406: rc = rc ? rc : WARNING;
1.19 millert 407: continue;
1.67 otto 408: default:
409: if (!S_ISREG(entry->fts_statp->st_mode) &&
410: !(S_ISLNK(entry->fts_statp->st_mode) &&
411: cat)) {
412: warnx("%s not a regular file%s",
413: infile, cat ? "" : ": unchanged");
414: rc = rc ? rc : WARNING;
415: continue;
416: }
417: break;
1.18 mickey 418: }
1.1 mickey 419:
1.36 millert 420: if (!decomp && !pipin && (s = check_suffix(infile)) != NULL) {
421: warnx("%s already has %s suffix -- unchanged",
422: infile, s);
1.37 millert 423: rc = rc ? rc : WARNING;
1.36 millert 424: continue;
425: }
426:
1.66 millert 427: if (!oflag) {
428: if (cat)
429: strlcpy(outfile, "stdout", sizeof(outfile));
430: else if (decomp) {
1.35 millert 431: if (set_outfile(infile, outfile,
432: sizeof outfile) == NULL) {
1.53 millert 433: if (!recurse) {
1.19 millert 434: warnx("%s: unknown suffix: "
435: "ignored", infile);
1.53 millert 436: rc = rc ? rc : WARNING;
437: }
1.19 millert 438: continue;
439: }
440: } else {
441: if (snprintf(outfile, sizeof(outfile),
442: "%s%s", infile, suffix) >= sizeof(outfile)) {
443: warnx("%s%s: name too long",
444: infile, suffix);
1.53 millert 445: rc = rc ? rc : WARNING;
1.19 millert 446: continue;
447: }
448: }
1.1 mickey 449: }
450:
1.37 millert 451: if (verbose > 0 && !pipin && !list)
1.1 mickey 452: fprintf(stderr, "%s:\t", infile);
453:
1.92 tedu 454: if (decomp)
455: error = dodecompress(infile, outfile, entry->fts_statp);
456: else
457: error = docompress(infile, outfile, method, bits, entry->fts_statp);
1.1 mickey 458:
1.37 millert 459: switch (error) {
460: case SUCCESS:
461: if (!cat && !testmode) {
462: if (!pipin && unlink(infile) && verbose >= 0)
1.18 mickey 463: warn("input: %s", infile);
1.1 mickey 464: }
1.37 millert 465: break;
466: case WARNING:
467: rc = rc ? rc : WARNING;
468: break;
469: default:
470: rc = FAILURE;
471: break;
1.1 mickey 472: }
1.19 millert 473: }
1.37 millert 474: if (list)
475: list_stats(NULL, NULL, NULL);
1.85 uebayasi 476: fts_close(ftsp);
1.18 mickey 477: exit(rc);
1.1 mickey 478: }
479:
480: int
1.49 henning 481: docompress(const char *in, char *out, const struct compressor *method,
1.24 deraadt 482: int bits, struct stat *sb)
1.1 mickey 483: {
1.59 moritz 484: #ifndef SMALL
1.18 mickey 485: u_char buf[Z_BUFSIZE];
1.97 naddy 486: char namebuf[PATH_MAX];
1.37 millert 487: char *name;
1.92 tedu 488: int error, ifd, ofd, oreg;
1.16 mpech 489: void *cookie;
490: ssize_t nr;
1.37 millert 491: u_int32_t mtime;
492: struct z_info info;
1.66 millert 493: struct stat osb;
1.1 mickey 494:
1.37 millert 495: mtime = 0;
1.92 tedu 496: oreg = 0;
1.37 millert 497: error = SUCCESS;
498: name = NULL;
1.1 mickey 499: cookie = NULL;
1.3 mickey 500:
1.66 millert 501: if (pipin)
502: ifd = dup(STDIN_FILENO);
503: else
504: ifd = open(in, O_RDONLY);
1.96 deraadt 505: if (ifd == -1) {
1.21 millert 506: if (verbose >= 0)
1.64 millert 507: warn("%s", in);
1.37 millert 508: return (FAILURE);
1.21 millert 509: }
510:
1.66 millert 511: if (cat)
512: ofd = dup(STDOUT_FILENO);
513: else {
514: if (stat(out, &osb) == 0) {
515: oreg = S_ISREG(osb.st_mode);
516: if (!force && oreg && !permission(out)) {
517: (void) close(ifd);
518: return (WARNING);
519: }
520: }
1.76 millert 521: ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR);
1.66 millert 522: }
1.96 deraadt 523: if (ofd == -1) {
1.3 mickey 524: if (verbose >= 0)
525: warn("%s", out);
1.32 mickey 526: (void) close(ifd);
1.37 millert 527: return (FAILURE);
1.3 mickey 528: }
529:
1.4 mickey 530: if (method != M_COMPRESS && !force && isatty(ofd)) {
1.3 mickey 531: if (verbose >= 0)
532: warnx("%s: won't write compressed data to terminal",
1.25 deraadt 533: out);
1.32 mickey 534: (void) close(ofd);
535: (void) close(ifd);
1.37 millert 536: return (FAILURE);
1.3 mickey 537: }
1.1 mickey 538:
1.70 millert 539: if (!pipin && storename) {
1.97 naddy 540: strlcpy(namebuf, in, sizeof(namebuf));
541: name = basename(namebuf);
1.37 millert 542: mtime = (u_int32_t)sb->st_mtime;
543: }
1.92 tedu 544: if ((cookie = method->wopen(ofd, name, bits, mtime)) == NULL) {
1.32 mickey 545: if (verbose >= 0)
1.66 millert 546: warn("%s", out);
547: if (oreg)
548: (void) unlink(out);
1.32 mickey 549: (void) close(ofd);
550: (void) close(ifd);
1.37 millert 551: return (FAILURE);
1.32 mickey 552: }
1.1 mickey 553:
1.32 mickey 554: while ((nr = read(ifd, buf, sizeof(buf))) > 0)
1.94 tedu 555: if (method->write(cookie, buf, nr) != nr) {
1.32 mickey 556: if (verbose >= 0)
557: warn("%s", out);
1.37 millert 558: error = FAILURE;
1.32 mickey 559: break;
560: }
1.1 mickey 561:
1.32 mickey 562: if (!error && nr < 0) {
563: if (verbose >= 0)
1.1 mickey 564: warn("%s", in);
1.37 millert 565: error = FAILURE;
1.1 mickey 566: }
567:
1.94 tedu 568: if (method->close(cookie, &info, out, sb)) {
1.1 mickey 569: if (!error && verbose >= 0)
570: warn("%s", out);
1.37 millert 571: error = FAILURE;
1.1 mickey 572: }
573:
1.18 mickey 574: if (close(ifd)) {
575: if (!error && verbose >= 0)
1.32 mickey 576: warn("%s", in);
1.37 millert 577: error = FAILURE;
1.18 mickey 578: }
1.37 millert 579:
1.79 millert 580: if (!force && !cat && info.total_out >= info.total_in) {
1.37 millert 581: if (verbose > 0)
582: fprintf(stderr, "file would grow; left unmodified\n");
1.66 millert 583: (void) unlink(out);
584: error = WARNING;
1.37 millert 585: }
586:
1.66 millert 587: if (error) {
588: if (oreg)
589: (void) unlink(out);
590: } else if (verbose > 0)
1.37 millert 591: verbose_info(out, info.total_out, info.total_in, info.hlen);
1.44 deraadt 592:
1.21 millert 593: return (error);
1.59 moritz 594: #else
595: warnx("compression not supported");
596: return (FAILURE);
597: #endif
1.1 mickey 598: }
599:
1.18 mickey 600: const struct compressor *
1.37 millert 601: check_method(int fd)
1.1 mickey 602: {
1.18 mickey 603: const struct compressor *method;
1.33 millert 604: u_char magic[2];
1.1 mickey 605:
1.33 millert 606: if (read(fd, magic, sizeof(magic)) != 2)
607: return (NULL);
608: for (method = &c_table[0]; method->name != NULL; method++) {
609: if (magic[0] == method->magic[0] &&
610: magic[1] == method->magic[1])
611: return (method);
612: }
1.45 tedu 613: #ifndef SMALL
614: if (force && cat) {
615: null_magic[0] = magic[0];
616: null_magic[1] = magic[1];
617: return (&null_method);
618: }
619: #endif /* SMALL */
1.33 millert 620: return (NULL);
1.1 mickey 621: }
622:
623: int
1.92 tedu 624: dodecompress(const char *in, char *out, struct stat *sb)
1.1 mickey 625: {
1.92 tedu 626: const struct compressor *method;
1.18 mickey 627: u_char buf[Z_BUFSIZE];
1.83 deraadt 628: char oldname[PATH_MAX];
1.66 millert 629: int error, oreg, ifd, ofd;
1.16 mpech 630: void *cookie;
631: ssize_t nr;
1.37 millert 632: struct z_info info;
1.66 millert 633: struct stat osb;
1.1 mickey 634:
1.66 millert 635: oreg = 0;
1.37 millert 636: error = SUCCESS;
1.1 mickey 637: cookie = NULL;
638:
1.66 millert 639: if (pipin)
640: ifd = dup(STDIN_FILENO);
641: else
642: ifd = open(in, O_RDONLY);
1.96 deraadt 643: if (ifd == -1) {
1.3 mickey 644: if (verbose >= 0)
645: warn("%s", in);
646: return -1;
647: }
648:
649: if (!force && isatty(ifd)) {
650: if (verbose >= 0)
651: warnx("%s: won't read compressed data from terminal",
1.25 deraadt 652: in);
1.3 mickey 653: close (ifd);
654: return -1;
655: }
656:
1.37 millert 657: if ((method = check_method(ifd)) == NULL) {
1.3 mickey 658: if (verbose >= 0)
659: warnx("%s: unrecognized file format", in);
1.13 d 660: close (ifd);
1.3 mickey 661: return -1;
662: }
663:
1.95 millert 664: /* XXX - open constrains outfile to PATH_MAX so this is safe */
1.66 millert 665: oldname[0] = '\0';
1.92 tedu 666: if ((cookie = method->ropen(ifd, oldname, 1)) == NULL) {
1.32 mickey 667: if (verbose >= 0)
668: warn("%s", in);
669: close (ifd);
1.37 millert 670: return (FAILURE);
1.32 mickey 671: }
1.70 millert 672: if (storename && oldname[0] != '\0') {
1.95 millert 673: char *oldbase = basename(oldname);
1.74 millert 674: char *cp = strrchr(out, '/');
675: if (cp != NULL) {
676: *(cp + 1) = '\0';
1.95 millert 677: strlcat(out, oldbase, PATH_MAX);
1.74 millert 678: } else
1.95 millert 679: strlcpy(out, oldbase, PATH_MAX);
1.66 millert 680: cat = 0; /* XXX should -c override? */
681: }
1.32 mickey 682:
1.37 millert 683: if (testmode)
684: ofd = -1;
1.66 millert 685: else {
686: if (cat)
687: ofd = dup(STDOUT_FILENO);
688: else {
689: if (stat(out, &osb) == 0) {
690: oreg = S_ISREG(osb.st_mode);
691: if (!force && oreg && !permission(out)) {
692: (void) close(ifd);
693: return (WARNING);
694: }
695: }
696: ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR);
697: }
1.96 deraadt 698: if (ofd == -1) {
1.66 millert 699: if (verbose >= 0)
700: warn("%s", in);
1.94 tedu 701: method->close(cookie, NULL, NULL, NULL);
1.66 millert 702: return (FAILURE);
703: }
1.32 mickey 704: }
705:
1.94 tedu 706: while ((nr = method->read(cookie, buf, sizeof(buf))) > 0) {
1.37 millert 707: if (ofd != -1 && write(ofd, buf, nr) != nr) {
1.21 millert 708: if (verbose >= 0)
1.32 mickey 709: warn("%s", out);
1.37 millert 710: error = FAILURE;
1.32 mickey 711: break;
1.21 millert 712: }
1.37 millert 713: }
1.1 mickey 714:
1.32 mickey 715: if (!error && nr < 0) {
716: if (verbose >= 0)
717: warnx("%s: %s", in,
718: errno == EINVAL ? "crc error" : strerror(errno));
1.37 millert 719: error = errno == EINVAL ? WARNING : FAILURE;
1.1 mickey 720: }
721:
1.94 tedu 722: if (method->close(cookie, &info, NULL, NULL)) {
1.1 mickey 723: if (!error && verbose >= 0)
1.32 mickey 724: warnx("%s", in);
1.37 millert 725: error = FAILURE;
726: }
1.70 millert 727: if (storename && !cat) {
1.40 millert 728: if (info.mtime != 0) {
729: sb->st_mtimespec.tv_sec =
730: sb->st_atimespec.tv_sec = info.mtime;
731: sb->st_mtimespec.tv_nsec =
732: sb->st_atimespec.tv_nsec = 0;
733: } else
1.70 millert 734: storename = 0; /* no timestamp to restore */
1.1 mickey 735: }
1.63 otto 736: if (error == SUCCESS)
737: setfile(out, ofd, sb);
1.1 mickey 738:
1.37 millert 739: if (ofd != -1 && close(ofd)) {
1.1 mickey 740: if (!error && verbose >= 0)
1.18 mickey 741: warn("%s", out);
1.37 millert 742: error = FAILURE;
743: }
744:
745: if (!error) {
746: if (list) {
747: if (info.mtime == 0)
748: info.mtime = (u_int32_t)sb->st_mtime;
1.43 millert 749: list_stats(out, method, &info);
1.37 millert 750: } else if (verbose > 0) {
751: verbose_info(out, info.total_in, info.total_out,
752: info.hlen);
753: }
1.1 mickey 754: }
755:
1.66 millert 756: /* On error, clean up the file we created but preserve errno. */
757: if (error && oreg)
758: unlink(out);
759:
1.21 millert 760: return (error);
1.1 mickey 761: }
762:
763: void
1.60 deraadt 764: setfile(const char *name, int fd, struct stat *fs)
1.1 mickey 765: {
1.84 guenther 766: struct timespec ts[2];
1.1 mickey 767:
1.63 otto 768: if (name == NULL || cat || testmode)
1.60 deraadt 769: return;
770:
1.40 millert 771: /*
772: * If input was a pipe we don't have any info to restore but we
773: * must set the mode since the current mode on the file is 0200.
774: */
775: if (pipin) {
776: mode_t mask = umask(022);
1.60 deraadt 777: fchmod(fd, DEFFILEMODE & ~mask);
1.40 millert 778: umask(mask);
779: return;
780: }
1.1 mickey 781:
782: /*
783: * Changing the ownership probably won't succeed, unless we're root
1.86 deraadt 784: * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid bits are not
785: * allowed.
1.1 mickey 786: */
1.86 deraadt 787: fs->st_mode &= ACCESSPERMS;
1.60 deraadt 788: if (fchown(fd, fs->st_uid, fs->st_gid)) {
1.1 mickey 789: if (errno != EPERM)
1.61 hshoexer 790: warn("fchown: %s", name);
1.1 mickey 791: fs->st_mode &= ~(S_ISUID|S_ISGID);
792: }
1.60 deraadt 793: if (fchmod(fd, fs->st_mode))
1.61 hshoexer 794: warn("fchmod: %s", name);
1.1 mickey 795:
1.60 deraadt 796: if (fs->st_flags && fchflags(fd, fs->st_flags))
1.61 hshoexer 797: warn("fchflags: %s", name);
1.64 millert 798:
1.84 guenther 799: ts[0] = fs->st_atim;
800: ts[1] = fs->st_mtim;
801: if (futimens(fd, ts))
802: warn("futimens: %s", name);
1.1 mickey 803: }
804:
805: int
1.24 deraadt 806: permission(const char *fname)
1.1 mickey 807: {
808: int ch, first;
809:
810: if (!isatty(fileno(stderr)))
811: return (0);
812: (void)fprintf(stderr, "overwrite %s? ", fname);
813: first = ch = getchar();
814: while (ch != '\n' && ch != EOF)
815: ch = getchar();
816: return (first == 'y');
1.35 millert 817: }
818:
819: /*
1.36 millert 820: * Check infile for a known suffix and return the suffix portion or NULL.
821: */
822: const char *
823: check_suffix(const char *infile)
824: {
825: int i;
826: char *suf, *sep, *separators = ".-_";
827: static char *suffixes[] = { "Z", "gz", "z", "tgz", "taz", NULL };
828:
829: for (sep = separators; *sep != '\0'; sep++) {
830: if ((suf = strrchr(infile, *sep)) == NULL)
831: continue;
832: suf++;
833:
834: for (i = 0; suffixes[i] != NULL; i++) {
835: if (strcmp(suf, suffixes[i]) == 0)
836: return (suf - 1);
837: }
838: }
839: return (NULL);
840: }
841:
842: /*
1.35 millert 843: * Set outfile based on the suffix. In most cases we just strip
844: * off the suffix but things like .tgz and .taz are special.
845: */
846: char *
1.36 millert 847: set_outfile(const char *infile, char *outfile, size_t osize)
1.35 millert 848: {
1.36 millert 849: const char *s;
850: char *cp;
851:
852: if ((s = check_suffix(infile)) == NULL)
1.35 millert 853: return (NULL);
854:
1.36 millert 855: (void)strlcpy(outfile, infile, osize);
856: cp = outfile + (s - infile) + 1;
857: /*
858: * Convert tgz and taz -> tar, else drop the suffix.
859: */
860: if (strcmp(cp, "tgz") == 0) {
861: cp[1] = 'a';
862: cp[2] = 'r';
863: } else if (strcmp(cp, "taz") == 0)
864: cp[2] = 'r';
865: else
866: cp[-1] = '\0';
867: return (outfile);
1.37 millert 868: }
869:
870: /*
871: * Print output for the -l option.
872: */
873: void
874: list_stats(const char *name, const struct compressor *method,
875: struct z_info *info)
876: {
877: static off_t compressed_total, uncompressed_total, header_total;
878: static u_int nruns;
879: char *timestr;
1.43 millert 880:
1.37 millert 881: if (nruns == 0) {
882: if (verbose >= 0) {
883: if (verbose > 0)
1.48 millert 884: fputs("method crc date time ", stdout);
885: puts("compressed uncompressed ratio uncompressed_name");
1.37 millert 886: }
887: }
888: nruns++;
889:
890: if (name != NULL) {
891: if (verbose > 0) {
1.80 deraadt 892: time_t t = info->mtime; /* XXX 32 bit mtime */
893:
894: timestr = ctime(&t) + 4;
1.37 millert 895: timestr[12] = '\0';
1.48 millert 896: if (timestr[4] == ' ')
897: timestr[4] = '0';
898: printf("%-7.7s %08x %s ", method->name, info->crc,
1.62 deraadt 899: timestr);
1.37 millert 900: }
1.48 millert 901: printf("%10lld %10lld %4.1f%% %s\n",
1.37 millert 902: (long long)(info->total_in + info->hlen),
903: (long long)info->total_out,
1.57 otto 904: ((long long)info->total_out - (long long)info->total_in) *
1.37 millert 905: 100.0 / info->total_out, name);
906: compressed_total += info->total_in;
907: uncompressed_total += info->total_out;
908: header_total += info->hlen;
909: } else if (verbose >= 0) {
910: if (nruns < 3) /* only do totals for > 1 files */
911: return;
912: if (verbose > 0)
1.48 millert 913: fputs(" ", stdout);
914: printf("%10lld %10lld %4.1f%% (totals)\n",
1.37 millert 915: (long long)(compressed_total + header_total),
916: (long long)uncompressed_total,
917: (uncompressed_total - compressed_total) *
918: 100.0 / uncompressed_total);
919: }
920: }
921:
922: void
923: verbose_info(const char *file, off_t compressed, off_t uncompressed,
924: u_int32_t hlen)
925: {
926: if (testmode) {
927: fputs("OK\n", stderr);
928: return;
929: }
930: if (!pipin) {
931: fprintf(stderr, "\t%4.1f%% -- replaced with %s\n",
932: (uncompressed - compressed) * 100.0 / uncompressed, file);
933: }
934: compressed += hlen;
1.44 deraadt 935: fprintf(stderr, "%lld bytes in, %lld bytes out\n",
1.37 millert 936: (long long)(decomp ? compressed : uncompressed),
937: (long long)(decomp ? uncompressed : compressed));
1.1 mickey 938: }
939:
1.31 millert 940: __dead void
1.73 sobrado 941: usage(int status)
1.1 mickey 942: {
1.81 millert 943: const bool gzip = (__progname[0] == 'g');
944:
1.73 sobrado 945: switch (pmode) {
1.72 sobrado 946: case MODE_COMP:
1.81 millert 947: fprintf(stderr, "usage: %s [-123456789cdf%sh%slNnOqrt%sv] "
1.72 sobrado 948: "[-b bits] [-o filename] [-S suffix]\n"
1.81 millert 949: " %*s [file ...]\n", __progname,
950: !gzip ? "g" : "", gzip ? "L" : "", gzip ? "V" : "",
951: (int)strlen(__progname), "");
1.72 sobrado 952: break;
953: case MODE_DECOMP:
1.81 millert 954: fprintf(stderr, "usage: %s [-cfh%slNnqrt%sv] [-o filename] "
955: "[file ...]\n", __progname,
956: gzip ? "L" : "", gzip ? "V" : "");
1.72 sobrado 957: break;
958: case MODE_CAT:
1.81 millert 959: fprintf(stderr, "usage: %s [-f%shqr] [file ...]\n",
960: __progname, gzip ? "" : "g");
1.72 sobrado 961: break;
962: }
1.31 millert 963: exit(status);
1.1 mickey 964: }