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