[Patch v5 2/6] dump: when given output file name, write atomically
Austin Clements
amdragon at MIT.EDU
Tue Apr 1 19:07:13 PDT 2014
(Pardon the mobile review)
On Apr 1, 2014 9:16 PM, David Bremner <david at tethera.net> wrote:
>
> It is useful to able to tell whether a dump completed successfully in
> situtions where we don't have access to the return code.
"Situations." This commit message doesn't seem very related to atomicity?
> ---
> notmuch-dump.c | 61 +++++++++++++++++++++++++++++++++++++++++++++-------------
> 1 file changed, 48 insertions(+), 13 deletions(-)
>
> diff --git a/notmuch-dump.c b/notmuch-dump.c
> index 28342b7..05ed6b4 100644
> --- a/notmuch-dump.c
> +++ b/notmuch-dump.c
> @@ -129,30 +129,65 @@ notmuch_database_dump (notmuch_database_t *notmuch,
> {
> gzFile output;
> const char *mode = gzip_output ? "w9" : "wT";
> + const char *name_for_error = output_file_name ? output_file_name : "stdout";
>
> - int ret;
> + char *tempname = NULL;
> + int outfd = -1;
> +
> + int ret = -1;
> +
> + if (output_file_name) {
> + tempname = talloc_asprintf (notmuch, "%s.XXXXXX", output_file_name);
> + outfd = mkstemp (tempname);
> + } else {
> + outfd = fileno (stdout);
> + }
>
> - if (output_file_name)
> - output = gzopen (output_file_name, mode);
> - else
> - output = gzdopen (fileno (stdout), mode);
> + if (outfd < 0) {
> + fprintf (stderr, "Bad output file %s\n", name_for_error);
> + goto DONE;
> + }
> +
> + output = gzdopen (outfd, mode);
>
> if (output == NULL) {
> fprintf (stderr, "Error opening %s for (gzip) writing: %s\n",
> - output_file_name ? output_file_name : "stdout", strerror (errno));
> - return EXIT_FAILURE;
> + name_for_error, strerror (errno));
> + goto DONE;
> }
>
> ret = database_dump_file (notmuch, output, query_str, output_format);
> + if (ret) goto DONE;
>
> - if (gzflush (output, Z_FINISH)) {
> - fprintf (stderr, "Error flushing output: %s\n",
> - gzerror (output, NULL));
> - return EXIT_FAILURE;
> + ret = gzflush (output, Z_FINISH);
> + if (ret) {
> + fprintf (stderr, "Error flushing output: %s\n", gzerror (output, NULL));
> + goto DONE;
> }
>
> - if (output_file_name)
> - gzclose_w (output);
> + ret = fdatasync (outfd);
This may fail if outfd isn't a regular file. We probably should only do this if output_file_name. Or we could ignore EINVAL (but that may mask bugs).
> + if (ret) {
> + perror ("fdatasync");
perror's fine for quick hacks, but it would be better to give a real error message here.
> + goto DONE;
> + }
> +
> + if (output_file_name) {
> + ret = gzclose_w (output);
> + if (ret != Z_OK) {
> + ret = EXIT_FAILURE;
> + goto DONE;
> + }
> +
> + ret = rename (tempname, output_file_name);
> + if (ret) {
> + perror ("rename");
> + goto DONE;
> + }
> +
> + }
> + DONE:
> + if (ret != EXIT_SUCCESS && output_file_name)
> + (void) unlink (tempname);
We need to gzclose on the error paths to free zlib's internal resources. This is a problem if we handed stdout to gzdopen. We shouldn't write any more to stdout anyway in that case, but maybe it would be better to dup stdout?
>
> return ret;
> }
> --
> 1.9.0
>
> _______________________________________________
> notmuch mailing list
> notmuch at notmuchmail.org
> http://notmuchmail.org/mailman/listinfo/notmuch
More information about the notmuch
mailing list