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

Reply via email to