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