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$"); 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 fourbytes[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 maybe_warn("can't stat %s", file); 1466 goto lose; 1467 } 1468 if (S_ISREG(isb.st_mode)) 1469 in_size = isb.st_size; 1470 else 1471 in_size = 0; 1472 infile_set(file, in_size); 1473 1474 strlcpy(outfile, file, outsize); 1475 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { 1476 maybe_warnx("%s: unknown suffix -- ignored", file); 1477 goto lose; 1478 } 1479 1480 rbytes = read(fd, fourbytes, sizeof fourbytes); 1481 if (rbytes != sizeof fourbytes) { 1482 /* we don't want to fail here. */ 1483#ifndef SMALL 1484 if (fflag) 1485 goto lose; 1486#endif 1487 if (rbytes == -1) 1488 maybe_warn("can't read %s", file); 1489 else 1490 goto unexpected_EOF; 1491 goto lose; 1492 } 1493 infile_newdata(rbytes); 1494 1495 method = file_gettype(fourbytes); 1496#ifndef SMALL 1497 if (fflag == 0 && method == FT_UNKNOWN) { 1498 maybe_warnx("%s: not in gzip format", file); 1499 goto lose; 1500 } 1501 1502#endif 1503 1504#ifndef SMALL 1505 if (method == FT_GZIP && Nflag) { 1506 unsigned char ts[4]; /* timestamp */ 1507 1508 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); 1509 if (rv >= 0 && rv < (ssize_t)(sizeof ts)) 1510 goto unexpected_EOF; 1511 if (rv == -1) { 1512 if (!fflag) 1513 maybe_warn("can't read %s", file); 1514 goto lose; 1515 } 1516 infile_newdata(rv); 1517 timestamp = le32dec(&ts[0]); 1518 1519 if (fourbytes[3] & ORIG_NAME) { 1520 rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME); 1521 if (rbytes < 0) { 1522 maybe_warn("can't read %s", file); 1523 goto lose; 1524 } 1525 if (name[0] != '\0') { 1526 char *dp, *nf; 1527 1528 /* Make sure that name is NUL-terminated */ 1529 name[rbytes] = '\0'; 1530 1531 /* strip saved directory name */ 1532 nf = strrchr(name, '/'); 1533 if (nf == NULL) 1534 nf = name; 1535 else 1536 nf++; 1537 1538 /* preserve original directory name */ 1539 dp = strrchr(file, '/'); 1540 if (dp == NULL) 1541 dp = file; 1542 else 1543 dp++; 1544 snprintf(outfile, outsize, "%.*s%.*s", 1545 (int) (dp - file), 1546 file, (int) rbytes, nf); 1547 } 1548 } 1549 } 1550#endif 1551 lseek(fd, 0, SEEK_SET); 1552 1553 if (cflag == 0 || lflag) { 1554#ifndef SMALL 1555 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { 1556 maybe_warnx("%s has %ju other links -- skipping", 1557 file, (uintmax_t)isb.st_nlink - 1); 1558 goto lose; 1559 } 1560 if (nflag == 0 && timestamp) 1561 isb.st_mtime = timestamp; 1562 if (check_outfile(outfile) == 0) 1563 goto lose; 1564#endif 1565 } 1566 1567 if (cflag) 1568 zfd = STDOUT_FILENO; 1569 else if (lflag) 1570 zfd = -1; 1571 else { 1572 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 1573 if (zfd == STDOUT_FILENO) { 1574 /* We won't close STDOUT_FILENO later... */ 1575 zfd = dup(zfd); 1576 close(STDOUT_FILENO); 1577 } 1578 if (zfd == -1) { 1579 maybe_warn("can't open %s", outfile); 1580 goto lose; 1581 } 1582 remove_file = outfile; 1583 } 1584 1585 switch (method) { 1586#ifndef NO_BZIP2_SUPPORT 1587 case FT_BZIP2: 1588 /* XXX */ 1589 if (lflag) { 1590 maybe_warnx("no -l with bzip2 files"); 1591 goto lose; 1592 } 1593 1594 size = unbzip2(fd, zfd, NULL, 0, NULL); 1595 break; 1596#endif 1597 1598#ifndef NO_COMPRESS_SUPPORT 1599 case FT_Z: { 1600 FILE *in, *out; 1601 1602 /* XXX */ 1603 if (lflag) { 1604 maybe_warnx("no -l with Lempel-Ziv files"); 1605 goto lose; 1606 } 1607 1608 if ((in = zdopen(fd)) == NULL) { 1609 maybe_warn("zdopen for read: %s", file); 1610 goto lose; 1611 } 1612 1613 out = fdopen(dup(zfd), "w"); 1614 if (out == NULL) { 1615 maybe_warn("fdopen for write: %s", outfile); 1616 fclose(in); 1617 goto lose; 1618 } 1619 1620 size = zuncompress(in, out, NULL, 0, NULL); 1621 /* need to fclose() if ferror() is true... */ 1622 error = ferror(in); 1623 if (error | fclose(in)) { 1624 if (error) 1625 maybe_warn("failed infile"); 1626 else 1627 maybe_warn("failed infile fclose"); 1628 if (cflag == 0) 1629 unlink(outfile); 1630 (void)fclose(out); 1631 goto lose; 1632 } 1633 if (fclose(out) != 0) { 1634 maybe_warn("failed outfile fclose"); 1635 if (cflag == 0) 1636 unlink(outfile); 1637 goto lose; 1638 } 1639 break; 1640 } 1641#endif 1642 1643#ifndef NO_PACK_SUPPORT 1644 case FT_PACK: 1645 if (lflag) { 1646 maybe_warnx("no -l with packed files"); 1647 goto lose; 1648 } 1649 1650 size = unpack(fd, zfd, NULL, 0, NULL); 1651 break; 1652#endif 1653 1654#ifndef NO_XZ_SUPPORT 1655 case FT_XZ: 1656 if (lflag) { 1657 size = unxz_len(fd); 1658 print_list_out(in_size, size, file); 1659 return -1; 1660 } 1661 size = unxz(fd, zfd, NULL, 0, NULL); 1662 break; 1663#endif 1664 1665#ifndef NO_LZ_SUPPORT 1666 case FT_LZ: 1667 if (lflag) { 1668 maybe_warnx("no -l with lzip files"); 1669 goto lose; 1670 } 1671 size = unlz(fd, zfd, NULL, 0, NULL); 1672 break; 1673#endif 1674#ifndef SMALL 1675 case FT_UNKNOWN: 1676 if (lflag) { 1677 maybe_warnx("no -l for unknown filetypes"); 1678 goto lose; 1679 } 1680 size = cat_fd(NULL, 0, NULL, fd); 1681 break; 1682#endif 1683 default: 1684 if (lflag) { 1685 print_list(fd, in_size, outfile, isb.st_mtime); 1686 close(fd); 1687 return -1; /* XXX */ 1688 } 1689 1690 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); 1691 break; 1692 } 1693 1694 if (close(fd) != 0) 1695 maybe_warn("couldn't close input"); 1696 if (zfd != STDOUT_FILENO && close(zfd) != 0) 1697 maybe_warn("couldn't close output"); 1698 1699 if (size == -1) { 1700 if (cflag == 0) 1701 unlink(outfile); 1702 maybe_warnx("%s: uncompress failed", file); 1703 return -1; 1704 } 1705 1706 /* if testing, or we uncompressed to stdout, this is all we need */ 1707#ifndef SMALL 1708 if (tflag) 1709 return size; 1710#endif 1711 /* if we are uncompressing to stdin, don't remove the file. */ 1712 if (cflag) 1713 return size; 1714 1715 /* 1716 * if we create a file... 1717 */ 1718 /* 1719 * if we can't stat the file don't remove the file. 1720 */ 1721 1722 ofd = open(outfile, O_RDWR, 0); 1723 if (ofd == -1) { 1724 maybe_warn("couldn't open (leaving original): %s", 1725 outfile); 1726 return -1; 1727 } 1728 if (fstat(ofd, &osb) != 0) { 1729 maybe_warn("couldn't stat (leaving original): %s", 1730 outfile); 1731 close(ofd); 1732 return -1; 1733 } 1734 if (osb.st_size != size) { 1735 maybe_warnx("stat gave different size: %ju != %ju (leaving original)", 1736 (uintmax_t)size, (uintmax_t)osb.st_size); 1737 close(ofd); 1738 unlink(outfile); 1739 return -1; 1740 } 1741#ifndef SMALL 1742 copymodes(ofd, &isb, outfile); 1743 remove_file = NULL; 1744#endif 1745 close(ofd); 1746 unlink_input(file, &isb); 1747 return size; 1748 1749 unexpected_EOF: 1750 maybe_warnx("%s: unexpected end of file", file); 1751 lose: 1752 if (fd != -1) 1753 close(fd); 1754 if (zfd != -1 && zfd != STDOUT_FILENO) 1755 close(zfd); 1756 return -1; 1757} 1758 1759#ifndef SMALL 1760static void 1761check_siginfo(void) 1762{ 1763 if (print_info == 0) 1764 return; 1765 if (infile) { 1766 if (infile_total) { 1767 int pcent = (int)((100.0 * infile_current) / infile_total); 1768 1769 fprintf(stderr, "%s: done %llu/%llu bytes %d%%\n", 1770 infile, (unsigned long long)infile_current, 1771 (unsigned long long)infile_total, pcent); 1772 } else 1773 fprintf(stderr, "%s: done %llu bytes\n", 1774 infile, (unsigned long long)infile_current); 1775 } 1776 print_info = 0; 1777} 1778 1779static off_t 1780cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) 1781{ 1782 char buf[BUFLEN]; 1783 off_t in_tot; 1784 ssize_t w; 1785 1786 in_tot = count; 1787 w = write_retry(STDOUT_FILENO, prepend, count); 1788 if (w == -1 || (size_t)w != count) { 1789 maybe_warn("write to stdout"); 1790 return -1; 1791 } 1792 for (;;) { 1793 ssize_t rv; 1794 1795 rv = read(fd, buf, sizeof buf); 1796 if (rv == 0) 1797 break; 1798 if (rv < 0) { 1799 maybe_warn("read from fd %d", fd); 1800 break; 1801 } 1802 infile_newdata(rv); 1803 1804 if (write_retry(STDOUT_FILENO, buf, rv) != rv) { 1805 maybe_warn("write to stdout"); 1806 break; 1807 } 1808 in_tot += rv; 1809 } 1810 1811 if (gsizep) 1812 *gsizep = in_tot; 1813 return (in_tot); 1814} 1815#endif 1816 1817static void 1818handle_stdin(void) 1819{ 1820 struct stat isb; 1821 unsigned char fourbytes[4]; 1822 size_t in_size; 1823 off_t usize, gsize; 1824 enum filetype method; 1825 ssize_t bytes_read; 1826#ifndef NO_COMPRESS_SUPPORT 1827 FILE *in; 1828#endif 1829 1830#ifndef SMALL 1831 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { 1832 maybe_warnx("standard input is a terminal -- ignoring"); 1833 goto out; 1834 } 1835#endif 1836 1837 if (fstat(STDIN_FILENO, &isb) < 0) { 1838 maybe_warn("fstat"); 1839 goto out; 1840 } 1841 if (S_ISREG(isb.st_mode)) 1842 in_size = isb.st_size; 1843 else 1844 in_size = 0; 1845 infile_set("(stdin)", in_size); 1846 1847 if (lflag) { 1848 print_list(STDIN_FILENO, in_size, infile, isb.st_mtime); 1849 goto out; 1850 } 1851 1852 bytes_read = read_retry(STDIN_FILENO, fourbytes, sizeof fourbytes); 1853 if (bytes_read == -1) { 1854 maybe_warn("can't read stdin"); 1855 goto out; 1856 } else if (bytes_read != sizeof(fourbytes)) { 1857 maybe_warnx("(stdin): unexpected end of file"); 1858 goto out; 1859 } 1860 1861 method = file_gettype(fourbytes); 1862 switch (method) { 1863 default: 1864#ifndef SMALL 1865 if (fflag == 0) { 1866 maybe_warnx("unknown compression format"); 1867 goto out; 1868 } 1869 usize = cat_fd(fourbytes, sizeof fourbytes, &gsize, STDIN_FILENO); 1870 break; 1871#endif 1872 case FT_GZIP: 1873 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 1874 (char *)fourbytes, sizeof fourbytes, &gsize, "(stdin)"); 1875 break; 1876#ifndef NO_BZIP2_SUPPORT 1877 case FT_BZIP2: 1878 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, 1879 (char *)fourbytes, sizeof fourbytes, &gsize); 1880 break; 1881#endif 1882#ifndef NO_COMPRESS_SUPPORT 1883 case FT_Z: 1884 if ((in = zdopen(STDIN_FILENO)) == NULL) { 1885 maybe_warnx("zopen of stdin"); 1886 goto out; 1887 } 1888 1889 usize = zuncompress(in, stdout, (char *)fourbytes, 1890 sizeof fourbytes, &gsize); 1891 fclose(in); 1892 break; 1893#endif 1894#ifndef NO_PACK_SUPPORT 1895 case FT_PACK: 1896 usize = unpack(STDIN_FILENO, STDOUT_FILENO, 1897 (char *)fourbytes, sizeof fourbytes, &gsize); 1898 break; 1899#endif 1900#ifndef NO_XZ_SUPPORT 1901 case FT_XZ: 1902 usize = unxz(STDIN_FILENO, STDOUT_FILENO, 1903 (char *)fourbytes, sizeof fourbytes, &gsize); 1904 break; 1905#endif 1906#ifndef NO_LZ_SUPPORT 1907 case FT_LZ: 1908 usize = unlz(STDIN_FILENO, STDOUT_FILENO, 1909 (char *)fourbytes, sizeof fourbytes, &gsize); 1910 break; 1911#endif 1912 } 1913 1914#ifndef SMALL 1915 if (vflag && !tflag && usize != -1 && gsize != -1) 1916 print_verbage(NULL, NULL, usize, gsize); 1917 if (vflag && tflag) 1918 print_test("(stdin)", usize != -1); 1919#else 1920 (void)&usize; 1921#endif 1922 1923out: 1924 infile_clear(); 1925} 1926 1927static void 1928handle_stdout(void) 1929{ 1930 off_t gsize; 1931#ifndef SMALL 1932 off_t usize; 1933 struct stat sb; 1934 time_t systime; 1935 uint32_t mtime; 1936 int ret; 1937 1938 infile_set("(stdout)", 0); 1939 1940 if (fflag == 0 && isatty(STDOUT_FILENO)) { 1941 maybe_warnx("standard output is a terminal -- ignoring"); 1942 return; 1943 } 1944 1945 /* If stdin is a file use its mtime, otherwise use current time */ 1946 ret = fstat(STDIN_FILENO, &sb); 1947 if (ret < 0) { 1948 maybe_warn("Can't stat stdin"); 1949 return; 1950 } 1951 1952 if (S_ISREG(sb.st_mode)) { 1953 infile_set("(stdout)", sb.st_size); 1954 mtime = (uint32_t)sb.st_mtime; 1955 } else { 1956 systime = time(NULL); 1957 if (systime == -1) { 1958 maybe_warn("time"); 1959 return; 1960 } 1961 mtime = (uint32_t)systime; 1962 } 1963 1964 usize = 1965#endif 1966 gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime); 1967#ifndef SMALL 1968 if (vflag && !tflag && usize != -1 && gsize != -1) 1969 print_verbage(NULL, NULL, usize, gsize); 1970#endif 1971} 1972 1973/* do what is asked for, for the path name */ 1974static void 1975handle_pathname(char *path) 1976{ 1977 char *opath = path, *s = NULL; 1978 ssize_t len; 1979 int slen; 1980 struct stat sb; 1981 1982 /* check for stdout/stdin */ 1983 if (path[0] == '-' && path[1] == '\0') { 1984 if (dflag) 1985 handle_stdin(); 1986 else 1987 handle_stdout(); 1988 return; 1989 } 1990 1991retry: 1992 if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 && 1993 lstat(path, &sb) != 0)) { 1994 /* lets try <path>.gz if we're decompressing */ 1995 if (dflag && s == NULL && errno == ENOENT) { 1996 len = strlen(path); 1997 slen = suffixes[0].ziplen; 1998 s = malloc(len + slen + 1); 1999 if (s == NULL) 2000 maybe_err("malloc"); 2001 memcpy(s, path, len); 2002 memcpy(s + len, suffixes[0].zipped, slen + 1); 2003 path = s; 2004 goto retry; 2005 } 2006 maybe_warn("can't stat: %s", opath); 2007 goto out; 2008 } 2009 2010 if (S_ISDIR(sb.st_mode)) { 2011#ifndef SMALL 2012 if (rflag) 2013 handle_dir(path); 2014 else 2015#endif 2016 maybe_warnx("%s is a directory", path); 2017 goto out; 2018 } 2019 2020 if (S_ISREG(sb.st_mode)) 2021 handle_file(path, &sb); 2022 else 2023 maybe_warnx("%s is not a regular file", path); 2024 2025out: 2026 if (s) 2027 free(s); 2028} 2029 2030/* compress/decompress a file */ 2031static void 2032handle_file(char *file, struct stat *sbp) 2033{ 2034 off_t usize, gsize; 2035 char outfile[PATH_MAX]; 2036 2037 infile_set(file, sbp->st_size); 2038 if (dflag) { 2039 usize = file_uncompress(file, outfile, sizeof(outfile)); 2040#ifndef SMALL 2041 if (vflag && tflag) 2042 print_test(file, usize != -1); 2043#endif 2044 if (usize == -1) 2045 return; 2046 gsize = sbp->st_size; 2047 } else { 2048 gsize = file_compress(file, outfile, sizeof(outfile)); 2049 if (gsize == -1) 2050 return; 2051 usize = sbp->st_size; 2052 } 2053 infile_clear(); 2054 2055#ifndef SMALL 2056 if (vflag && !tflag) 2057 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); 2058#endif 2059} 2060 2061#ifndef SMALL 2062/* this is used with -r to recursively descend directories */ 2063static void 2064handle_dir(char *dir) 2065{ 2066 char *path_argv[2]; 2067 FTS *fts; 2068 FTSENT *entry; 2069 2070 path_argv[0] = dir; 2071 path_argv[1] = 0; 2072 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 2073 if (fts == NULL) { 2074 warn("couldn't fts_open %s", dir); 2075 return; 2076 } 2077 2078 while (errno = 0, (entry = fts_read(fts))) { 2079 switch(entry->fts_info) { 2080 case FTS_D: 2081 case FTS_DP: 2082 continue; 2083 2084 case FTS_DNR: 2085 case FTS_ERR: 2086 case FTS_NS: 2087 maybe_warn("%s", entry->fts_path); 2088 continue; 2089 case FTS_F: 2090 handle_file(entry->fts_path, entry->fts_statp); 2091 } 2092 } 2093 if (errno != 0) 2094 warn("error with fts_read %s", dir); 2095 (void)fts_close(fts); 2096} 2097#endif 2098 2099/* print a ratio - size reduction as a fraction of uncompressed size */ 2100static void 2101print_ratio(off_t in, off_t out, FILE *where) 2102{ 2103 int percent10; /* 10 * percent */ 2104 off_t diff; 2105 char buff[8]; 2106 int len; 2107 2108 diff = in - out/2; 2109 if (in == 0 && out == 0) 2110 percent10 = 0; 2111 else if (diff < 0) 2112 /* 2113 * Output is more than double size of input! print -99.9% 2114 * Quite possibly we've failed to get the original size. 2115 */ 2116 percent10 = -999; 2117 else { 2118 /* 2119 * We only need 12 bits of result from the final division, 2120 * so reduce the values until a 32bit division will suffice. 2121 */ 2122 while (in > 0x100000) { 2123 diff >>= 1; 2124 in >>= 1; 2125 } 2126 if (in != 0) 2127 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; 2128 else 2129 percent10 = 0; 2130 } 2131 2132 len = snprintf(buff, sizeof buff, "%2.2d.", percent10); 2133 /* Move the '.' to before the last digit */ 2134 buff[len - 1] = buff[len - 2]; 2135 buff[len - 2] = '.'; 2136 fprintf(where, "%5s%%", buff); 2137} 2138 2139#ifndef SMALL 2140/* print compression statistics, and the new name (if there is one!) */ 2141static void 2142print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) 2143{ 2144 if (file) 2145 fprintf(stderr, "%s:%s ", file, 2146 strlen(file) < 7 ? "\t\t" : "\t"); 2147 print_ratio(usize, gsize, stderr); 2148 if (nfile) 2149 fprintf(stderr, " -- replaced with %s", nfile); 2150 fprintf(stderr, "\n"); 2151 fflush(stderr); 2152} 2153 2154/* print test results */ 2155static void 2156print_test(const char *file, int ok) 2157{ 2158 2159 if (exit_value == 0 && ok == 0) 2160 exit_value = 1; 2161 fprintf(stderr, "%s:%s %s\n", file, 2162 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); 2163 fflush(stderr); 2164} 2165#endif 2166 2167/* print a file's info ala --list */ 2168/* eg: 2169 compressed uncompressed ratio uncompressed_name 2170 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar 2171*/ 2172static void 2173print_list(int fd, off_t out, const char *outfile, time_t ts) 2174{ 2175 static int first = 1; 2176#ifndef SMALL 2177 static off_t in_tot, out_tot; 2178 uint32_t crc = 0; 2179#endif 2180 off_t in = 0, rv; 2181 2182 if (first) { 2183#ifndef SMALL 2184 if (vflag) 2185 printf("method crc date time "); 2186#endif 2187 if (qflag == 0) 2188 printf(" compressed uncompressed " 2189 "ratio uncompressed_name\n"); 2190 } 2191 first = 0; 2192 2193 /* print totals? */ 2194#ifndef SMALL 2195 if (fd == -1) { 2196 in = in_tot; 2197 out = out_tot; 2198 } else 2199#endif 2200 { 2201 /* read the last 4 bytes - this is the uncompressed size */ 2202 rv = lseek(fd, (off_t)(-8), SEEK_END); 2203 if (rv != -1) { 2204 unsigned char buf[8]; 2205 uint32_t usize; 2206 2207 rv = read(fd, (char *)buf, sizeof(buf)); 2208 if (rv == -1) 2209 maybe_warn("read of uncompressed size"); 2210 else if (rv != sizeof(buf)) 2211 maybe_warnx("read of uncompressed size"); 2212 2213 else { 2214 usize = le32dec(&buf[4]); 2215 in = (off_t)usize; 2216#ifndef SMALL 2217 crc = le32dec(&buf[0]); 2218#endif 2219 } 2220 } 2221 } 2222 2223#ifndef SMALL 2224 if (vflag && fd == -1) 2225 printf(" "); 2226 else if (vflag) { 2227 char *date = ctime(&ts); 2228 2229 /* skip the day, 1/100th second, and year */ 2230 date += 4; 2231 date[12] = 0; 2232 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); 2233 } 2234 in_tot += in; 2235 out_tot += out; 2236#else 2237 (void)&ts; /* XXX */ 2238#endif 2239 print_list_out(out, in, outfile); 2240} 2241 2242static void 2243print_list_out(off_t out, off_t in, const char *outfile) 2244{ 2245 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); 2246 print_ratio(in, out, stdout); 2247 printf(" %s\n", outfile); 2248} 2249 2250/* display the usage of NetBSD gzip */ 2251static void 2252usage(void) 2253{ 2254 2255 fprintf(stderr, "%s\n", gzip_version); 2256 fprintf(stderr, 2257#ifdef SMALL 2258 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n", 2259#else 2260 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n" 2261 " -1 --fast fastest (worst) compression\n" 2262 " -2 .. -8 set compression level\n" 2263 " -9 --best best (slowest) compression\n" 2264 " -c --stdout write to stdout, keep original files\n" 2265 " --to-stdout\n" 2266 " -d --decompress uncompress files\n" 2267 " --uncompress\n" 2268 " -f --force force overwriting & compress links\n" 2269 " -h --help display this help\n" 2270 " -k --keep don't delete input files during operation\n" 2271 " -l --list list compressed file contents\n" 2272 " -N --name save or restore original file name and time stamp\n" 2273 " -n --no-name don't save original file name or time stamp\n" 2274 " -q --quiet output no warnings\n" 2275 " -r --recursive recursively compress files in directories\n" 2276 " -S .suf use suffix .suf instead of .gz\n" 2277 " --suffix .suf\n" 2278 " -t --test test compressed file\n" 2279 " -V --version display program version\n" 2280 " -v --verbose print extra statistics\n", 2281#endif 2282 getprogname()); 2283 exit(0); 2284} 2285 2286#ifndef SMALL 2287/* display the license information of FreeBSD gzip */ 2288static void 2289display_license(void) 2290{ 2291 2292 fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version); 2293 fprintf(stderr, "%s\n", gzip_copyright); 2294 exit(0); 2295} 2296#endif 2297 2298/* display the version of NetBSD gzip */ 2299static void 2300display_version(void) 2301{ 2302 2303 fprintf(stderr, "%s\n", gzip_version); 2304 exit(0); 2305} 2306 2307#ifndef NO_BZIP2_SUPPORT 2308#include "unbzip2.c" 2309#endif 2310#ifndef NO_COMPRESS_SUPPORT 2311#include "zuncompress.c" 2312#endif 2313#ifndef NO_PACK_SUPPORT 2314#include "unpack.c" 2315#endif 2316#ifndef NO_XZ_SUPPORT 2317#include "unxz.c" 2318#endif 2319#ifndef NO_LZ_SUPPORT 2320#include "unlz.c" 2321#endif 2322 2323static ssize_t 2324read_retry(int fd, void *buf, size_t sz) 2325{ 2326 char *cp = buf; 2327 size_t left = MIN(sz, (size_t) SSIZE_MAX); 2328 2329 while (left > 0) { 2330 ssize_t ret; 2331 2332 ret = read(fd, cp, left); 2333 if (ret == -1) { 2334 return ret; 2335 } else if (ret == 0) { 2336 break; /* EOF */ 2337 } 2338 cp += ret; 2339 left -= ret; 2340 } 2341 2342 return sz - left; 2343} 2344 2345static ssize_t 2346write_retry(int fd, const void *buf, size_t sz) 2347{ 2348 const char *cp = buf; 2349 size_t left = MIN(sz, (size_t) SSIZE_MAX); 2350 2351 while (left > 0) { 2352 ssize_t ret; 2353 2354 ret = write(fd, cp, left); 2355 if (ret == -1) { 2356 return ret; 2357 } else if (ret == 0) { 2358 abort(); /* Can't happen */ 2359 } 2360 cp += ret; 2361 left -= ret; 2362 } 2363 2364 return sz - left; 2365} 2366