From 1ac3e2c08bae4bd8e5e771dab94564d79638d414 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 13 Sep 2010 16:08:01 -0400 Subject: [PATCH] sponge: Ensure that output file permissions are always preserved if it already exists. --- debian/changelog | 2 ++ sponge.c | 58 +++++++++++++++++++++++++----------------------- sponge.docbook | 8 +++++-- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/debian/changelog b/debian/changelog index ae71b8c..9705e4b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,8 @@ moreutils (0.42) UNRELEASED; urgency=low * Typo. Closes: #596032 * sponge: Guarantee that output file is always updated atomically, by renaming a temp file into place. Closes: #592144 + * sponge: Ensure that output file permissions are always preserved + if it already exists. -- Joey Hess Wed, 08 Sep 2010 02:09:29 -0400 diff --git a/sponge.c b/sponge.c index f9a521d..4d5a1f4 100644 --- a/sponge.c +++ b/sponge.c @@ -262,7 +262,6 @@ int main (int argc, char **argv) { FILE *outfile, *tmpfile = 0; ssize_t i = 0; size_t mem_available = default_sponge_size(); - struct stat statbuf; if (argc > 2 || (argc == 2 && strcmp(argv[1], "-h") == 0)) { usage(); @@ -309,41 +308,44 @@ int main (int argc, char **argv) { } if (outname) { + mode_t mode; + struct stat statbuf; + int exists = (lstat(outname, &statbuf) == 0); + + /* Set temp file mode to match either + * the old file mode, or the default file + * mode for a newly created file. */ + if (exists) { + mode = statbuf.st_mode; + } + else { + mode_t mask = umask(0); + umask(mask); + mode = 0666 & ~mask; + } + if (chmod(tmpname, mode) != 0) { + perror("chmod"); + exit(1); + } + /* If it's a regular file, or does not yet exist, * attempt a fast rename of the temp file. */ - if (((lstat(outname, &statbuf) == 0 && + if (((exists && S_ISREG(statbuf.st_mode) && ! S_ISLNK(statbuf.st_mode) - ) || errno == ENOENT) && + ) || ! exists) && rename(tmpname, outname) == 0) { - tmpname=NULL; - /* Fix renamed file mode to match either - * the old file mode, or the default file - * mode for a newly created file. */ - mode_t mode; - if (errno != ENOENT) { - mode = statbuf.st_mode; - } - else { - mode_t mask = umask(0); - umask(mask); - mode = 0666 & ~mask; - } - if (chmod(outname, mode) != 0) { - perror("chmod"); + tmpname=NULL; /* don't try to cleanup tmpname */ + } + else { + /* Fall back to slow copy. */ + outfile = fopen(outname, "w"); + if (!outfile) { + perror("error opening output file"); exit(1); } - return(0); - } - - /* Fall back to slow copy. */ - outfile = fopen(outname, "w"); - if (!outfile) { - perror("error opening output file"); - exit(1); + copy_tmpfile(tmpfile, outfile, bufstart, bufsize); } - copy_tmpfile(tmpfile, outfile, bufstart, bufsize); - fclose(outfile); } else { copy_tmpfile(tmpfile, stdout, bufstart, bufsize); diff --git a/sponge.docbook b/sponge.docbook index ab2c42d..04447e3 100644 --- a/sponge.docbook +++ b/sponge.docbook @@ -58,8 +58,12 @@ USA redirect, sponge soaks up all its input before opening the output file. This allows constricting pipelines that read from and write to - the same file. It also creates the output file - atomically by renaming a temp file into place. + the same file. + + It also creates the output file + atomically by renaming a temp file into place. + If the output file is a special file or symlink, + the data will be written to it. If no output file is specified, sponge outputs to stdout. -- 2.39.5