1 /* $NetBSD: gzip.c,v 1.116 2018/10/27 11:39:12 skrll Exp $ */
2
3 /*-
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 1997, 1998, 2003, 2004, 2006, 2008, 2009, 2010, 2011, 2015, 2017
7 * Matthew R. Green
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 */
32
33 /*
34 * gzip.c -- GPL free gzip using zlib.
35 *
36 * RFC 1950 covers the zlib format
37 * RFC 1951 covers the deflate format
38 * RFC 1952 covers the gzip format
39 *
40 * TODO:
41 * - use mmap where possible
42 * - make bzip2/compress -v/-t/-l support work as well as possible
43 */
44
45 #include <sys/endian.h>
46 #include <sys/param.h>
47 #include <sys/stat.h>
48 #include <sys/time.h>
49
50 #include <inttypes.h>
51 #include <unistd.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <stdlib.h>
55 #include <err.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <zlib.h>
59 #include <fts.h>
60 #include <libgen.h>
61 #include <stdarg.h>
62 #include <getopt.h>
63 #include <time.h>
64
65 /* what type of file are we dealing with */
66 enum filetype {
67 FT_GZIP,
68 #ifndef NO_BZIP2_SUPPORT
69 FT_BZIP2,
70 #endif
71 #ifndef NO_COMPRESS_SUPPORT
72 FT_Z,
73 #endif
74 #ifndef NO_PACK_SUPPORT
75 FT_PACK,
76 #endif
77 #ifndef NO_XZ_SUPPORT
78 FT_XZ,
79 #endif
80 #ifndef NO_LZ_SUPPORT
81 FT_LZ,
82 #endif
83 #ifndef NO_ZSTD_SUPPORT
84 FT_ZSTD,
85 #endif
86 FT_LAST,
87 FT_UNKNOWN
88 };
89
90 #ifndef NO_BZIP2_SUPPORT
91 #include <bzlib.h>
92
93 #define BZ2_SUFFIX ".bz2"
94 #define BZIP2_MAGIC "BZh"
95 #endif
96
97 #ifndef NO_COMPRESS_SUPPORT
98 #define Z_SUFFIX ".Z"
99 #define Z_MAGIC "\037\235"
100 #endif
101
102 #ifndef NO_PACK_SUPPORT
103 #define PACK_MAGIC "\037\036"
104 #endif
105
106 #ifndef NO_XZ_SUPPORT
107 #include <lzma.h>
108 #define XZ_SUFFIX ".xz"
109 #define XZ_MAGIC "\3757zXZ"
110 #endif
111
112 #ifndef NO_LZ_SUPPORT
113 #define LZ_SUFFIX ".lz"
114 #define LZ_MAGIC "LZIP"
115 #endif
116
117 #ifndef NO_ZSTD_SUPPORT
118 #include <zstd.h>
119 #define ZSTD_SUFFIX ".zst"
120 #define ZSTD_MAGIC "\050\265\057\375"
121 #endif
122
123 #define GZ_SUFFIX ".gz"
124
125 #define BUFLEN (64 * 1024)
126
127 #define GZIP_MAGIC0 0x1F
128 #define GZIP_MAGIC1 0x8B
129 #define GZIP_OMAGIC1 0x9E
130
131 #define GZIP_TIMESTAMP (off_t)4
132 #define GZIP_ORIGNAME (off_t)10
133
134 #define HEAD_CRC 0x02
135 #define EXTRA_FIELD 0x04
136 #define ORIG_NAME 0x08
137 #define COMMENT 0x10
138
139 #define OS_CODE 3 /* Unix */
140
141 typedef struct {
142 const char *zipped;
143 int ziplen;
144 const char *normal; /* for unzip - must not be longer than zipped */
145 } suffixes_t;
146 static suffixes_t suffixes[] = {
147 #define SUFFIX(Z, N) {Z, sizeof Z - 1, N}
148 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */
149 SUFFIX(GZ_SUFFIX, ""),
150 SUFFIX(".z", ""),
151 SUFFIX("-gz", ""),
152 SUFFIX("-z", ""),
153 SUFFIX("_z", ""),
154 SUFFIX(".taz", ".tar"),
155 SUFFIX(".tgz", ".tar"),
156 #ifndef NO_BZIP2_SUPPORT
157 SUFFIX(BZ2_SUFFIX, ""),
158 SUFFIX(".tbz", ".tar"),
159 SUFFIX(".tbz2", ".tar"),
160 #endif
161 #ifndef NO_COMPRESS_SUPPORT
162 SUFFIX(Z_SUFFIX, ""),
163 #endif
164 #ifndef NO_XZ_SUPPORT
165 SUFFIX(XZ_SUFFIX, ""),
166 #endif
167 #ifndef NO_LZ_SUPPORT
168 SUFFIX(LZ_SUFFIX, ""),
169 #endif
170 #ifndef NO_ZSTD_SUPPORT
171 SUFFIX(ZSTD_SUFFIX, ""),
172 #endif
173 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */
174 #undef SUFFIX
175 };
176 #define NUM_SUFFIXES (nitems(suffixes))
177 #define SUFFIX_MAXLEN 30
178
179 static const char gzip_version[] = "FreeBSD gzip 20190107";
180
181 static const char gzip_copyright[] = \
182 " Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
183 " All rights reserved.\n"
184 "\n"
185 " Redistribution and use in source and binary forms, with or without\n"
186 " modification, are permitted provided that the following conditions\n"
187 " are met:\n"
188 " 1. Redistributions of source code must retain the above copyright\n"
189 " notice, this list of conditions and the following disclaimer.\n"
190 " 2. Redistributions in binary form must reproduce the above copyright\n"
191 " notice, this list of conditions and the following disclaimer in the\n"
192 " documentation and/or other materials provided with the distribution.\n"
193 "\n"
194 " THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
195 " IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
196 " OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
197 " IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
198 " INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
199 " BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
200 " LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
201 " AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
202 " OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
203 " OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
204 " SUCH DAMAGE.";
205
206 static int cflag; /* stdout mode */
207 static int dflag; /* decompress mode */
208 static int lflag; /* list mode */
209 static int numflag = 6; /* gzip -1..-9 value */
210
211 static const char *remove_file = NULL; /* file to be removed upon SIGINT */
212
213 static int fflag; /* force mode */
214 static int kflag; /* don't delete input files */
215 static int nflag; /* don't save name/timestamp */
216 static int Nflag; /* don't restore name/timestamp */
217 static int qflag; /* quiet mode */
218 static int rflag; /* recursive mode */
219 static int tflag; /* test */
220 static int vflag; /* verbose mode */
221 static sig_atomic_t print_info = 0;
222
223 static int exit_value = 0; /* exit value */
224
225 static const char *infile; /* name of file coming in */
226
227 static void maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2;
228 #if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
229 !defined(NO_XZ_SUPPORT) || !defined(NO_ZSTD_SUPPORT)
230 static void maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2;
231 #endif
232 static void maybe_warn(const char *fmt, ...) __printflike(1, 2);
233 static void maybe_warnx(const char *fmt, ...) __printflike(1, 2);
234 static enum filetype file_gettype(u_char *);
235 static off_t gz_compress(int, int, off_t *, const char *, uint32_t);
236 static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *);
237 static off_t file_compress(char *, char *, size_t);
238 static off_t file_uncompress(char *, char *, size_t);
239 static void handle_pathname(char *);
240 static void handle_file(char *, struct stat *);
241 static void handle_stdin(void);
242 static void handle_stdout(void);
243 static void print_ratio(off_t, off_t, FILE *);
244 static void print_list(int fd, off_t, const char *, time_t);
245 static void usage(void) __dead2;
246 static void display_version(void) __dead2;
247 static void display_license(void);
248 static const suffixes_t *check_suffix(char *, int);
249 static ssize_t read_retry(int, void *, size_t);
250 static ssize_t write_retry(int, const void *, size_t);
251 static void print_list_out(off_t, off_t, const char*);
252
253 static void infile_set(const char *newinfile, off_t total);
254
255 static off_t infile_total; /* total expected to read/write */
256 static off_t infile_current; /* current read/write */
257
258 static void check_siginfo(void);
259 static off_t cat_fd(unsigned char *, size_t, off_t *, int fd);
260 static void prepend_gzip(char *, int *, char ***);
261 static void handle_dir(char *);
262 static void print_verbage(const char *, const char *, off_t, off_t);
263 static void print_test(const char *, int);
264 static void copymodes(int fd, const struct stat *, const char *file);
265 static int check_outfile(const char *outfile);
266 static void setup_signals(void);
267 static void infile_newdata(size_t newdata);
268 static void infile_clear(void);
269
270 #ifndef NO_BZIP2_SUPPORT
271 static off_t unbzip2(int, int, char *, size_t, off_t *);
272 #endif
273
274 #ifndef NO_COMPRESS_SUPPORT
275 static FILE *zdopen(int);
276 static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *);
277 #endif
278
279 #ifndef NO_PACK_SUPPORT
280 static off_t unpack(int, int, char *, size_t, off_t *);
281 #endif
282
283 #ifndef NO_XZ_SUPPORT
284 static off_t unxz(int, int, char *, size_t, off_t *);
285 static off_t unxz_len(int);
286 #endif
287
288 #ifndef NO_LZ_SUPPORT
289 static off_t unlz(int, int, char *, size_t, off_t *);
290 #endif
291
292 #ifndef NO_ZSTD_SUPPORT
293 static off_t unzstd(int, int, char *, size_t, off_t *);
294 #endif
295
296 static const struct option longopts[] = {
297 { "stdout", no_argument, 0, 'c' },
298 { "to-stdout", no_argument, 0, 'c' },
299 { "decompress", no_argument, 0, 'd' },
300 { "uncompress", no_argument, 0, 'd' },
301 { "force", no_argument, 0, 'f' },
302 { "help", no_argument, 0, 'h' },
303 { "keep", no_argument, 0, 'k' },
304 { "list", no_argument, 0, 'l' },
305 { "no-name", no_argument, 0, 'n' },
306 { "name", no_argument, 0, 'N' },
307 { "quiet", no_argument, 0, 'q' },
308 { "recursive", no_argument, 0, 'r' },
309 { "suffix", required_argument, 0, 'S' },
310 { "test", no_argument, 0, 't' },
311 { "verbose", no_argument, 0, 'v' },
312 { "version", no_argument, 0, 'V' },
313 { "fast", no_argument, 0, '1' },
314 { "best", no_argument, 0, '9' },
315 { "ascii", no_argument, 0, 'a' },
316 { "license", no_argument, 0, 'L' },
317 { NULL, no_argument, 0, 0 },
318 };
319
320 int
main(int argc,char ** argv)321 main(int argc, char **argv)
322 {
323 const char *progname = getprogname();
324 char *gzip;
325 int len;
326 int ch;
327
328 setup_signals();
329
330 if ((gzip = getenv("GZIP")) != NULL)
331 prepend_gzip(gzip, &argc, &argv);
332
333 /*
334 * XXX
335 * handle being called `gunzip', `zcat' and `gzcat'
336 */
337 if (strcmp(progname, "gunzip") == 0)
338 dflag = 1;
339 else if (strcmp(progname, "zcat") == 0 ||
340 strcmp(progname, "gzcat") == 0)
341 dflag = cflag = 1;
342
343 #define OPT_LIST "123456789acdfhklLNnqrS:tVv"
344
345 while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
346 switch (ch) {
347 case '1': case '2': case '3':
348 case '4': case '5': case '6':
349 case '7': case '8': case '9':
350 numflag = ch - '0';
351 break;
352 case 'c':
353 cflag = 1;
354 break;
355 case 'd':
356 dflag = 1;
357 break;
358 case 'l':
359 lflag = 1;
360 dflag = 1;
361 break;
362 case 'V':
363 display_version();
364 /* NOTREACHED */
365 case 'a':
366 fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
367 break;
368 case 'f':
369 fflag = 1;
370 break;
371 case 'k':
372 kflag = 1;
373 break;
374 case 'L':
375 display_license();
376 /* NOT REACHED */
377 case 'N':
378 nflag = 0;
379 Nflag = 1;
380 break;
381 case 'n':
382 nflag = 1;
383 Nflag = 0;
384 break;
385 case 'q':
386 qflag = 1;
387 break;
388 case 'r':
389 rflag = 1;
390 break;
391 case 'S':
392 len = strlen(optarg);
393 if (len != 0) {
394 if (len > SUFFIX_MAXLEN)
395 errx(1, "incorrect suffix: '%s': too long", optarg);
396 suffixes[0].zipped = optarg;
397 suffixes[0].ziplen = len;
398 } else {
399 suffixes[NUM_SUFFIXES - 1].zipped = "";
400 suffixes[NUM_SUFFIXES - 1].ziplen = 0;
401 }
402 break;
403 case 't':
404 cflag = 1;
405 tflag = 1;
406 dflag = 1;
407 break;
408 case 'v':
409 vflag = 1;
410 break;
411 default:
412 usage();
413 /* NOTREACHED */
414 }
415 }
416 argv += optind;
417 argc -= optind;
418
419 if (argc == 0) {
420 if (dflag) /* stdin mode */
421 handle_stdin();
422 else /* stdout mode */
423 handle_stdout();
424 } else {
425 do {
426 handle_pathname(argv[0]);
427 } while (*++argv);
428 }
429 if (qflag == 0 && lflag && argc > 1)
430 print_list(-1, 0, "(totals)", 0);
431 exit(exit_value);
432 }
433
434 /* maybe print a warning */
435 void
maybe_warn(const char * fmt,...)436 maybe_warn(const char *fmt, ...)
437 {
438 va_list ap;
439
440 if (qflag == 0) {
441 va_start(ap, fmt);
442 vwarn(fmt, ap);
443 va_end(ap);
444 }
445 if (exit_value == 0)
446 exit_value = 1;
447 }
448
449 /* ... without an errno. */
450 void
maybe_warnx(const char * fmt,...)451 maybe_warnx(const char *fmt, ...)
452 {
453 va_list ap;
454
455 if (qflag == 0) {
456 va_start(ap, fmt);
457 vwarnx(fmt, ap);
458 va_end(ap);
459 }
460 if (exit_value == 0)
461 exit_value = 1;
462 }
463
464 /* maybe print an error */
465 void
maybe_err(const char * fmt,...)466 maybe_err(const char *fmt, ...)
467 {
468 va_list ap;
469
470 if (qflag == 0) {
471 va_start(ap, fmt);
472 vwarn(fmt, ap);
473 va_end(ap);
474 }
475 exit(2);
476 }
477
478 #if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
479 !defined(NO_XZ_SUPPORT) || !defined(NO_ZSTD_SUPPORT)
480 /* ... without an errno. */
481 void
maybe_errx(const char * fmt,...)482 maybe_errx(const char *fmt, ...)
483 {
484 va_list ap;
485
486 if (qflag == 0) {
487 va_start(ap, fmt);
488 vwarnx(fmt, ap);
489 va_end(ap);
490 }
491 exit(2);
492 }
493 #endif
494
495 /* split up $GZIP and prepend it to the argument list */
496 static void
prepend_gzip(char * gzip,int * argc,char *** argv)497 prepend_gzip(char *gzip, int *argc, char ***argv)
498 {
499 char *s, **nargv, **ac;
500 int nenvarg = 0, i;
501
502 /* scan how many arguments there are */
503 for (s = gzip;;) {
504 while (*s == ' ' || *s == '\t')
505 s++;
506 if (*s == 0)
507 goto count_done;
508 nenvarg++;
509 while (*s != ' ' && *s != '\t')
510 if (*s++ == 0)
511 goto count_done;
512 }
513 count_done:
514 /* punt early */
515 if (nenvarg == 0)
516 return;
517
518 *argc += nenvarg;
519 ac = *argv;
520
521 nargv = (char **)malloc((*argc + 1) * sizeof(char *));
522 if (nargv == NULL)
523 maybe_err("malloc");
524
525 /* stash this away */
526 *argv = nargv;
527
528 /* copy the program name first */
529 i = 0;
530 nargv[i++] = *(ac++);
531
532 /* take a copy of $GZIP and add it to the array */
533 s = strdup(gzip);
534 if (s == NULL)
535 maybe_err("strdup");
536 for (;;) {
537 /* Skip whitespaces. */
538 while (*s == ' ' || *s == '\t')
539 s++;
540 if (*s == 0)
541 goto copy_done;
542 nargv[i++] = s;
543 /* Find the end of this argument. */
544 while (*s != ' ' && *s != '\t')
545 if (*s++ == 0)
546 /* Argument followed by NUL. */
547 goto copy_done;
548 /* Terminate by overwriting ' ' or '\t' with NUL. */
549 *s++ = 0;
550 }
551 copy_done:
552
553 /* copy the original arguments and a NULL */
554 while (*ac)
555 nargv[i++] = *(ac++);
556 nargv[i] = NULL;
557 }
558
559 /* compress input to output. Return bytes read, -1 on error */
560 static off_t
gz_compress(int in,int out,off_t * gsizep,const char * origname,uint32_t mtime)561 gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
562 {
563 z_stream z;
564 char *outbufp, *inbufp;
565 off_t in_tot = 0, out_tot = 0;
566 ssize_t in_size;
567 int i, error;
568 uLong crc;
569
570 outbufp = malloc(BUFLEN);
571 inbufp = malloc(BUFLEN);
572 if (outbufp == NULL || inbufp == NULL) {
573 maybe_err("malloc failed");
574 goto out;
575 }
576
577 memset(&z, 0, sizeof z);
578 z.zalloc = Z_NULL;
579 z.zfree = Z_NULL;
580 z.opaque = 0;
581
582 if (nflag != 0) {
583 mtime = 0;
584 origname = "";
585 }
586
587 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
588 GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
589 *origname ? ORIG_NAME : 0,
590 mtime & 0xff,
591 (mtime >> 8) & 0xff,
592 (mtime >> 16) & 0xff,
593 (mtime >> 24) & 0xff,
594 numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
595 OS_CODE, origname);
596 if (i >= BUFLEN)
597 /* this need PATH_MAX > BUFLEN ... */
598 maybe_err("snprintf");
599 if (*origname)
600 i++;
601
602 z.next_out = (unsigned char *)outbufp + i;
603 z.avail_out = BUFLEN - i;
604
605 error = deflateInit2(&z, numflag, Z_DEFLATED,
606 (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
607 if (error != Z_OK) {
608 maybe_warnx("deflateInit2 failed");
609 in_tot = -1;
610 goto out;
611 }
612
613 crc = crc32(0L, Z_NULL, 0);
614 for (;;) {
615 if (z.avail_out == 0) {
616 if (write_retry(out, outbufp, BUFLEN) != BUFLEN) {
617 maybe_warn("write");
618 out_tot = -1;
619 goto out;
620 }
621
622 out_tot += BUFLEN;
623 z.next_out = (unsigned char *)outbufp;
624 z.avail_out = BUFLEN;
625 }
626
627 if (z.avail_in == 0) {
628 in_size = read(in, inbufp, BUFLEN);
629 if (in_size < 0) {
630 maybe_warn("read");
631 in_tot = -1;
632 goto out;
633 }
634 if (in_size == 0)
635 break;
636 infile_newdata(in_size);
637
638 crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
639 in_tot += in_size;
640 z.next_in = (unsigned char *)inbufp;
641 z.avail_in = in_size;
642 }
643
644 error = deflate(&z, Z_NO_FLUSH);
645 if (error != Z_OK && error != Z_STREAM_END) {
646 maybe_warnx("deflate failed");
647 in_tot = -1;
648 goto out;
649 }
650 }
651
652 /* clean up */
653 for (;;) {
654 size_t len;
655 ssize_t w;
656
657 error = deflate(&z, Z_FINISH);
658 if (error != Z_OK && error != Z_STREAM_END) {
659 maybe_warnx("deflate failed");
660 in_tot = -1;
661 goto out;
662 }
663
664 len = (char *)z.next_out - outbufp;
665
666 w = write_retry(out, outbufp, len);
667 if (w == -1 || (size_t)w != len) {
668 maybe_warn("write");
669 out_tot = -1;
670 goto out;
671 }
672 out_tot += len;
673 z.next_out = (unsigned char *)outbufp;
674 z.avail_out = BUFLEN;
675
676 if (error == Z_STREAM_END)
677 break;
678 }
679
680 if (deflateEnd(&z) != Z_OK) {
681 maybe_warnx("deflateEnd failed");
682 in_tot = -1;
683 goto out;
684 }
685
686 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
687 (int)crc & 0xff,
688 (int)(crc >> 8) & 0xff,
689 (int)(crc >> 16) & 0xff,
690 (int)(crc >> 24) & 0xff,
691 (int)in_tot & 0xff,
692 (int)(in_tot >> 8) & 0xff,
693 (int)(in_tot >> 16) & 0xff,
694 (int)(in_tot >> 24) & 0xff);
695 if (i != 8)
696 maybe_err("snprintf");
697 if (write_retry(out, outbufp, i) != i) {
698 maybe_warn("write");
699 in_tot = -1;
700 } else
701 out_tot += i;
702
703 out:
704 if (inbufp != NULL)
705 free(inbufp);
706 if (outbufp != NULL)
707 free(outbufp);
708 if (gsizep)
709 *gsizep = out_tot;
710 return in_tot;
711 }
712
713 /*
714 * uncompress input to output then close the input. return the
715 * uncompressed size written, and put the compressed sized read
716 * into `*gsizep'.
717 */
718 static off_t
gz_uncompress(int in,int out,char * pre,size_t prelen,off_t * gsizep,const char * filename)719 gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
720 const char *filename)
721 {
722 z_stream z;
723 char *outbufp, *inbufp;
724 off_t out_tot = -1, in_tot = 0;
725 uint32_t out_sub_tot = 0;
726 enum {
727 GZSTATE_MAGIC0,
728 GZSTATE_MAGIC1,
729 GZSTATE_METHOD,
730 GZSTATE_FLAGS,
731 GZSTATE_SKIPPING,
732 GZSTATE_EXTRA,
733 GZSTATE_EXTRA2,
734 GZSTATE_EXTRA3,
735 GZSTATE_ORIGNAME,
736 GZSTATE_COMMENT,
737 GZSTATE_HEAD_CRC1,
738 GZSTATE_HEAD_CRC2,
739 GZSTATE_INIT,
740 GZSTATE_READ,
741 GZSTATE_CRC,
742 GZSTATE_LEN,
743 } state = GZSTATE_MAGIC0;
744 int flags = 0, skip_count = 0;
745 int error = Z_STREAM_ERROR, done_reading = 0;
746 uLong crc = 0;
747 ssize_t wr;
748 int needmore = 0;
749
750 #define ADVANCE() { z.next_in++; z.avail_in--; }
751
752 if ((outbufp = malloc(BUFLEN)) == NULL) {
753 maybe_err("malloc failed");
754 goto out2;
755 }
756 if ((inbufp = malloc(BUFLEN)) == NULL) {
757 maybe_err("malloc failed");
758 goto out1;
759 }
760
761 memset(&z, 0, sizeof z);
762 z.avail_in = prelen;
763 z.next_in = (unsigned char *)pre;
764 z.avail_out = BUFLEN;
765 z.next_out = (unsigned char *)outbufp;
766 z.zalloc = NULL;
767 z.zfree = NULL;
768 z.opaque = 0;
769
770 in_tot = prelen;
771 out_tot = 0;
772
773 for (;;) {
774 check_siginfo();
775 if ((z.avail_in == 0 || needmore) && done_reading == 0) {
776 ssize_t in_size;
777
778 if (z.avail_in > 0) {
779 memmove(inbufp, z.next_in, z.avail_in);
780 }
781 z.next_in = (unsigned char *)inbufp;
782 in_size = read(in, z.next_in + z.avail_in,
783 BUFLEN - z.avail_in);
784
785 if (in_size == -1) {
786 maybe_warn("failed to read stdin");
787 goto stop_and_fail;
788 } else if (in_size == 0) {
789 done_reading = 1;
790 }
791 infile_newdata(in_size);
792
793 z.avail_in += in_size;
794 needmore = 0;
795
796 in_tot += in_size;
797 }
798 if (z.avail_in == 0) {
799 if (done_reading && state != GZSTATE_MAGIC0) {
800 maybe_warnx("%s: unexpected end of file",
801 filename);
802 goto stop_and_fail;
803 }
804 goto stop;
805 }
806 switch (state) {
807 case GZSTATE_MAGIC0:
808 if (*z.next_in != GZIP_MAGIC0) {
809 if (in_tot > 0) {
810 maybe_warnx("%s: trailing garbage "
811 "ignored", filename);
812 exit_value = 2;
813 goto stop;
814 }
815 maybe_warnx("input not gziped (MAGIC0)");
816 goto stop_and_fail;
817 }
818 ADVANCE();
819 state++;
820 out_sub_tot = 0;
821 crc = crc32(0L, Z_NULL, 0);
822 break;
823
824 case GZSTATE_MAGIC1:
825 if (*z.next_in != GZIP_MAGIC1 &&
826 *z.next_in != GZIP_OMAGIC1) {
827 maybe_warnx("input not gziped (MAGIC1)");
828 goto stop_and_fail;
829 }
830 ADVANCE();
831 state++;
832 break;
833
834 case GZSTATE_METHOD:
835 if (*z.next_in != Z_DEFLATED) {
836 maybe_warnx("unknown compression method");
837 goto stop_and_fail;
838 }
839 ADVANCE();
840 state++;
841 break;
842
843 case GZSTATE_FLAGS:
844 flags = *z.next_in;
845 ADVANCE();
846 skip_count = 6;
847 state++;
848 break;
849
850 case GZSTATE_SKIPPING:
851 if (skip_count > 0) {
852 skip_count--;
853 ADVANCE();
854 } else
855 state++;
856 break;
857
858 case GZSTATE_EXTRA:
859 if ((flags & EXTRA_FIELD) == 0) {
860 state = GZSTATE_ORIGNAME;
861 break;
862 }
863 skip_count = *z.next_in;
864 ADVANCE();
865 state++;
866 break;
867
868 case GZSTATE_EXTRA2:
869 skip_count |= ((*z.next_in) << 8);
870 ADVANCE();
871 state++;
872 break;
873
874 case GZSTATE_EXTRA3:
875 if (skip_count > 0) {
876 skip_count--;
877 ADVANCE();
878 } else
879 state++;
880 break;
881
882 case GZSTATE_ORIGNAME:
883 if ((flags & ORIG_NAME) == 0) {
884 state++;
885 break;
886 }
887 if (*z.next_in == 0)
888 state++;
889 ADVANCE();
890 break;
891
892 case GZSTATE_COMMENT:
893 if ((flags & COMMENT) == 0) {
894 state++;
895 break;
896 }
897 if (*z.next_in == 0)
898 state++;
899 ADVANCE();
900 break;
901
902 case GZSTATE_HEAD_CRC1:
903 if (flags & HEAD_CRC)
904 skip_count = 2;
905 else
906 skip_count = 0;
907 state++;
908 break;
909
910 case GZSTATE_HEAD_CRC2:
911 if (skip_count > 0) {
912 skip_count--;
913 ADVANCE();
914 } else
915 state++;
916 break;
917
918 case GZSTATE_INIT:
919 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
920 maybe_warnx("failed to inflateInit");
921 goto stop_and_fail;
922 }
923 state++;
924 break;
925
926 case GZSTATE_READ:
927 error = inflate(&z, Z_FINISH);
928 switch (error) {
929 /* Z_BUF_ERROR goes with Z_FINISH... */
930 case Z_BUF_ERROR:
931 if (z.avail_out > 0 && !done_reading)
932 continue;
933
934 case Z_STREAM_END:
935 case Z_OK:
936 break;
937
938 case Z_NEED_DICT:
939 maybe_warnx("Z_NEED_DICT error");
940 goto stop_and_fail;
941 case Z_DATA_ERROR:
942 maybe_warnx("data stream error");
943 goto stop_and_fail;
944 case Z_STREAM_ERROR:
945 maybe_warnx("internal stream error");
946 goto stop_and_fail;
947 case Z_MEM_ERROR:
948 maybe_warnx("memory allocation error");
949 goto stop_and_fail;
950
951 default:
952 maybe_warn("unknown error from inflate(): %d",
953 error);
954 }
955 wr = BUFLEN - z.avail_out;
956
957 if (wr != 0) {
958 crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
959 if (
960 /* don't write anything with -t */
961 tflag == 0 &&
962 write_retry(out, outbufp, wr) != wr) {
963 maybe_warn("error writing to output");
964 goto stop_and_fail;
965 }
966
967 out_tot += wr;
968 out_sub_tot += wr;
969 }
970
971 if (error == Z_STREAM_END) {
972 inflateEnd(&z);
973 state++;
974 }
975
976 z.next_out = (unsigned char *)outbufp;
977 z.avail_out = BUFLEN;
978
979 break;
980 case GZSTATE_CRC:
981 {
982 uLong origcrc;
983
984 if (z.avail_in < 4) {
985 if (!done_reading) {
986 needmore = 1;
987 continue;
988 }
989 maybe_warnx("truncated input");
990 goto stop_and_fail;
991 }
992 origcrc = le32dec(&z.next_in[0]);
993 if (origcrc != crc) {
994 maybe_warnx("invalid compressed"
995 " data--crc error");
996 goto stop_and_fail;
997 }
998 }
999
1000 z.avail_in -= 4;
1001 z.next_in += 4;
1002
1003 if (!z.avail_in && done_reading) {
1004 goto stop;
1005 }
1006 state++;
1007 break;
1008 case GZSTATE_LEN:
1009 {
1010 uLong origlen;
1011
1012 if (z.avail_in < 4) {
1013 if (!done_reading) {
1014 needmore = 1;
1015 continue;
1016 }
1017 maybe_warnx("truncated input");
1018 goto stop_and_fail;
1019 }
1020 origlen = le32dec(&z.next_in[0]);
1021
1022 if (origlen != out_sub_tot) {
1023 maybe_warnx("invalid compressed"
1024 " data--length error");
1025 goto stop_and_fail;
1026 }
1027 }
1028
1029 z.avail_in -= 4;
1030 z.next_in += 4;
1031
1032 if (error < 0) {
1033 maybe_warnx("decompression error");
1034 goto stop_and_fail;
1035 }
1036 state = GZSTATE_MAGIC0;
1037 break;
1038 }
1039 continue;
1040 stop_and_fail:
1041 out_tot = -1;
1042 stop:
1043 break;
1044 }
1045 if (state > GZSTATE_INIT)
1046 inflateEnd(&z);
1047
1048 free(inbufp);
1049 out1:
1050 free(outbufp);
1051 out2:
1052 if (gsizep)
1053 *gsizep = in_tot;
1054 return (out_tot);
1055 }
1056
1057 /*
1058 * set the owner, mode, flags & utimes using the given file descriptor.
1059 * file is only used in possible warning messages.
1060 */
1061 static void
copymodes(int fd,const struct stat * sbp,const char * file)1062 copymodes(int fd, const struct stat *sbp, const char *file)
1063 {
1064 struct timespec times[2];
1065 struct stat sb;
1066
1067 /*
1068 * If we have no info on the input, give this file some
1069 * default values and return..
1070 */
1071 if (sbp == NULL) {
1072 mode_t mask = umask(022);
1073
1074 (void)fchmod(fd, DEFFILEMODE & ~mask);
1075 (void)umask(mask);
1076 return;
1077 }
1078 sb = *sbp;
1079
1080 /* if the chown fails, remove set-id bits as-per compress(1) */
1081 if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1082 if (errno != EPERM)
1083 maybe_warn("couldn't fchown: %s", file);
1084 sb.st_mode &= ~(S_ISUID|S_ISGID);
1085 }
1086
1087 /* we only allow set-id and the 9 normal permission bits */
1088 sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1089 if (fchmod(fd, sb.st_mode) < 0)
1090 maybe_warn("couldn't fchmod: %s", file);
1091
1092 times[0] = sb.st_atim;
1093 times[1] = sb.st_mtim;
1094 if (futimens(fd, times) < 0)
1095 maybe_warn("couldn't futimens: %s", file);
1096
1097 /* only try flags if they exist already */
1098 if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1099 maybe_warn("couldn't fchflags: %s", file);
1100 }
1101
1102 /* what sort of file is this? */
1103 static enum filetype
file_gettype(u_char * buf)1104 file_gettype(u_char *buf)
1105 {
1106
1107 if (buf[0] == GZIP_MAGIC0 &&
1108 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1109 return FT_GZIP;
1110 #ifndef NO_BZIP2_SUPPORT
1111 else if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1112 buf[3] >= '0' && buf[3] <= '9')
1113 return FT_BZIP2;
1114 #endif
1115 #ifndef NO_COMPRESS_SUPPORT
1116 else if (memcmp(buf, Z_MAGIC, 2) == 0)
1117 return FT_Z;
1118 #endif
1119 #ifndef NO_PACK_SUPPORT
1120 else if (memcmp(buf, PACK_MAGIC, 2) == 0)
1121 return FT_PACK;
1122 #endif
1123 #ifndef NO_XZ_SUPPORT
1124 else if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */
1125 return FT_XZ;
1126 #endif
1127 #ifndef NO_LZ_SUPPORT
1128 else if (memcmp(buf, LZ_MAGIC, 4) == 0)
1129 return FT_LZ;
1130 #endif
1131 #ifndef NO_ZSTD_SUPPORT
1132 else if (memcmp(buf, ZSTD_MAGIC, 4) == 0)
1133 return FT_ZSTD;
1134 #endif
1135 else
1136 return FT_UNKNOWN;
1137 }
1138
1139 /* check the outfile is OK. */
1140 static int
check_outfile(const char * outfile)1141 check_outfile(const char *outfile)
1142 {
1143 struct stat sb;
1144 int ok = 1;
1145
1146 if (lflag == 0 && stat(outfile, &sb) == 0) {
1147 if (fflag)
1148 unlink(outfile);
1149 else if (isatty(STDIN_FILENO)) {
1150 char ans[10] = { 'n', '\0' }; /* default */
1151
1152 fprintf(stderr, "%s already exists -- do you wish to "
1153 "overwrite (y or n)? " , outfile);
1154 (void)fgets(ans, sizeof(ans) - 1, stdin);
1155 if (ans[0] != 'y' && ans[0] != 'Y') {
1156 fprintf(stderr, "\tnot overwriting\n");
1157 ok = 0;
1158 } else
1159 unlink(outfile);
1160 } else {
1161 maybe_warnx("%s already exists -- skipping", outfile);
1162 ok = 0;
1163 }
1164 }
1165 return ok;
1166 }
1167
1168 static void
unlink_input(const char * file,const struct stat * sb)1169 unlink_input(const char *file, const struct stat *sb)
1170 {
1171 struct stat nsb;
1172
1173 if (kflag)
1174 return;
1175 if (stat(file, &nsb) != 0)
1176 /* Must be gone already */
1177 return;
1178 if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1179 /* Definitely a different file */
1180 return;
1181 unlink(file);
1182 }
1183
1184 static void
got_sigint(int signo __unused)1185 got_sigint(int signo __unused)
1186 {
1187
1188 if (remove_file != NULL)
1189 unlink(remove_file);
1190 _exit(2);
1191 }
1192
1193 static void
got_siginfo(int signo __unused)1194 got_siginfo(int signo __unused)
1195 {
1196
1197 print_info = 1;
1198 }
1199
1200 static void
setup_signals(void)1201 setup_signals(void)
1202 {
1203
1204 signal(SIGINFO, got_siginfo);
1205 signal(SIGINT, got_sigint);
1206 }
1207
1208 static void
infile_newdata(size_t newdata)1209 infile_newdata(size_t newdata)
1210 {
1211
1212 infile_current += newdata;
1213 }
1214
1215 static void
infile_set(const char * newinfile,off_t total)1216 infile_set(const char *newinfile, off_t total)
1217 {
1218
1219 if (newinfile)
1220 infile = newinfile;
1221 infile_total = total;
1222 }
1223
1224 static void
infile_clear(void)1225 infile_clear(void)
1226 {
1227
1228 infile = NULL;
1229 infile_total = infile_current = 0;
1230 }
1231
1232 static const suffixes_t *
check_suffix(char * file,int xlate)1233 check_suffix(char *file, int xlate)
1234 {
1235 const suffixes_t *s;
1236 int len = strlen(file);
1237 char *sp;
1238
1239 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1240 /* if it doesn't fit in "a.suf", don't bother */
1241 if (s->ziplen >= len)
1242 continue;
1243 sp = file + len - s->ziplen;
1244 if (strcmp(s->zipped, sp) != 0)
1245 continue;
1246 if (xlate)
1247 strcpy(sp, s->normal);
1248 return s;
1249 }
1250 return NULL;
1251 }
1252
1253 /*
1254 * compress the given file: create a corresponding .gz file and remove the
1255 * original.
1256 */
1257 static off_t
file_compress(char * file,char * outfile,size_t outsize)1258 file_compress(char *file, char *outfile, size_t outsize)
1259 {
1260 int in;
1261 int out;
1262 off_t size, in_size;
1263 struct stat isb, osb;
1264 const suffixes_t *suff;
1265
1266 in = open(file, O_RDONLY);
1267 if (in == -1) {
1268 maybe_warn("can't open %s", file);
1269 return (-1);
1270 }
1271
1272 if (fstat(in, &isb) != 0) {
1273 maybe_warn("couldn't stat: %s", file);
1274 close(in);
1275 return (-1);
1276 }
1277
1278 if (fstat(in, &isb) != 0) {
1279 close(in);
1280 maybe_warn("can't stat %s", file);
1281 return -1;
1282 }
1283 infile_set(file, isb.st_size);
1284
1285 if (cflag == 0) {
1286 if (isb.st_nlink > 1 && fflag == 0) {
1287 maybe_warnx("%s has %ju other link%s -- "
1288 "skipping", file,
1289 (uintmax_t)isb.st_nlink - 1,
1290 isb.st_nlink == 1 ? "" : "s");
1291 close(in);
1292 return -1;
1293 }
1294
1295 if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1296 suff->zipped[0] != 0) {
1297 maybe_warnx("%s already has %s suffix -- unchanged",
1298 file, suff->zipped);
1299 close(in);
1300 return (-1);
1301 }
1302
1303 /* Add (usually) .gz to filename */
1304 if ((size_t)snprintf(outfile, outsize, "%s%s",
1305 file, suffixes[0].zipped) >= outsize)
1306 memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1307 suffixes[0].zipped, suffixes[0].ziplen + 1);
1308
1309 if (check_outfile(outfile) == 0) {
1310 close(in);
1311 return (-1);
1312 }
1313 }
1314
1315 if (cflag == 0) {
1316 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1317 if (out == -1) {
1318 maybe_warn("could not create output: %s", outfile);
1319 fclose(stdin);
1320 return (-1);
1321 }
1322 remove_file = outfile;
1323 } else
1324 out = STDOUT_FILENO;
1325
1326 in_size = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1327
1328 (void)close(in);
1329
1330 /*
1331 * If there was an error, in_size will be -1.
1332 * If we compressed to stdout, just return the size.
1333 * Otherwise stat the file and check it is the correct size.
1334 * We only blow away the file if we can stat the output and it
1335 * has the expected size.
1336 */
1337 if (cflag != 0)
1338 return in_size == -1 ? -1 : size;
1339
1340 if (fstat(out, &osb) != 0) {
1341 maybe_warn("couldn't stat: %s", outfile);
1342 goto bad_outfile;
1343 }
1344
1345 if (osb.st_size != size) {
1346 maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1347 outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1348 goto bad_outfile;
1349 }
1350
1351 copymodes(out, &isb, outfile);
1352 remove_file = NULL;
1353 if (close(out) == -1)
1354 maybe_warn("couldn't close output");
1355
1356 /* output is good, ok to delete input */
1357 unlink_input(file, &isb);
1358 return (size);
1359
1360 bad_outfile:
1361 if (close(out) == -1)
1362 maybe_warn("couldn't close output");
1363
1364 maybe_warnx("leaving original %s", file);
1365 unlink(outfile);
1366 return (size);
1367 }
1368
1369 /* uncompress the given file and remove the original */
1370 static off_t
file_uncompress(char * file,char * outfile,size_t outsize)1371 file_uncompress(char *file, char *outfile, size_t outsize)
1372 {
1373 struct stat isb, osb;
1374 off_t size;
1375 ssize_t rbytes;
1376 unsigned char fourbytes[4];
1377 enum filetype method;
1378 int fd, ofd, zfd = -1;
1379 int error;
1380 size_t in_size;
1381 ssize_t rv;
1382 time_t timestamp = 0;
1383 char name[PATH_MAX + 1];
1384
1385 /* gather the old name info */
1386
1387 fd = open(file, O_RDONLY);
1388 if (fd < 0) {
1389 maybe_warn("can't open %s", file);
1390 goto lose;
1391 }
1392 if (fstat(fd, &isb) != 0) {
1393 maybe_warn("can't stat %s", file);
1394 goto lose;
1395 }
1396 if (S_ISREG(isb.st_mode))
1397 in_size = isb.st_size;
1398 else
1399 in_size = 0;
1400 infile_set(file, in_size);
1401
1402 strlcpy(outfile, file, outsize);
1403 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1404 maybe_warnx("%s: unknown suffix -- ignored", file);
1405 goto lose;
1406 }
1407
1408 rbytes = read(fd, fourbytes, sizeof fourbytes);
1409 if (rbytes != sizeof fourbytes) {
1410 /* we don't want to fail here. */
1411 if (fflag)
1412 goto lose;
1413 if (rbytes == -1)
1414 maybe_warn("can't read %s", file);
1415 else
1416 goto unexpected_EOF;
1417 goto lose;
1418 }
1419 infile_newdata(rbytes);
1420
1421 method = file_gettype(fourbytes);
1422 if (fflag == 0 && method == FT_UNKNOWN) {
1423 maybe_warnx("%s: not in gzip format", file);
1424 goto lose;
1425 }
1426
1427
1428 if (method == FT_GZIP && Nflag) {
1429 unsigned char ts[4]; /* timestamp */
1430
1431 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1432 if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1433 goto unexpected_EOF;
1434 if (rv == -1) {
1435 if (!fflag)
1436 maybe_warn("can't read %s", file);
1437 goto lose;
1438 }
1439 infile_newdata(rv);
1440 timestamp = le32dec(&ts[0]);
1441
1442 if (fourbytes[3] & ORIG_NAME) {
1443 rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
1444 if (rbytes < 0) {
1445 maybe_warn("can't read %s", file);
1446 goto lose;
1447 }
1448 if (name[0] != '\0') {
1449 char *dp, *nf;
1450
1451 /* Make sure that name is NUL-terminated */
1452 name[rbytes] = '\0';
1453
1454 /* strip saved directory name */
1455 nf = strrchr(name, '/');
1456 if (nf == NULL)
1457 nf = name;
1458 else
1459 nf++;
1460
1461 /* preserve original directory name */
1462 dp = strrchr(file, '/');
1463 if (dp == NULL)
1464 dp = file;
1465 else
1466 dp++;
1467 snprintf(outfile, outsize, "%.*s%.*s",
1468 (int) (dp - file),
1469 file, (int) rbytes, nf);
1470 }
1471 }
1472 }
1473 lseek(fd, 0, SEEK_SET);
1474
1475 if (cflag == 0 || lflag) {
1476 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1477 maybe_warnx("%s has %ju other links -- skipping",
1478 file, (uintmax_t)isb.st_nlink - 1);
1479 goto lose;
1480 }
1481 if (nflag == 0 && timestamp)
1482 isb.st_mtime = timestamp;
1483 if (check_outfile(outfile) == 0)
1484 goto lose;
1485 }
1486
1487 if (cflag)
1488 zfd = STDOUT_FILENO;
1489 else if (lflag)
1490 zfd = -1;
1491 else {
1492 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1493 if (zfd == STDOUT_FILENO) {
1494 /* We won't close STDOUT_FILENO later... */
1495 zfd = dup(zfd);
1496 close(STDOUT_FILENO);
1497 }
1498 if (zfd == -1) {
1499 maybe_warn("can't open %s", outfile);
1500 goto lose;
1501 }
1502 remove_file = outfile;
1503 }
1504
1505 switch (method) {
1506 #ifndef NO_BZIP2_SUPPORT
1507 case FT_BZIP2:
1508 /* XXX */
1509 if (lflag) {
1510 maybe_warnx("no -l with bzip2 files");
1511 goto lose;
1512 }
1513
1514 size = unbzip2(fd, zfd, NULL, 0, NULL);
1515 break;
1516 #endif
1517
1518 #ifndef NO_COMPRESS_SUPPORT
1519 case FT_Z: {
1520 FILE *in, *out;
1521
1522 /* XXX */
1523 if (lflag) {
1524 maybe_warnx("no -l with Lempel-Ziv files");
1525 goto lose;
1526 }
1527
1528 if ((in = zdopen(fd)) == NULL) {
1529 maybe_warn("zdopen for read: %s", file);
1530 goto lose;
1531 }
1532
1533 out = fdopen(dup(zfd), "w");
1534 if (out == NULL) {
1535 maybe_warn("fdopen for write: %s", outfile);
1536 fclose(in);
1537 goto lose;
1538 }
1539
1540 size = zuncompress(in, out, NULL, 0, NULL);
1541 /* need to fclose() if ferror() is true... */
1542 error = ferror(in);
1543 if (error | fclose(in)) {
1544 if (error)
1545 maybe_warn("failed infile");
1546 else
1547 maybe_warn("failed infile fclose");
1548 if (cflag == 0)
1549 unlink(outfile);
1550 (void)fclose(out);
1551 goto lose;
1552 }
1553 if (fclose(out) != 0) {
1554 maybe_warn("failed outfile fclose");
1555 if (cflag == 0)
1556 unlink(outfile);
1557 goto lose;
1558 }
1559 break;
1560 }
1561 #endif
1562
1563 #ifndef NO_PACK_SUPPORT
1564 case FT_PACK:
1565 if (lflag) {
1566 maybe_warnx("no -l with packed files");
1567 goto lose;
1568 }
1569
1570 size = unpack(fd, zfd, NULL, 0, NULL);
1571 break;
1572 #endif
1573
1574 #ifndef NO_XZ_SUPPORT
1575 case FT_XZ:
1576 if (lflag) {
1577 size = unxz_len(fd);
1578 if (!tflag) {
1579 print_list_out(in_size, size, file);
1580 close(fd);
1581 return -1;
1582 }
1583 } else
1584 size = unxz(fd, zfd, NULL, 0, NULL);
1585 break;
1586 #endif
1587
1588 #ifndef NO_LZ_SUPPORT
1589 case FT_LZ:
1590 if (lflag) {
1591 maybe_warnx("no -l with lzip files");
1592 goto lose;
1593 }
1594 size = unlz(fd, zfd, NULL, 0, NULL);
1595 break;
1596 #endif
1597
1598 #ifndef NO_ZSTD_SUPPORT
1599 case FT_ZSTD:
1600 if (lflag) {
1601 maybe_warnx("no -l with zstd files");
1602 goto lose;
1603 }
1604 size = unzstd(fd, zfd, NULL, 0, NULL);
1605 break;
1606 #endif
1607 case FT_UNKNOWN:
1608 if (lflag) {
1609 maybe_warnx("no -l for unknown filetypes");
1610 goto lose;
1611 }
1612 size = cat_fd(NULL, 0, NULL, fd);
1613 break;
1614 default:
1615 if (lflag) {
1616 print_list(fd, in_size, outfile, isb.st_mtime);
1617 if (!tflag) {
1618 close(fd);
1619 return -1; /* XXX */
1620 }
1621 }
1622
1623 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1624 break;
1625 }
1626
1627 if (close(fd) != 0)
1628 maybe_warn("couldn't close input");
1629 if (zfd != STDOUT_FILENO && close(zfd) != 0)
1630 maybe_warn("couldn't close output");
1631
1632 if (size == -1) {
1633 if (cflag == 0)
1634 unlink(outfile);
1635 maybe_warnx("%s: uncompress failed", file);
1636 return -1;
1637 }
1638
1639 /* if testing, or we uncompressed to stdout, this is all we need */
1640 if (tflag)
1641 return size;
1642 /* if we are uncompressing to stdin, don't remove the file. */
1643 if (cflag)
1644 return size;
1645
1646 /*
1647 * if we create a file...
1648 */
1649 /*
1650 * if we can't stat the file don't remove the file.
1651 */
1652
1653 ofd = open(outfile, O_RDWR, 0);
1654 if (ofd == -1) {
1655 maybe_warn("couldn't open (leaving original): %s",
1656 outfile);
1657 return -1;
1658 }
1659 if (fstat(ofd, &osb) != 0) {
1660 maybe_warn("couldn't stat (leaving original): %s",
1661 outfile);
1662 close(ofd);
1663 return -1;
1664 }
1665 if (osb.st_size != size) {
1666 maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1667 (uintmax_t)size, (uintmax_t)osb.st_size);
1668 close(ofd);
1669 unlink(outfile);
1670 return -1;
1671 }
1672 copymodes(ofd, &isb, outfile);
1673 remove_file = NULL;
1674 close(ofd);
1675 unlink_input(file, &isb);
1676 return size;
1677
1678 unexpected_EOF:
1679 maybe_warnx("%s: unexpected end of file", file);
1680 lose:
1681 if (fd != -1)
1682 close(fd);
1683 if (zfd != -1 && zfd != STDOUT_FILENO)
1684 close(zfd);
1685 return -1;
1686 }
1687
1688 static void
check_siginfo(void)1689 check_siginfo(void)
1690 {
1691 if (print_info == 0)
1692 return;
1693 if (infile) {
1694 if (infile_total) {
1695 int pcent = (int)((100.0 * infile_current) / infile_total);
1696
1697 fprintf(stderr, "%s: done %llu/%llu bytes %d%%\n",
1698 infile, (unsigned long long)infile_current,
1699 (unsigned long long)infile_total, pcent);
1700 } else
1701 fprintf(stderr, "%s: done %llu bytes\n",
1702 infile, (unsigned long long)infile_current);
1703 }
1704 print_info = 0;
1705 }
1706
1707 static off_t
cat_fd(unsigned char * prepend,size_t count,off_t * gsizep,int fd)1708 cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1709 {
1710 char buf[BUFLEN];
1711 off_t in_tot;
1712 ssize_t w;
1713
1714 in_tot = count;
1715 w = write_retry(STDOUT_FILENO, prepend, count);
1716 if (w == -1 || (size_t)w != count) {
1717 maybe_warn("write to stdout");
1718 return -1;
1719 }
1720 for (;;) {
1721 ssize_t rv;
1722
1723 rv = read(fd, buf, sizeof buf);
1724 if (rv == 0)
1725 break;
1726 if (rv < 0) {
1727 maybe_warn("read from fd %d", fd);
1728 break;
1729 }
1730 infile_newdata(rv);
1731
1732 if (write_retry(STDOUT_FILENO, buf, rv) != rv) {
1733 maybe_warn("write to stdout");
1734 break;
1735 }
1736 in_tot += rv;
1737 }
1738
1739 if (gsizep)
1740 *gsizep = in_tot;
1741 return (in_tot);
1742 }
1743
1744 static void
handle_stdin(void)1745 handle_stdin(void)
1746 {
1747 struct stat isb;
1748 unsigned char fourbytes[4];
1749 size_t in_size;
1750 off_t usize, gsize;
1751 enum filetype method;
1752 ssize_t bytes_read;
1753 #ifndef NO_COMPRESS_SUPPORT
1754 FILE *in;
1755 #endif
1756
1757 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1758 maybe_warnx("standard input is a terminal -- ignoring");
1759 goto out;
1760 }
1761
1762 if (fstat(STDIN_FILENO, &isb) < 0) {
1763 maybe_warn("fstat");
1764 goto out;
1765 }
1766 if (S_ISREG(isb.st_mode))
1767 in_size = isb.st_size;
1768 else
1769 in_size = 0;
1770 infile_set("(stdin)", in_size);
1771
1772 if (lflag) {
1773 print_list(STDIN_FILENO, in_size, infile, isb.st_mtime);
1774 goto out;
1775 }
1776
1777 bytes_read = read_retry(STDIN_FILENO, fourbytes, sizeof fourbytes);
1778 if (bytes_read == -1) {
1779 maybe_warn("can't read stdin");
1780 goto out;
1781 } else if (bytes_read != sizeof(fourbytes)) {
1782 maybe_warnx("(stdin): unexpected end of file");
1783 goto out;
1784 }
1785
1786 method = file_gettype(fourbytes);
1787 switch (method) {
1788 default:
1789 if (fflag == 0) {
1790 maybe_warnx("unknown compression format");
1791 goto out;
1792 }
1793 usize = cat_fd(fourbytes, sizeof fourbytes, &gsize, STDIN_FILENO);
1794 break;
1795 case FT_GZIP:
1796 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1797 (char *)fourbytes, sizeof fourbytes, &gsize, "(stdin)");
1798 break;
1799 #ifndef NO_BZIP2_SUPPORT
1800 case FT_BZIP2:
1801 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1802 (char *)fourbytes, sizeof fourbytes, &gsize);
1803 break;
1804 #endif
1805 #ifndef NO_COMPRESS_SUPPORT
1806 case FT_Z:
1807 if ((in = zdopen(STDIN_FILENO)) == NULL) {
1808 maybe_warnx("zopen of stdin");
1809 goto out;
1810 }
1811
1812 usize = zuncompress(in, stdout, (char *)fourbytes,
1813 sizeof fourbytes, &gsize);
1814 fclose(in);
1815 break;
1816 #endif
1817 #ifndef NO_PACK_SUPPORT
1818 case FT_PACK:
1819 usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1820 (char *)fourbytes, sizeof fourbytes, &gsize);
1821 break;
1822 #endif
1823 #ifndef NO_XZ_SUPPORT
1824 case FT_XZ:
1825 usize = unxz(STDIN_FILENO, STDOUT_FILENO,
1826 (char *)fourbytes, sizeof fourbytes, &gsize);
1827 break;
1828 #endif
1829 #ifndef NO_LZ_SUPPORT
1830 case FT_LZ:
1831 usize = unlz(STDIN_FILENO, STDOUT_FILENO,
1832 (char *)fourbytes, sizeof fourbytes, &gsize);
1833 break;
1834 #endif
1835 #ifndef NO_ZSTD_SUPPORT
1836 case FT_ZSTD:
1837 usize = unzstd(STDIN_FILENO, STDOUT_FILENO,
1838 (char *)fourbytes, sizeof fourbytes, &gsize);
1839 break;
1840 #endif
1841 }
1842
1843 if (vflag && !tflag && usize != -1 && gsize != -1)
1844 print_verbage(NULL, NULL, usize, gsize);
1845 if (vflag && tflag)
1846 print_test("(stdin)", usize != -1);
1847
1848 out:
1849 infile_clear();
1850 }
1851
1852 static void
handle_stdout(void)1853 handle_stdout(void)
1854 {
1855 off_t gsize;
1856 off_t usize;
1857 struct stat sb;
1858 time_t systime;
1859 uint32_t mtime;
1860 int ret;
1861
1862 infile_set("(stdout)", 0);
1863
1864 if (fflag == 0 && isatty(STDOUT_FILENO)) {
1865 maybe_warnx("standard output is a terminal -- ignoring");
1866 return;
1867 }
1868
1869 /* If stdin is a file use its mtime, otherwise use current time */
1870 ret = fstat(STDIN_FILENO, &sb);
1871 if (ret < 0) {
1872 maybe_warn("Can't stat stdin");
1873 return;
1874 }
1875
1876 if (S_ISREG(sb.st_mode)) {
1877 infile_set("(stdout)", sb.st_size);
1878 mtime = (uint32_t)sb.st_mtime;
1879 } else {
1880 systime = time(NULL);
1881 if (systime == -1) {
1882 maybe_warn("time");
1883 return;
1884 }
1885 mtime = (uint32_t)systime;
1886 }
1887
1888 usize =
1889 gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1890 if (vflag && !tflag && usize != -1 && gsize != -1)
1891 print_verbage(NULL, NULL, usize, gsize);
1892 }
1893
1894 /* do what is asked for, for the path name */
1895 static void
handle_pathname(char * path)1896 handle_pathname(char *path)
1897 {
1898 char *opath = path, *s = NULL;
1899 ssize_t len;
1900 int slen;
1901 struct stat sb;
1902
1903 /* check for stdout/stdin */
1904 if (path[0] == '-' && path[1] == '\0') {
1905 if (dflag)
1906 handle_stdin();
1907 else
1908 handle_stdout();
1909 return;
1910 }
1911
1912 retry:
1913 if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1914 lstat(path, &sb) != 0)) {
1915 /* lets try <path>.gz if we're decompressing */
1916 if (dflag && s == NULL && errno == ENOENT) {
1917 len = strlen(path);
1918 slen = suffixes[0].ziplen;
1919 s = malloc(len + slen + 1);
1920 if (s == NULL)
1921 maybe_err("malloc");
1922 memcpy(s, path, len);
1923 memcpy(s + len, suffixes[0].zipped, slen + 1);
1924 path = s;
1925 goto retry;
1926 }
1927 maybe_warn("can't stat: %s", opath);
1928 goto out;
1929 }
1930
1931 if (S_ISDIR(sb.st_mode)) {
1932 if (rflag)
1933 handle_dir(path);
1934 else
1935 maybe_warnx("%s is a directory", path);
1936 goto out;
1937 }
1938
1939 if (S_ISREG(sb.st_mode))
1940 handle_file(path, &sb);
1941 else
1942 maybe_warnx("%s is not a regular file", path);
1943
1944 out:
1945 if (s)
1946 free(s);
1947 }
1948
1949 /* compress/decompress a file */
1950 static void
handle_file(char * file,struct stat * sbp)1951 handle_file(char *file, struct stat *sbp)
1952 {
1953 off_t usize, gsize;
1954 char outfile[PATH_MAX];
1955
1956 infile_set(file, sbp->st_size);
1957 if (dflag) {
1958 usize = file_uncompress(file, outfile, sizeof(outfile));
1959 if (vflag && tflag)
1960 print_test(file, usize != -1);
1961 if (usize == -1)
1962 return;
1963 gsize = sbp->st_size;
1964 } else {
1965 gsize = file_compress(file, outfile, sizeof(outfile));
1966 if (gsize == -1)
1967 return;
1968 usize = sbp->st_size;
1969 }
1970 infile_clear();
1971
1972 if (vflag && !tflag)
1973 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1974 }
1975
1976 /* this is used with -r to recursively descend directories */
1977 static void
handle_dir(char * dir)1978 handle_dir(char *dir)
1979 {
1980 char *path_argv[2];
1981 FTS *fts;
1982 FTSENT *entry;
1983
1984 path_argv[0] = dir;
1985 path_argv[1] = 0;
1986 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1987 if (fts == NULL) {
1988 warn("couldn't fts_open %s", dir);
1989 return;
1990 }
1991
1992 while (errno = 0, (entry = fts_read(fts))) {
1993 switch(entry->fts_info) {
1994 case FTS_D:
1995 case FTS_DP:
1996 continue;
1997
1998 case FTS_DNR:
1999 case FTS_ERR:
2000 case FTS_NS:
2001 maybe_warn("%s", entry->fts_path);
2002 continue;
2003 case FTS_F:
2004 handle_file(entry->fts_path, entry->fts_statp);
2005 }
2006 }
2007 if (errno != 0)
2008 warn("error with fts_read %s", dir);
2009 (void)fts_close(fts);
2010 }
2011
2012 /* print a ratio - size reduction as a fraction of uncompressed size */
2013 static void
print_ratio(off_t in,off_t out,FILE * where)2014 print_ratio(off_t in, off_t out, FILE *where)
2015 {
2016 int percent10; /* 10 * percent */
2017 off_t diff;
2018 char buff[8];
2019 int len;
2020
2021 diff = in - out/2;
2022 if (in == 0 && out == 0)
2023 percent10 = 0;
2024 else if (diff < 0)
2025 /*
2026 * Output is more than double size of input! print -99.9%
2027 * Quite possibly we've failed to get the original size.
2028 */
2029 percent10 = -999;
2030 else {
2031 /*
2032 * We only need 12 bits of result from the final division,
2033 * so reduce the values until a 32bit division will suffice.
2034 */
2035 while (in > 0x100000) {
2036 diff >>= 1;
2037 in >>= 1;
2038 }
2039 if (in != 0)
2040 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
2041 else
2042 percent10 = 0;
2043 }
2044
2045 len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
2046 /* Move the '.' to before the last digit */
2047 buff[len - 1] = buff[len - 2];
2048 buff[len - 2] = '.';
2049 fprintf(where, "%5s%%", buff);
2050 }
2051
2052 /* print compression statistics, and the new name (if there is one!) */
2053 static void
print_verbage(const char * file,const char * nfile,off_t usize,off_t gsize)2054 print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
2055 {
2056 if (file)
2057 fprintf(stderr, "%s:%s ", file,
2058 strlen(file) < 7 ? "\t\t" : "\t");
2059 print_ratio(usize, gsize, stderr);
2060 if (nfile)
2061 fprintf(stderr, " -- replaced with %s", nfile);
2062 fprintf(stderr, "\n");
2063 fflush(stderr);
2064 }
2065
2066 /* print test results */
2067 static void
print_test(const char * file,int ok)2068 print_test(const char *file, int ok)
2069 {
2070
2071 if (exit_value == 0 && ok == 0)
2072 exit_value = 1;
2073 fprintf(stderr, "%s:%s %s\n", file,
2074 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
2075 fflush(stderr);
2076 }
2077
2078 /* print a file's info ala --list */
2079 /* eg:
2080 compressed uncompressed ratio uncompressed_name
2081 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
2082 */
2083 static void
print_list(int fd,off_t out,const char * outfile,time_t ts)2084 print_list(int fd, off_t out, const char *outfile, time_t ts)
2085 {
2086 static int first = 1;
2087 static off_t in_tot, out_tot;
2088 uint32_t crc = 0;
2089 off_t in = 0, rv;
2090
2091 if (first) {
2092 if (vflag)
2093 printf("method crc date time ");
2094 if (qflag == 0)
2095 printf(" compressed uncompressed "
2096 "ratio uncompressed_name\n");
2097 }
2098 first = 0;
2099
2100 /* print totals? */
2101 if (fd == -1) {
2102 in = in_tot;
2103 out = out_tot;
2104 } else
2105 {
2106 /* read the last 4 bytes - this is the uncompressed size */
2107 rv = lseek(fd, (off_t)(-8), SEEK_END);
2108 if (rv != -1) {
2109 unsigned char buf[8];
2110 uint32_t usize;
2111
2112 rv = read(fd, (char *)buf, sizeof(buf));
2113 if (rv == -1)
2114 maybe_warn("read of uncompressed size");
2115 else if (rv != sizeof(buf))
2116 maybe_warnx("read of uncompressed size");
2117
2118 else {
2119 usize = le32dec(&buf[4]);
2120 in = (off_t)usize;
2121 crc = le32dec(&buf[0]);
2122 }
2123 }
2124 }
2125
2126 if (vflag && fd == -1)
2127 printf(" ");
2128 else if (vflag) {
2129 char *date = ctime(&ts);
2130
2131 /* skip the day, 1/100th second, and year */
2132 date += 4;
2133 date[12] = 0;
2134 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2135 }
2136 in_tot += in;
2137 out_tot += out;
2138 print_list_out(out, in, outfile);
2139 }
2140
2141 static void
print_list_out(off_t out,off_t in,const char * outfile)2142 print_list_out(off_t out, off_t in, const char *outfile)
2143 {
2144 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2145 print_ratio(in, out, stdout);
2146 printf(" %s\n", outfile);
2147 }
2148
2149 /* display the usage of NetBSD gzip */
2150 static void
usage(void)2151 usage(void)
2152 {
2153
2154 fprintf(stderr, "%s\n", gzip_version);
2155 fprintf(stderr,
2156 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2157 " -1 --fast fastest (worst) compression\n"
2158 " -2 .. -8 set compression level\n"
2159 " -9 --best best (slowest) compression\n"
2160 " -c --stdout write to stdout, keep original files\n"
2161 " --to-stdout\n"
2162 " -d --decompress uncompress files\n"
2163 " --uncompress\n"
2164 " -f --force force overwriting & compress links\n"
2165 " -h --help display this help\n"
2166 " -k --keep don't delete input files during operation\n"
2167 " -l --list list compressed file contents\n"
2168 " -N --name save or restore original file name and time stamp\n"
2169 " -n --no-name don't save original file name or time stamp\n"
2170 " -q --quiet output no warnings\n"
2171 " -r --recursive recursively compress files in directories\n"
2172 " -S .suf use suffix .suf instead of .gz\n"
2173 " --suffix .suf\n"
2174 " -t --test test compressed file\n"
2175 " -V --version display program version\n"
2176 " -v --verbose print extra statistics\n",
2177 getprogname());
2178 exit(0);
2179 }
2180
2181 /* display the license information of FreeBSD gzip */
2182 static void
display_license(void)2183 display_license(void)
2184 {
2185
2186 fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version);
2187 fprintf(stderr, "%s\n", gzip_copyright);
2188 exit(0);
2189 }
2190
2191 /* display the version of NetBSD gzip */
2192 static void
display_version(void)2193 display_version(void)
2194 {
2195
2196 fprintf(stderr, "%s\n", gzip_version);
2197 exit(0);
2198 }
2199
2200 #ifndef NO_BZIP2_SUPPORT
2201 #include "unbzip2.c"
2202 #endif
2203 #ifndef NO_COMPRESS_SUPPORT
2204 #include "zuncompress.c"
2205 #endif
2206 #ifndef NO_PACK_SUPPORT
2207 #include "unpack.c"
2208 #endif
2209 #ifndef NO_XZ_SUPPORT
2210 #include "unxz.c"
2211 #endif
2212 #ifndef NO_LZ_SUPPORT
2213 #include "unlz.c"
2214 #endif
2215 #ifndef NO_ZSTD_SUPPORT
2216 #include "unzstd.c"
2217 #endif
2218
2219 static ssize_t
read_retry(int fd,void * buf,size_t sz)2220 read_retry(int fd, void *buf, size_t sz)
2221 {
2222 char *cp = buf;
2223 size_t left = MIN(sz, (size_t) SSIZE_MAX);
2224
2225 while (left > 0) {
2226 ssize_t ret;
2227
2228 ret = read(fd, cp, left);
2229 if (ret == -1) {
2230 return ret;
2231 } else if (ret == 0) {
2232 break; /* EOF */
2233 }
2234 cp += ret;
2235 left -= ret;
2236 }
2237
2238 return sz - left;
2239 }
2240
2241 static ssize_t
write_retry(int fd,const void * buf,size_t sz)2242 write_retry(int fd, const void *buf, size_t sz)
2243 {
2244 const char *cp = buf;
2245 size_t left = MIN(sz, (size_t) SSIZE_MAX);
2246
2247 while (left > 0) {
2248 ssize_t ret;
2249
2250 ret = write(fd, cp, left);
2251 if (ret == -1) {
2252 return ret;
2253 } else if (ret == 0) {
2254 abort(); /* Can't happen */
2255 }
2256 cp += ret;
2257 left -= ret;
2258 }
2259
2260 return sz - left;
2261 }
2262