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