>From d09376128d896a3bbd79eea3d7b77fe6e0b9ea35 Mon Sep 17 00:00:00 2001 From: Paul Eggert 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}. address@hidden --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 address@hidden, 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