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