Antonio Diaz Diaz wrote:
ddrescue already provides the option '-y, --synchronous' for a somewhat similar functionality.
OK, let's do it as --synchronous, long-only. If the need keeps growing we can add -y.
Just now my preference is to make the behavior optional
On second thought, as Bob Proulx suggested, this is a better approach. I tried the a synchronous gzip on a contrived example (compressing 1000 empty files on an ext4 file system on an actual hard drive with options relatime, seclabel, data=ordered) and synchronizing made gzip 700x slower. Most people will prefer the old behavior, where gzip is faster and is unsafe mostly just in theory.
I'm attaching the patches I installed recently in this area, to help fix this problem. I'll follow up on your other recent email in another message soon.
>From 8604a5f86e8d1d83594b722a6e401f2e853cafa2 Mon Sep 17 00:00:00 2001 From: Paul Eggert <egg...@cs.ucla.edu> Date: Sat, 27 Feb 2016 14:12:46 -0800 Subject: [PATCH 1/4] gzip: fdatasync output dir before unlinking This follows up on the earlier patch to avoid data loss near the system crashes. See: http://bugs.gnu.org/22768 * bootstrap.conf (gnulib_modules): Add dirname-lgpl, fdatasync, openat-safer, unistd-safer, unlinkat. * gzip.c: Include stddef.h, dirname.h. Include fcntl--.h instead of fcntl-safer.h. (RW_USER): Remove; no longer needed. (dfname, dfd): New static vars. (dot): New static const. (atdir_eq, atdir_set): New functions. (treat_file): Also fdatasync the output directory, if !keep. (treat_file, create_outfile, open_and_stat): Use dir fd for unlinkat and openat, if possible. (open_and_stat): Omit mode argument, since it was always the same. All callers changed. * lib/.gitignore, m4/.gitignore: Add new gnulib files. * tailor.h (PROTO, NO_STDIN_FSTAT, OPEN): Remove. Remove MACOS section, as this stuff would not work anyway now, and circa 2001 Apple stopped supporting Mac OS 9 and earlier. * zip.c: Do not include unistd.h and fcntl.h, as this file does not directly use any symbols defined by those headers. --- bootstrap.conf | 5 +++ gzip.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++--------- lib/.gitignore | 10 +++++- m4/.gitignore | 7 +++- tailor.h | 21 ------------ zip.c | 3 -- 6 files changed, 109 insertions(+), 42 deletions(-) diff --git a/bootstrap.conf b/bootstrap.conf index b15caa3..308dc5e 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -25,10 +25,12 @@ announce-gen calloc close closein +dirname-lgpl fclose fcntl fcntl-safer fdl +fdatasync fdopendir fprintf-posix fsync @@ -47,6 +49,7 @@ lstat maintainer-makefile malloc-gnu manywarnings +openat-safer perror printf-posix readme-release @@ -55,6 +58,8 @@ savedir stat-time sys_stat time +unistd-safer +unlinkat update-copyright utimens xalloc diff --git a/gzip.c b/gzip.c index b872383..15bcf78 100644 --- a/gzip.c +++ b/gzip.c @@ -59,6 +59,7 @@ static char const *const license_msg[] = { #include <sys/types.h> #include <signal.h> #include <stdbool.h> +#include <stddef.h> #include <sys/stat.h> #include <errno.h> @@ -70,7 +71,8 @@ static char const *const license_msg[] = { #include "revision.h" #include "timespec.h" -#include "fcntl-safer.h" +#include "dirname.h" +#include "fcntl--.h" #include "getopt.h" #include "ignore-value.h" #include "stat-time.h" @@ -79,7 +81,6 @@ static char const *const license_msg[] = { /* configuration */ -#include <fcntl.h> #include <limits.h> #include <unistd.h> #include <stdlib.h> @@ -97,8 +98,6 @@ static char const *const license_msg[] = { # include <utimens.h> #endif -#define RW_USER (S_IRUSR | S_IWUSR) /* creation mode for open() */ - #ifndef MAX_PATH_LEN # define MAX_PATH_LEN 1024 /* max pathname length */ #endif @@ -205,9 +204,11 @@ static off_t total_in; /* input bytes for all files */ static off_t total_out; /* output bytes for all files */ char ifname[MAX_PATH_LEN]; /* input file name */ char ofname[MAX_PATH_LEN]; /* output file name */ +static char dfname[MAX_PATH_LEN]; /* name of dir containing output file */ static struct stat istat; /* status for input file */ int ifd; /* input file descriptor */ int ofd; /* output file descriptor */ +static int dfd = -1; /* output directory file descriptor */ unsigned insize; /* valid bytes in inbuf */ unsigned inptr; /* index of next byte to be processed in inbuf */ unsigned outcnt; /* bytes in output buffer */ @@ -769,6 +770,48 @@ local void treat_stdin() } } +static char const dot = '.'; + +/* True if the cached directory for calls to openat etc. is DIR, with + length DIRLEN. DIR need not be null-terminated. DIRLEN must be + less than MAX_PATH_LEN. */ +static bool +atdir_eq (char const *dir, ptrdiff_t dirlen) +{ + if (dirlen == 0) + dir = &dot, dirlen = 1; + return memcmp (dfname, dir, dirlen) == 0 && !dfname[dirlen]; +} + +/* Set the directory used for calls to openat etc. to be the directory + DIR, with length DIRLEN. DIR need not be null-terminated. + DIRLEN must be less than MAX_PATH_LEN. Return a file descriptor for + the directory, or -1 if one could not be obtained. */ +static int +atdir_set (char const *dir, ptrdiff_t dirlen) +{ + /* Don't bother opening directories on older systems that + lack openat and unlinkat. It's not worth the porting hassle. */ + #if HAVE_OPENAT && HAVE_UNLINKAT + enum { try_opening_directories = true }; + #else + enum { try_opening_directories = false }; + #endif + + if (try_opening_directories && ! atdir_eq (dir, dirlen)) + { + if (0 <= dfd) + close (dfd); + if (dirlen == 0) + dir = &dot, dirlen = 1; + memcpy (dfname, dir, dirlen); + dfname[dirlen] = '\0'; + dfd = open (dfname, O_SEARCH | O_DIRECTORY); + } + + return dfd; +} + /* ======================================================================== * Compress or decompress the given file */ @@ -928,26 +971,30 @@ local void treat_file(iname) { copy_stat (&istat); - /* Transfer output data to the output file's storage device. + /* If KEEP, transfer output data to the output file's storage device. Otherwise, if the system crashed now the user might lose both input and output data. See: Pillai TS et al. All file systems are not created equal: on the complexity of crafting crash-consistent applications. OSDI'14. 2014:433-48. https://www.usenix.org/conference/osdi14/technical-sessions/presentation/pillai */ - if (!keep && fsync (ofd) != 0 && errno != EINVAL) - write_error (); - - if (close (ofd) != 0) + if ((!keep + && ((0 <= dfd && fdatasync (dfd) != 0 && errno != EINVAL) + || (fsync (ofd) != 0 && errno != EINVAL))) + || close (ofd) != 0) write_error (); if (!keep) { sigset_t oldset; int unlink_errno; + char *ifbase = last_component (ifname); + int ufd = atdir_eq (ifname, ifbase - ifname) ? dfd : -1; + int res; sigprocmask (SIG_BLOCK, &caught_signals, &oldset); remove_ofname_fd = -1; - unlink_errno = xunlink (ifname) == 0 ? 0 : errno; + res = ufd < 0 ? xunlink (ifname) : unlinkat (ufd, ifbase, 0); + unlink_errno = res == 0 ? 0 : errno; sigprocmask (SIG_SETMASK, &oldset, NULL); if (unlink_errno) @@ -998,6 +1045,19 @@ local int create_outfile() int name_shortened = 0; int flags = (O_WRONLY | O_CREAT | O_EXCL | (ascii && decompress ? 0 : O_BINARY)); + char const *base = ofname; + int atfd = AT_FDCWD; + + if (!keep) + { + char const *b = last_component (ofname); + int f = atdir_set (ofname, b - ofname); + if (0 <= f) + { + base = b; + atfd = f; + } + } for (;;) { @@ -1005,7 +1065,7 @@ local int create_outfile() sigset_t oldset; sigprocmask (SIG_BLOCK, &caught_signals, &oldset); - remove_ofname_fd = ofd = OPEN (ofname, flags, RW_USER); + remove_ofname_fd = ofd = openat (atfd, base, flags, S_IRUSR | S_IWUSR); open_errno = errno; sigprocmask (SIG_SETMASK, &oldset, NULL); @@ -1115,13 +1175,15 @@ local char *get_suffix(name) } -/* Open file NAME with the given flags and mode and store its status +/* Open file NAME with the given flags and store its status into *ST. Return a file descriptor to the newly opened file, or -1 (setting errno) on failure. */ static int -open_and_stat (char *name, int flags, mode_t mode, struct stat *st) +open_and_stat (char *name, int flags, struct stat *st) { int fd; + int atfd = AT_FDCWD; + char const *base = name; /* Refuse to follow symbolic links unless -c or -f. */ if (!to_stdout && !force) @@ -1142,7 +1204,18 @@ open_and_stat (char *name, int flags, mode_t mode, struct stat *st) } } - fd = OPEN (name, flags, mode); + if (!keep) + { + char const *b = last_component (name); + int f = atdir_set (name, b - name); + if (0 <= f) + { + base = b; + atfd = f; + } + } + + fd = openat (atfd, base, flags); if (0 <= fd && fstat (fd, st) != 0) { int e = errno; @@ -1186,7 +1259,7 @@ open_input_file (iname, sbuf) strcpy(ifname, iname); /* If input file exists, return OK. */ - fd = open_and_stat (ifname, open_flags, RW_USER, sbuf); + fd = open_and_stat (ifname, open_flags, sbuf); if (0 <= fd) return fd; @@ -1227,7 +1300,7 @@ open_input_file (iname, sbuf) if (sizeof ifname <= ilen + strlen (s)) goto name_too_long; strcat(ifname, s); - fd = open_and_stat (ifname, open_flags, RW_USER, sbuf); + fd = open_and_stat (ifname, open_flags, sbuf); if (0 <= fd) return fd; if (errno != ENOENT) diff --git a/lib/.gitignore b/lib/.gitignore index a368a26..b26889f 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -5,6 +5,7 @@ /alloca.in.h /asnprintf.c /assure.h +/at-func.c /basename-lgpl.c /c-ctype.c /c-ctype.h @@ -59,6 +60,7 @@ /fd-hook.c /fd-hook.h /fd-safer.c +/fdatasync.c /fdopendir.c /fflush.c /filename.h @@ -84,6 +86,8 @@ /fseterr.c /fseterr.h /fstat.c +/fstatat.c +/fsync.c /ftell.c /ftello.c /getcwd-lgpl.c @@ -132,6 +136,7 @@ /openat-die.c /openat-priv.h /openat-proc.c +/openat-safer.c /openat.c /openat.h /opendir-safer.c @@ -157,6 +162,7 @@ /ref-add.sin /ref-del.sed /ref-del.sin +/rmdir.c /save-cwd.c /save-cwd.h /savedir.c @@ -169,6 +175,7 @@ /stat-time.c /stat-time.h /stat.c +/statat.c /stdbool.h /stdbool.in.h /stddef.h @@ -205,6 +212,8 @@ /unistd.c /unistd.h /unistd.in.h +/unlink.c +/unlinkat.c /unused-parameter.h /utimens.c /utimens.h @@ -225,4 +234,3 @@ /xsize.h /yesno.c /yesno.h -/fsync.c diff --git a/m4/.gitignore b/m4/.gitignore index 32f5566..4641515 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -32,6 +32,7 @@ /fcntl-safer.m4 /fcntl.m4 /fcntl_h.m4 +/fdatasync.m4 /fdopendir.m4 /fflush.m4 /filenamecat.m4 @@ -48,6 +49,8 @@ /fseeko.m4 /fseterr.m4 /fstat.m4 +/fstatat.m4 +/fsync.m4 /ftell.m4 /ftello.m4 /getcwd.m4 @@ -109,6 +112,7 @@ /quotearg.m4 /readdir.m4 /realloc.m4 +/rmdir.m4 /save-cwd.m4 /savedir.m4 /signbit.m4 @@ -136,6 +140,8 @@ /timespec.m4 /unistd-safer.m4 /unistd_h.m4 +/unlink.m4 +/unlinkat.m4 /utimbuf.m4 /utimens.m4 /utimes.m4 @@ -150,4 +156,3 @@ /xalloc.m4 /xsize.m4 /yesno.m4 -/fsync.m4 diff --git a/tailor.h b/tailor.h index 9d2399d..1feb807 100644 --- a/tailor.h +++ b/tailor.h @@ -56,7 +56,6 @@ # define NO_MULTIPLE_DOTS # define MAX_EXT_CHARS 3 # define Z_SUFFIX "z" -# define PROTO # define STDC_HEADERS # define NO_SIZE_CHECK # define UNLINK_READONLY_BUG @@ -81,7 +80,6 @@ # define Z_SUFFIX "z" # define casemap(c) tolow(c) # endif -# define PROTO # define STDC_HEADERS # define UNLINK_READONLY_BUG # include <io.h> @@ -114,7 +112,6 @@ # define PATH_SEP2 '\\' # define PATH_SEP3 ':' # define MAX_PATH_LEN 260 -# define PROTO # define STDC_HEADERS # define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) # define UNLINK_READONLY_BUG @@ -179,7 +176,6 @@ # define HAVE_CHOWN # define HAVE_LSTAT # else /* SASC */ -# define NO_STDIN_FSTAT # define HAVE_SYS_DIR_H # include <fcntl.h> /* for read() and write() */ # define direct dirent @@ -202,19 +198,6 @@ # endif #endif -#ifdef MACOS -# define PATH_SEP ':' -# define DYN_ALLOC -# define PROTO -# define NO_STDIN_FSTAT -# define chmod(file, mode) (0) -# define OPEN(name, flags, mode) open(name, flags) -# define OS_CODE 0x07 -# ifdef MPW -# define isatty(fd) ((fd) <= 2) -# endif -#endif - #ifdef TOPS20 # define OS_CODE 0x0a #endif @@ -276,7 +259,3 @@ #ifndef SET_BINARY_MODE # define SET_BINARY_MODE(fd) #endif - -#ifndef OPEN -# define OPEN(name, flags, mode) open_safer (name, flags, mode) -#endif diff --git a/zip.c b/zip.c index 7f1117c..178bfd0 100644 --- a/zip.c +++ b/zip.c @@ -23,9 +23,6 @@ #include "tailor.h" #include "gzip.h" -#include <unistd.h> -#include <fcntl.h> - local ulg crc; /* crc on uncompressed file data */ off_t header_bytes; /* number of bytes in gzip header */ -- 2.5.0
>From 94757ff442298042a5613a4515612a7ad1ee4597 Mon Sep 17 00:00:00 2001 From: Paul Eggert <egg...@cs.ucla.edu> Date: Sat, 27 Feb 2016 14:33:17 -0800 Subject: [PATCH 2/4] gzip: use constants, not fileno * gzip.c (main, treat_stdin, treat_file, get_method) (check_ofname): Prefer STDIN_FILENO to fileno (stdin), and similarly for STDOUT_FILENO. --- gzip.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gzip.c b/gzip.c index 15bcf78..6cfc561 100644 --- a/gzip.c +++ b/gzip.c @@ -634,7 +634,7 @@ int main (int argc, char **argv) /* And get to work */ if (file_count != 0) { if (to_stdout && !test && !list && (!decompress || !ascii)) { - SET_BINARY_MODE(fileno(stdout)); + SET_BINARY_MODE (STDOUT_FILENO); } while (optind < argc) { treat_file(argv[optind++]); @@ -675,7 +675,7 @@ local void treat_stdin() { if (!force && !list && (presume_input_tty - || isatty(fileno((FILE *)(decompress ? stdin : stdout))))) { + || isatty (decompress ? STDIN_FILENO : STDOUT_FILENO))) { /* Do not send compressed data to the terminal or read it from * the terminal. We get here when user invoked the program * without parameters, so be helpful. According to the GNU standards: @@ -701,16 +701,16 @@ local void treat_stdin() } if (decompress || !ascii) { - SET_BINARY_MODE(fileno(stdin)); + SET_BINARY_MODE (STDIN_FILENO); } if (!test && !list && (!decompress || !ascii)) { - SET_BINARY_MODE(fileno(stdout)); + SET_BINARY_MODE (STDOUT_FILENO); } strcpy(ifname, "stdin"); strcpy(ofname, "stdout"); /* Get the file's time stamp and size. */ - if (fstat (fileno (stdin), &istat) != 0) + if (fstat (STDIN_FILENO, &istat) != 0) { progerror ("standard input"); do_exit (ERROR); @@ -728,7 +728,7 @@ local void treat_stdin() clear_bufs(); /* clear input and output buffers */ to_stdout = 1; part_nb = 0; - ifd = fileno(stdin); + ifd = STDIN_FILENO; if (decompress) { method = get_method(ifd); @@ -744,7 +744,8 @@ local void treat_stdin() /* Actually do the compression/decompression. Loop over zipped members. */ for (;;) { - if ((*work)(fileno(stdin), fileno(stdout)) != OK) return; + if (work (STDIN_FILENO, STDOUT_FILENO) != OK) + return; if (input_eof ()) break; @@ -931,7 +932,7 @@ local void treat_file(iname) * a new ofname and save the original name in the compressed file. */ if (to_stdout) { - ofd = fileno(stdout); + ofd = STDOUT_FILENO; /* Keep remove_ofname_fd negative. */ } else { if (create_outfile() != OK) return; @@ -1630,7 +1631,7 @@ local int get_method(in) inptr--; last_member = 1; if (imagic0 != EOF) { - write_buf(fileno(stdout), magic, 1); + write_buf (STDOUT_FILENO, magic, 1); bytes_out++; } } @@ -1844,7 +1845,7 @@ local int check_ofname() if (!force) { int ok = 0; fprintf (stderr, "%s: %s already exists;", program_name, ofname); - if (foreground && (presume_input_tty || isatty(fileno(stdin)))) { + if (foreground && (presume_input_tty || isatty (STDIN_FILENO))) { fprintf(stderr, " do you wish to overwrite (y or n)? "); fflush(stderr); ok = yesno(); -- 2.5.0
>From d09376128d896a3bbd79eea3d7b77fe6e0b9ea35 Mon Sep 17 00:00:00 2001 From: Paul Eggert <egg...@cs.ucla.edu> Date: Sat, 27 Feb 2016 23:58:54 -0800 Subject: [PATCH 3/4] gzip: new option --synchronous This follows up on the earlier patch to avoid data loss near the system crashes. It makes the new behavior optional, with the default off. See: http://bugs.gnu.org/22768 * NEWS, doc/gzip.texi (Sample, Invoking gzip), gunzip.in (usage): * gzip.1, zcat.in (usage): Document this. * gzip.c (synchronous): New static var. (SYNCHRONOUS_OPTION): New constant. (longopts, help, main, treat_file): Add support for --synchronous. --- NEWS | 12 ++++++++---- doc/gzip.texi | 11 +++++++++++ gunzip.in | 1 + gzip.1 | 6 ++++++ gzip.c | 28 +++++++++++++++++++++------- zcat.in | 1 + 6 files changed, 48 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index 31472cc..a3989af 100644 --- a/NEWS +++ b/NEWS @@ -4,14 +4,18 @@ GNU gzip NEWS -*- outline -*- ** Changes in behavior - When acting in-place, gzip now fsyncs the output before closing it. - This is slower, but on many file systems it is safer if the system - is about to crash. - The GZIP environment variable is now obsolescent; gzip now warns if it is used, and rejects attempts to use dangerous options or operands. You can use an alias or script instead. +** New features + + gzip now accepts the --synchronous option, which causes it to use + fsync and similar primitives to transfer output data to the output + file's storage device when the file system supports this. Although + this option makes gzip safer in the presence of system crashes, it + can make gzip considerably slower. + ** Bug fixes gzip -k -v no longer reports that files are replaced. diff --git a/doc/gzip.texi b/doc/gzip.texi index 978b919..fa94b84 100644 --- a/doc/gzip.texi +++ b/doc/gzip.texi @@ -204,6 +204,7 @@ Mandatory arguments to long options are mandatory for short options too. -q, --quiet suppress all warnings -r, --recursive operate recursively on directories -S, --suffix=SUF use suffix SUF on compressed files + --synchronous synchronous output (safer if system crashes, but slower) -t, --test test compressed file integrity -v, --verbose verbose mode -V, --version display version number @@ -374,6 +375,16 @@ gunzip -S "" * (*.* for MSDOS) Previous versions of gzip used the @samp{.z} suffix. This was changed to avoid a conflict with @command{pack}. +@item --synchronous +Use synchronous output, by transferring output data to the output +file's storage device when the file system supports this. Because +file system data can be cached, without this option if the system +crashes around the time a command like @samp{gzip FOO} is run the user +might lose both @file{FOO} and @file{FOO.gz}; this is the default with +@command{gzip}, just as it is the default with most applications that +move data. When this option is used, @command{gzip} is safer but can +be considerably slower. + @item --test @itemx -t Test. Check the compressed file integrity. diff --git a/gunzip.in b/gunzip.in index 1296834..d0efd2d 100644 --- a/gunzip.in +++ b/gunzip.in @@ -45,6 +45,7 @@ Mandatory arguments to long options are mandatory for short options too. -q, --quiet suppress all warnings -r, --recursive operate recursively on directories -S, --suffix=SUF use suffix SUF on compressed files + --synchronous synchronous output (safer if system crashes, but slower) -t, --test test compressed file integrity -v, --verbose verbose mode --help display this help and exit diff --git a/gzip.1 b/gzip.1 index 07995ea..3262a87 100644 --- a/gzip.1 +++ b/gzip.1 @@ -296,6 +296,12 @@ are transferred to other systems. When decompressing, add .suf to the beginning of the list of suffixes to try, when deriving an output file name from an input file name. .TP +.B --synchronous +Use synchronous output. With this option, +.I gzip +is less likely to lose data during a system crash, but it can be +considerably slower. +.TP .B \-t --test Test. Check the compressed file integrity. .TP diff --git a/gzip.c b/gzip.c index 6cfc561..d9cdfaa 100644 --- a/gzip.c +++ b/gzip.c @@ -159,6 +159,14 @@ DECLARE(uch, window, 2L*WSIZE); is deliberately not documented, and only for testing. */ static bool presume_input_tty; +/* If true, transfer output data to the output file's storage device + when supported. Otherwise, if the system crashes around the time + gzip is run, the user might lose both input and output data. See: + Pillai TS et al. All file systems are not created equal: on the + complexity of crafting crash-consistent applications. OSDI'14. 2014:433-48. + https://www.usenix.org/conference/osdi14/technical-sessions/presentation/pillai */ +static bool synchronous; + static int ascii = 0; /* convert end-of-lines to local OS conventions */ int to_stdout = 0; /* output to stdout (-c) */ static int decompress = 0; /* decompress (-d) */ @@ -240,6 +248,7 @@ static int handled_sig[] = enum { PRESUME_INPUT_TTY_OPTION = CHAR_MAX + 1, + SYNCHRONOUS_OPTION, /* A value greater than all valid long options, used as a flag to distinguish options derived from the GZIP environment variable. */ @@ -268,6 +277,7 @@ static const struct option longopts[] = {"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION}, {"quiet", 0, 0, 'q'}, /* quiet mode */ {"silent", 0, 0, 'q'}, /* quiet mode */ + {"synchronous",0, 0, SYNCHRONOUS_OPTION}, {"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 */ @@ -353,6 +363,7 @@ local void help() " -r, --recursive operate recursively on directories", #endif " -S, --suffix=SUF use suffix SUF on compressed files", + " --synchronous synchronous output (safer if system crashes, but slower)", " -t, --test test compressed file integrity", " -v, --verbose verbose mode", " -V, --version display version number", @@ -551,6 +562,9 @@ int main (int argc, char **argv) z_len = strlen(optarg); z_suffix = optarg; break; + case SYNCHRONOUS_OPTION: + synchronous = true; + break; case 't': test = decompress = to_stdout = 1; break; @@ -645,6 +659,12 @@ int main (int argc, char **argv) if (list && !quiet && file_count > 1) { do_list(-1, -1); /* print totals */ } + if (to_stdout + && ((synchronous + && (fdatasync (STDOUT_FILENO) != 0 && errno != EINVAL)) + || close (STDOUT_FILENO) != 0) + && errno != EBADF) + write_error (); do_exit(exit_code); return exit_code; /* just to avoid lint warning */ } @@ -972,13 +992,7 @@ local void treat_file(iname) { copy_stat (&istat); - /* If KEEP, transfer output data to the output file's storage device. - Otherwise, if the system crashed now the user might lose - both input and output data. See: Pillai TS et al. All - file systems are not created equal: on the complexity of - crafting crash-consistent applications. OSDI'14. 2014:433-48. - https://www.usenix.org/conference/osdi14/technical-sessions/presentation/pillai */ - if ((!keep + if ((synchronous && ((0 <= dfd && fdatasync (dfd) != 0 && errno != EINVAL) || (fsync (ofd) != 0 && errno != EINVAL))) || close (ofd) != 0) diff --git a/zcat.in b/zcat.in index be6577f..75660e5 100644 --- a/zcat.in +++ b/zcat.in @@ -39,6 +39,7 @@ Uncompress FILEs to standard output. -q, --quiet suppress all warnings -r, --recursive operate recursively on directories -S, --suffix=SUF use suffix SUF on compressed files + --synchronous synchronous output (safer if system crashes, but slower) -t, --test test compressed file integrity -v, --verbose verbose mode --help display this help and exit -- 2.5.0
>From e441ca6547c4034104a144395e1e3f3bffca93a5 Mon Sep 17 00:00:00 2001 From: Paul Eggert <egg...@cs.ucla.edu> Date: Sun, 28 Feb 2016 00:01:50 -0800 Subject: [PATCH 4/4] misc: update --version copyright * gunzip.in, gzexe.in, zcat.in, zdiff.in, zforce.in, zgrep.in: * zless.in, zmore.in, znew.in: Update copyright year in --version output to 2016. --- gunzip.in | 2 +- gzexe.in | 2 +- zcat.in | 2 +- zdiff.in | 2 +- zforce.in | 2 +- zgrep.in | 2 +- zless.in | 2 +- zmore.in | 2 +- znew.in | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gunzip.in b/gunzip.in index d0efd2d..d7edb53 100644 --- a/gunzip.in +++ b/gunzip.in @@ -24,7 +24,7 @@ esac PATH=$bindir:$PATH version="gunzip (gzip) @VERSION@ -Copyright (C) 2007, 2011-2015 Free Software Foundation, Inc. +Copyright (C) 2007, 2011-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/gzexe.in b/gzexe.in index d5b82a2..6b61ec4 100644 --- a/gzexe.in +++ b/gzexe.in @@ -37,7 +37,7 @@ nl=' IFS=" $tab$nl" version='gzexe (gzip) @VERSION@ -Copyright (C) 2007, 2011-2015 Free Software Foundation, Inc. +Copyright (C) 2007, 2011-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/zcat.in b/zcat.in index 75660e5..d8ffd85 100644 --- a/zcat.in +++ b/zcat.in @@ -24,7 +24,7 @@ esac PATH=$bindir:$PATH version="zcat (gzip) @VERSION@ -Copyright (C) 2007, 2011-2015 Free Software Foundation, Inc. +Copyright (C) 2007, 2011-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/zdiff.in b/zdiff.in index 60905d5..f1fa95f 100644 --- a/zdiff.in +++ b/zdiff.in @@ -31,7 +31,7 @@ case $1 in esac version="z$prog (gzip) @VERSION@ -Copyright (C) 2009, 2011-2015 Free Software Foundation, Inc. +Copyright (C) 2009, 2011-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/zforce.in b/zforce.in index e5d66ad..5baff07 100644 --- a/zforce.in +++ b/zforce.in @@ -30,7 +30,7 @@ esac PATH=$bindir:$PATH; export PATH version="zforce (gzip) @VERSION@ -Copyright (C) 2010-2015 Free Software Foundation, Inc. +Copyright (C) 2010-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/zgrep.in b/zgrep.in index 9668fae..d94b8d8 100644 --- a/zgrep.in +++ b/zgrep.in @@ -31,7 +31,7 @@ PATH=$bindir:$PATH grep='${GREP-'\''@GREP@'\''}' version='zgrep (gzip) @VERSION@ -Copyright (C) 2010-2015 Free Software Foundation, Inc. +Copyright (C) 2010-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/zless.in b/zless.in index 9c7d163..a3edcc7 100644 --- a/zless.in +++ b/zless.in @@ -23,7 +23,7 @@ esac PATH=$bindir:$PATH; export PATH version="zless (gzip) @VERSION@ -Copyright (C) 2007, 2011-2015 Free Software Foundation, Inc. +Copyright (C) 2007, 2011-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/zmore.in b/zmore.in index 5f5bb26..b1bb68f 100644 --- a/zmore.in +++ b/zmore.in @@ -24,7 +24,7 @@ esac PATH=$bindir:$PATH; export PATH version="zmore (gzip) @VERSION@ -Copyright (C) 2010-2015 Free Software Foundation, Inc. +Copyright (C) 2010-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. diff --git a/znew.in b/znew.in index 7f4a8eb..ec025d2 100644 --- a/znew.in +++ b/znew.in @@ -25,7 +25,7 @@ esac PATH=$bindir:$PATH; export PATH version="znew (gzip) @VERSION@ -Copyright (C) 2010-2015 Free Software Foundation, Inc. +Copyright (C) 2010-2016 Free Software Foundation, Inc. This is free software. You may redistribute copies of it under the terms of the GNU General Public License <http://www.gnu.org/licenses/gpl.html>. There is NO WARRANTY, to the extent permitted by law. -- 2.5.0