X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=gzip.c;h=9751c5e7cc121210be0909131b6924af82f1629d;hb=92249085071a973e2c0621b0415b93d2e48bb00d;hp=4a51b1334f81b983d62581a240064726b44ba26c;hpb=2e7e88b2efec90c4ad9dc7a24d533b36a6223be2;p=debian%2Fgzip diff --git a/gzip.c b/gzip.c index 4a51b13..9751c5e 100644 --- a/gzip.c +++ b/gzip.c @@ -1,6 +1,6 @@ /* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface - Copyright (C) 1999, 2001-2002, 2006-2007, 2009-2016 Free Software + Copyright (C) 1999, 2001-2002, 2006-2007, 2009-2018 Free Software Foundation, Inc. Copyright (C) 1992-1993 Jean-loup Gailly @@ -29,10 +29,10 @@ */ static char const *const license_msg[] = { -"Copyright (C) 2016 Free Software Foundation, Inc.", +"Copyright (C) 2018 Free Software Foundation, Inc.", "Copyright (C) 1993 Jean-loup Gailly.", "This is free software. You may redistribute copies of it under the terms of", -"the GNU General Public License .", +"the GNU General Public License .", "There is NO WARRANTY, to the extent permitted by law.", 0}; @@ -43,7 +43,7 @@ static char const *const license_msg[] = { * or stdout with -c option or if stdin used as input. * If the output file name had to be truncated, the original name is kept * in the compressed file. - * On MSDOS, file.tmp -> file.tmz. On VMS, file.tmp -> file.tmp-gz. + * On MSDOS, file.tmp -> file.tmz. * * Using gz on MSDOS would create too many file name conflicts. For * example, foo.txt -> foo.tgz (.tgz must be reserved as shorthand for @@ -71,11 +71,13 @@ static char const *const license_msg[] = { #include "timespec.h" #include "dirname.h" +#include "dosname.h" #include "fcntl--.h" #include "getopt.h" #include "ignore-value.h" #include "stat-time.h" #include "version.h" +#include "xalloc.h" #include "yesno.h" /* configuration */ @@ -113,21 +115,6 @@ static char const *const license_msg[] = { off_t lseek (int fd, off_t offset, int whence); #endif -#ifndef OFF_T_MAX -# define OFF_T_MAX TYPE_MAXIMUM (off_t) -#endif - -/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is - present. */ -#ifndef SA_NOCLDSTOP -# define SA_NOCLDSTOP 0 -# define sigprocmask(how, set, oset) /* empty */ -# define sigset_t int -# if ! HAVE_SIGINTERRUPT -# define siginterrupt(sig, flag) /* empty */ -# endif -#endif - #ifndef HAVE_WORKING_O_NOFOLLOW # define HAVE_WORKING_O_NOFOLLOW 0 #endif @@ -188,12 +175,19 @@ static int foreground = 0; /* set if program run in foreground */ int save_orig_name; /* set if original name must be saved */ static int last_member; /* set for .zip and .Z files */ static int part_nb; /* number of parts in .gz file */ - struct timespec time_stamp; /* original time stamp (modification time) */ off_t ifile_size; /* input file size, -1 for devices (debug only) */ static char *env; /* contents of GZIP env variable */ static char const *z_suffix; /* default suffix (can be set with --suffix) */ static size_t z_len; /* strlen(z_suffix) */ +/* The original timestamp (modification time). If the original is + unknown, TIME_STAMP.tv_nsec is negative. If the original is + greater than struct timespec range, TIME_STAMP is the maximal + struct timespec value; this can happen on hosts with 32-bit signed + time_t because the gzip format's MTIME is 32-bit unsigned. + The original cannot be less than struct timespec range. */ +struct timespec time_stamp; + /* The set of signals that are caught. */ static sigset_t caught_signals; @@ -202,8 +196,10 @@ static sigset_t caught_signals; suppresses a "Broken Pipe" message with some shells. */ static int volatile exiting_signal; -/* If nonnegative, close this file descriptor and unlink ofname on error. */ +/* If nonnegative, close this file descriptor and unlink remove_ofname + on error. */ static int volatile remove_ofname_fd = -1; +static char volatile remove_ofname[MAX_PATH_LEN]; static bool stdin_was_read; @@ -284,7 +280,6 @@ static const struct option longopts[] = {"recursive", 0, 0, 'r'}, /* recurse through directories */ {"suffix", 1, 0, 'S'}, /* use given suffix instead of .gz */ {"test", 0, 0, 't'}, /* test compressed file integrity */ - {"no-time", 0, 0, 'T'}, /* don't save or restore the time stamp */ {"verbose", 0, 0, 'v'}, /* verbose mode */ {"version", 0, 0, 'V'}, /* display version number */ {"fast", 0, 0, '1'}, /* compress faster */ @@ -297,7 +292,7 @@ static const struct option longopts[] = /* local functions */ -local void try_help (void) ATTRIBUTE_NORETURN; +local noreturn void try_help (void); local void help (void); local void license (void); local void version (void); @@ -315,9 +310,9 @@ local void do_list (int ifd, int method); local int check_ofname (void); local void copy_stat (struct stat *ifstat); local void install_signal_handlers (void); -local void remove_output_file (void); -local RETSIGTYPE abort_gzip_signal (int); -local void do_exit (int exitcode) ATTRIBUTE_NORETURN; +static void remove_output_file (bool); +static void abort_gzip_signal (int); +local noreturn void do_exit (int exitcode); static void finish_out (void); int main (int argc, char **argv); static int (*work) (int infile, int outfile) = zip; /* function to call */ @@ -357,11 +352,11 @@ local void help() " -l, --list list compressed file contents", " -L, --license display software license", #ifdef UNDOCUMENTED - " -m, --no-time do not save or restore the original modification time", + " -m do not save or restore the original modification time", " -M, --time save or restore the original modification time", #endif - " -n, --no-name do not save or restore the original name and time stamp", - " -N, --name save or restore the original name and time stamp", + " -n, --no-name do not save or restore the original name and timestamp", + " -N, --name save or restore the original name and timestamp", " -q, --quiet suppress all warnings", #if ! NO_DIR " -r, --recursive operate recursively on directories", @@ -429,7 +424,7 @@ int main (int argc, char **argv) program_name = gzip_base_name (argv[0]); proglen = strlen (program_name); - /* Suppress .exe for MSDOS, OS/2 and VMS: */ + /* Suppress .exe for MSDOS and OS/2: */ if (4 < proglen && strequ (program_name + proglen - 4, ".exe")) program_name[proglen - 4] = '\0'; @@ -447,7 +442,7 @@ int main (int argc, char **argv) * gzip even if it is invoked under the name gunzip or zcat. * * Systems which do not support links can still use -d or -dc. - * Ignore an .exe extension for MSDOS, OS/2 and VMS. + * Ignore an .exe extension for MSDOS and OS/2. */ if (strncmp (program_name, "un", 2) == 0 /* ungzip, uncompress */ || strncmp (program_name, "gun", 3) == 0) /* gunzip */ @@ -594,7 +589,7 @@ int main (int argc, char **argv) case '4' + ENV_OPTION: case '5' + ENV_OPTION: case '6' + ENV_OPTION: case '7' + ENV_OPTION: case '8' + ENV_OPTION: case '9' + ENV_OPTION: optc -= ENV_OPTION; - /* Fall through. */ + FALLTHROUGH; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = optc - '0'; @@ -684,7 +679,6 @@ int main (int argc, char **argv) && errno != EBADF) write_error (); do_exit(exit_code); - return exit_code; /* just to avoid lint warning */ } /* Return nonzero when at end of file on input. */ @@ -706,6 +700,25 @@ input_eof () return 0; } +static void +get_input_size_and_time (void) +{ + ifile_size = -1; + time_stamp.tv_nsec = -1; + + /* Record the input file's size and timestamp only if it is a + regular file. Doing this for the timestamp helps to keep gzip's + output more reproducible when it is used as part of a + pipeline. */ + + if (S_ISREG (istat.st_mode)) + { + ifile_size = istat.st_size; + if (!no_time || list) + time_stamp = get_stat_mtime (&istat); + } +} + /* ======================================================================== * Compress or decompress stdin */ @@ -747,21 +760,14 @@ local void treat_stdin() strcpy(ifname, "stdin"); strcpy(ofname, "stdout"); - /* Get the file's time stamp and size. */ + /* Get the file's timestamp and size. */ if (fstat (STDIN_FILENO, &istat) != 0) { progerror ("standard input"); do_exit (ERROR); } - ifile_size = S_ISREG (istat.st_mode) ? istat.st_size : -1; - time_stamp.tv_nsec = -1; - if (!no_time || list) - { - if (S_ISREG (istat.st_mode)) - time_stamp = get_stat_mtime (&istat); - else - gettime (&time_stamp); - } + + get_input_size_and_time (); clear_bufs(); /* clear input and output buffers */ to_stdout = 1; @@ -933,10 +939,7 @@ local void treat_file(iname) } } - ifile_size = S_ISREG (istat.st_mode) ? istat.st_size : -1; - time_stamp.tv_nsec = -1; - if (!no_time || list) - time_stamp = get_stat_mtime (&istat); + get_input_size_and_time (); /* Generate output file name. For -r and (-t or -l), skip files * without a valid gzip suffix (check done in make_ofname). @@ -1045,7 +1048,7 @@ local void treat_file(iname) if (method == -1) { if (!to_stdout) - remove_output_file (); + remove_output_file (false); return; } @@ -1065,6 +1068,13 @@ local void treat_file(iname) } } +static void +volatile_strcpy (char volatile *dst, char const volatile *src) +{ + while ((*dst++ = *src++)) + continue; +} + /* ======================================================================== * Create the output file. Return OK or ERROR. * Try several times if necessary to avoid truncating the z_suffix. For @@ -1098,6 +1108,8 @@ local int create_outfile() int open_errno; sigset_t oldset; + volatile_strcpy (remove_ofname, ofname); + sigprocmask (SIG_BLOCK, &caught_signals, &oldset); remove_ofname_fd = ofd = openat (atfd, base, flags, S_IRUSR | S_IWUSR); open_errno = errno; @@ -1149,8 +1161,6 @@ local int create_outfile() * also accepted suffixes. For Unix, we do not want to accept any * .??z suffix as indicating a compressed file; some people use .xyz * to denote volume data. - * On systems allowing multiple versions of the same file (such as VMS), - * this function removes any version suffix in the given name. */ local char *get_suffix(name) char *name; @@ -1177,18 +1187,14 @@ local char *get_suffix(name) break; } } + + char *z_lower = xstrdup(z_suffix); + strlwr(z_lower); known_suffixes[suffix_of_builtin ? sizeof known_suffixes / sizeof *known_suffixes - 2 - : 0] = z_suffix; + : 0] = z_lower; suf = known_suffixes + suffix_of_builtin; -#ifdef SUFFIX_SEP - /* strip a version number from the file name */ - { - char *v = strrchr(name, SUFFIX_SEP); - if (v != NULL) *v = '\0'; - } -#endif nlen = strlen(name); if (nlen <= MAX_SUFFIX+2) { strcpy(suffix, name); @@ -1197,15 +1203,18 @@ local char *get_suffix(name) } strlwr(suffix); slen = strlen(suffix); + char *match = NULL; do { int s = strlen(*suf); - if (slen > s && suffix[slen-s-1] != PATH_SEP + if (slen > s && ! ISSLASH (suffix[slen - s - 1]) && strequ(suffix + slen - s, *suf)) { - return name+nlen-s; + match = name+nlen-s; + break; } } while (*++suf != NULL); + free(z_lower); - return NULL; + return match; } @@ -1226,7 +1235,7 @@ open_and_stat (char *name, int flags, struct stat *st) flags |= O_NOFOLLOW; else { -#if HAVE_LSTAT || defined lstat +#ifdef S_ISLNK if (lstat (name, st) != 0) return -1; else if (S_ISLNK (st->st_mode)) @@ -1301,9 +1310,7 @@ open_input_file (iname, sbuf) progerror(ifname); return -1; } - /* file.ext doesn't exist, try adding a suffix (after removing any - * version number for VMS). - */ + /* File.ext doesn't exist. Try adding a suffix. */ s = get_suffix(ifname); if (s != NULL) { progerror(ifname); /* ifname already has z suffix and does not exist */ @@ -1472,7 +1479,7 @@ discard_input_bytes (nbytes, flags) * original name was given and to_stdout is not set. * Return the compression method, -1 for error, -2 for warning. * Set inptr to the offset of the next byte to be processed. - * Updates time_stamp if there is one and --no-time is not used. + * Updates time_stamp if there is one and neither -m nor -n is used. * This function may be called repeatedly for an input file consisting * of several contiguous gzip'ed members. * IN assertions: there is at least one remaining compressed member. @@ -1485,7 +1492,7 @@ local int get_method(in) uch magic[10]; /* magic header */ int imagic0; /* first magic byte or EOF */ int imagic1; /* like magic[1], but can represent EOF */ - ulg stamp; /* time stamp */ + ulg stamp; /* timestamp */ /* If --force and --stdout, zcat == cat, so do not complain about * premature end of file: use try_byte instead of get_byte. @@ -1510,7 +1517,7 @@ local int get_method(in) method = -1; /* unknown yet */ part_nb++; /* number of parts in gzip file */ header_bytes = 0; - last_member = RECORD_IO; + last_member = 0; /* assume multiple members in gzip file except for record oriented I/O */ if (memcmp(magic, GZIP_MAGIC, 2) == 0 @@ -1547,8 +1554,19 @@ local int get_method(in) stamp |= ((ulg)get_byte()) << 24; if (stamp != 0 && !no_time) { - time_stamp.tv_sec = stamp; - time_stamp.tv_nsec = 0; + if (stamp <= TYPE_MAXIMUM (time_t)) + { + time_stamp.tv_sec = stamp; + time_stamp.tv_nsec = 0; + } + else + { + WARN ((stderr, + "%s: %s: MTIME %lu out of range for this platform\n", + program_name, ifname, stamp)); + time_stamp.tv_sec = TYPE_MAXIMUM (time_t); + time_stamp.tv_nsec = TIMESPEC_RESOLUTION - 1; + } } magic[8] = get_byte (); /* Ignore extra flags. */ @@ -1714,12 +1732,7 @@ local void do_list(ifd, method) "lzh ", /* 3 */ "", "", "", "", /* 4 to 7 reserved */ "defla"}; /* 8 */ - int positive_off_t_width = 1; - off_t o; - - for (o = OFF_T_MAX; 9 < o; o /= 10) { - positive_off_t_width++; - } + int positive_off_t_width = INT_STRLEN_BOUND (off_t) - 1; if (first_time && method >= 0) { first_time = 0; @@ -1753,7 +1766,7 @@ local void do_list(ifd, method) bytes_out = -1L; bytes_in = ifile_size; - if (!RECORD_IO && method == DEFLATED && !last_member) { + if (method == DEFLATED && !last_member) { /* Get the crc and uncompressed size for gzip'ed (not zip'ed) files. * If the lseek fails, we could use read() to get to the end, but * --list is used to get quick results. @@ -1845,8 +1858,7 @@ local void shorten_name(name) * 1234.678.012.gz -> 123.678.012.gz */ do { - p = strrchr(name, PATH_SEP); - p = p ? p+1 : name; + p = last_component (name); while (*p) { plen = strcspn(p, PART_SEP); p += plen; @@ -1901,17 +1913,20 @@ local int check_ofname() the file and NAME its name. Change it to user UID and to group GID. If UID or GID is -1, though, do not change the corresponding user or group. */ +#if ! (HAVE_FCHOWN || HAVE_CHOWN) +/* The types uid_t and gid_t do not exist on mingw, so don't assume them. */ +# define do_chown(fd, name, uid, gid) ((void) 0) +#else static void do_chown (int fd, char const *name, uid_t uid, gid_t gid) { -#ifndef NO_CHOWN # if HAVE_FCHOWN ignore_value (fchown (fd, uid, gid)); # else ignore_value (chown (name, uid, gid)); # endif -#endif } +#endif /* ======================================================================== * Copy modes, times, ownership from input file to output file. @@ -1924,21 +1939,23 @@ local void copy_stat(ifstat) int r; #ifndef NO_UTIME + bool restoring; struct timespec timespec[2]; timespec[0] = get_stat_atime (ifstat); timespec[1] = get_stat_mtime (ifstat); + restoring = (decompress && 0 <= time_stamp.tv_nsec + && ! (timespec[1].tv_sec == time_stamp.tv_sec + && timespec[1].tv_nsec == time_stamp.tv_nsec)); + if (restoring) + timespec[1] = time_stamp; - if (decompress && 0 <= time_stamp.tv_nsec - && ! (timespec[1].tv_sec == time_stamp.tv_sec - && timespec[1].tv_nsec == time_stamp.tv_nsec)) + if (fdutimens (ofd, ofname, timespec) == 0) { - timespec[1] = time_stamp; - if (verbose > 1) { - fprintf(stderr, "%s: time stamp restored\n", ofname); + if (restoring && 1 < verbose) { + fprintf(stderr, "%s: timestamp restored\n", ofname); } } - - if (fdutimens (ofd, ofname, timespec) != 0) + else { int e = errno; WARN ((stderr, "%s: ", program_name)); @@ -2013,16 +2030,8 @@ local void treat_dir (fd, dir) continue; if (len + entrylen < MAX_PATH_LEN - 2) { strcpy(nbuf,dir); - if (len != 0 /* dir = "" means current dir on Amiga */ -#ifdef PATH_SEP2 - && dir[len-1] != PATH_SEP2 -#endif -#ifdef PATH_SEP3 - && dir[len-1] != PATH_SEP3 -#endif - ) { - nbuf[len++] = PATH_SEP; - } + if (*last_component (nbuf) && !ISSLASH (nbuf[len - 1])) + nbuf[len++] = '/'; strcpy (nbuf + len, entry); treat_file(nbuf); } else { @@ -2042,8 +2051,6 @@ install_signal_handlers () { int nsigs = sizeof handled_sig / sizeof handled_sig[0]; int i; - -#if SA_NOCLDSTOP struct sigaction act; sigemptyset (&caught_signals); @@ -2065,16 +2072,6 @@ install_signal_handlers () foreground = 1; sigaction (handled_sig[i], &act, NULL); } -#else - for (i = 0; i < nsigs; i++) - if (signal (handled_sig[i], SIG_IGN) != SIG_IGN) - { - if (i == 0) - foreground = 1; - signal (handled_sig[i], abort_gzip_signal); - siginterrupt (handled_sig[i], 1); - } -#endif } /* ======================================================================== @@ -2114,42 +2111,43 @@ finish_out (void) * Close and unlink the output file. */ static void -remove_output_file () +remove_output_file (bool signals_already_blocked) { int fd; sigset_t oldset; - sigprocmask (SIG_BLOCK, &caught_signals, &oldset); + if (!signals_already_blocked) + sigprocmask (SIG_BLOCK, &caught_signals, &oldset); fd = remove_ofname_fd; if (0 <= fd) { + char fname[MAX_PATH_LEN]; remove_ofname_fd = -1; close (fd); - xunlink (ofname); + volatile_strcpy (fname, remove_ofname); + xunlink (fname); } - sigprocmask (SIG_SETMASK, &oldset, NULL); + if (!signals_already_blocked) + sigprocmask (SIG_SETMASK, &oldset, NULL); } /* ======================================================================== * Error handler. */ void -abort_gzip () +abort_gzip (void) { - remove_output_file (); + remove_output_file (false); do_exit(ERROR); } /* ======================================================================== * Signal handler. */ -static RETSIGTYPE -abort_gzip_signal (sig) - int sig; +static void +abort_gzip_signal (int sig) { - if (! SA_NOCLDSTOP) - signal (sig, SIG_IGN); - remove_output_file (); + remove_output_file (true); if (sig == exiting_signal) _exit (WARNING); signal (sig, SIG_DFL);