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