--- gzip-1.3.5/gzip.1.fsync 2002-09-30 09:58:04.000000000 +0300 +++ gzip-1.3.5/gzip.1 2006-10-01 14:00:42.806162920 +0300 @@ -4,7 +4,7 @@ gzip, gunzip, zcat \- compress or expand .SH SYNOPSIS .ll +8 .B gzip -.RB [ " \-acdfhlLnNrtvV19 " ] +.RB [ " \-acdfFhklLnNrtvV19 " ] .RB [ \-S\ suffix ] [ .I "name \&..." @@ -12,14 +12,14 @@ gzip, gunzip, zcat \- compress or expand .ll -8 .br .B gunzip -.RB [ " \-acfhlLnNrtvV " ] +.RB [ " \-acfFhklLnNrtvV " ] .RB [ \-S\ suffix ] [ .I "name \&..." ] .br .B zcat -.RB [ " \-fhLV " ] +.RB [ " \-fFhLV " ] [ .I "name \&..." ] @@ -221,9 +221,23 @@ and when not running in the background, .I gzip prompts to verify whether an existing file should be overwritten. .TP +.B \-F --fsync +Do fsync() for output file before closing it so you don't end up with +corrupted output file (and possibly lost input file) if there's a system +malfunction / power outage. +NOTE: if environment variable +.I +TAR_WANTS_FSYNC +is set (when using +the appropriately patched tar) it enables fsync in gzip. +Fsync is done only for regular files and block devices. +.TP .B \-h --help Display a help screen and quit. .TP +.B \-k --keep +Keep (don't delete) input files +.TP .B \-l --list For each compressed file, list the following fields: --- gzip-1.3.9/gzip.c.bak 2007-01-16 17:29:36.881490000 +0200 +++ gzip-1.3.9/gzip.c 2007-01-16 20:45:15.813729224 +0200 @@ -63,6 +63,8 @@ static char rcsid[] = "$Id: gzip.c,v 1.1 #include #include #include +#include +#include #include "tailor.h" #include "gzip.h" @@ -191,6 +193,8 @@ int ascii = 0; /* convert end-of- int to_stdout = 0; /* output to stdout (-c) */ int decompress = 0; /* decompress (-d) */ int force = 0; /* don't ask questions, compress links (-f) */ +int do_fsync = 0; /* fsync output files */ +int keep_input = 0; /* keep (don't delete) input files when compressing/decompressing */ int no_name = -1; /* don't save or restore the original file name */ int no_time = -1; /* don't save or restore the original file time */ int recursive = 0; /* recurse through directories (-r) */ @@ -250,7 +254,9 @@ struct option longopts[] = {"uncompress", 0, 0, 'd'}, /* decompress */ /* {"encrypt", 0, 0, 'e'}, encrypt */ {"force", 0, 0, 'f'}, /* force overwrite of output file */ + {"fsync", 0, 0, 'F'}, /* fsync output files */ {"help", 0, 0, 'h'}, /* give help */ + {"keep", 0, 0, 'k'}, /* keep (don't delete) input files */ /* {"pkzip", 0, 0, 'k'}, force output in pkzip format */ {"list", 0, 0, 'l'}, /* list .gz file contents */ {"license", 0, 0, 'L'}, /* display software license */ @@ -294,6 +300,7 @@ local void install_signal_handlers OF((v local void remove_output_file OF((void)); local RETSIGTYPE abort_gzip_signal OF((int)); local void do_exit OF((int exitcode)) ATTRIBUTE_NORETURN; +local int try_fsync OF((int fd)); int main OF((int argc, char **argv)); int (*work) OF((int infile, int outfile)) = zip; /* function to call */ @@ -326,8 +333,10 @@ local void help() " -d, --decompress decompress", /* -e, --encrypt encrypt */ " -f, --force force overwrite of output file and compress links", + " -F, --fsync fsync output files prior to closing", " -h, --help give this help", /* -k, --pkzip force output in pkzip format */ + " -k, --keep keep input files (do not delete)", " -l, --list list compressed file contents", " -L, --license display software license", #ifdef UNDOCUMENTED @@ -389,6 +398,32 @@ local void progerror (string) exit_code = ERROR; } +local int try_fsync (int fd) +{ + struct stat fst; + int ret; + int e; + + if (!do_fsync) return 0; + + if (fstat(fd, &fst) == -1) return -1; + if (!S_ISREG(fst.st_mode) && !S_ISBLK(fst.st_mode)) return 0; + if (verbose) { + fprintf(stderr, "fsync..."); + } + ret = fsync(fd); + if (verbose) { + e = errno; + if (ret != 0) { + fprintf(stderr, " failed "); + } else { + fprintf(stderr, " OK "); + } + errno = e; + } + return ret; +} + /* ======================================================================== */ int main (argc, argv) int argc; @@ -430,7 +465,7 @@ int main (argc, argv) z_suffix = Z_SUFFIX; z_len = strlen(z_suffix); - while ((optc = getopt_long (argc, argv, "ab:cdfhH?lLmMnNqrS:tvVZ123456789", + while ((optc = getopt_long (argc, argv, "ab:cdfFhH?klLmMnNqrS:tvVZ123456789", longopts, (int *)0)) != -1) { switch (optc) { case 'a': @@ -451,8 +486,12 @@ int main (argc, argv) decompress = 1; break; case 'f': force++; break; + case 'F': + do_fsync = 1; break; case 'h': case 'H': help(); do_exit(OK); break; + case 'k': + keep_input = 1; break; case 'l': list = decompress = to_stdout = 1; break; case 'L': @@ -549,6 +588,10 @@ int main (argc, argv) exiting_signal = quiet ? SIGPIPE : 0; install_signal_handlers (); + if (getenv("TAR_WANTS_FSYNC")) { + do_fsync = 1; + } + /* And get to work */ if (file_count != 0) { if (to_stdout && !test && !list && (!decompress || !ascii)) { @@ -591,6 +634,8 @@ input_eof () */ local void treat_stdin() { + struct stat fst; + if (!force && !list && isatty(fileno((FILE *)(decompress ? stdin : stdout)))) { /* Do not send compressed data to the terminal or read it from @@ -667,6 +712,12 @@ local void treat_stdin() bytes_out = 0; /* required for length check */ } + if (try_fsync(fileno(stdout)) != 0) write_error(); + if (fstat(fileno(stdout), &fst) == -1) write_error(); + if (S_ISREG(fst.st_mode) || S_ISBLK(fst.st_mode)) { + if (close(fileno(stdout)) != 0) write_error(); + } + if (verbose) { if (test) { fprintf(stderr, " OK\n"); @@ -829,29 +880,37 @@ local void treat_file(iname) if (close (ifd) != 0) read_error (); + if (try_fsync(ofd) != 0) + write_error(); + if (!to_stdout) { sigset_t oldset; int unlink_errno; copy_stat (&istat); - if (close (ofd) != 0) - write_error (); - - sigprocmask (SIG_BLOCK, &caught_signals, &oldset); - remove_ofname_fd = -1; - unlink_errno = xunlink (ifname) == 0 ? 0 : errno; - sigprocmask (SIG_SETMASK, &oldset, NULL); - - if (unlink_errno) - { - WARN ((stderr, "%s: ", program_name)); - if (!quiet) - { - errno = unlink_errno; - perror (ifname); - } - } + if (close(ofd) != 0) + write_error(); + if (!keep_input) + { + sigprocmask (SIG_BLOCK, &caught_signals, &oldset); + remove_ofname_fd = -1; + unlink_errno = xunlink (ifname) == 0 ? 0 : errno; + sigprocmask (SIG_SETMASK, &oldset, NULL); + + if (unlink_errno) + { + WARN ((stderr, "%s: ", program_name)); + if (!quiet) + { + errno = unlink_errno; + perror (ifname); + } + } + } + } else { + if (close(ofd) != 0) + write_error(); } if (method == -1) { @@ -870,7 +929,7 @@ local void treat_file(iname) display_ratio(bytes_in-(bytes_out-header_bytes), bytes_in, stderr); } if (!test && !to_stdout) { - fprintf(stderr, " -- replaced with %s", ofname); + fprintf(stderr, " -- %s %s", (!keep_input) ? "replaced with" : "wrote", ofname); } fprintf(stderr, "\n"); }