[Patch v5 2/6] dump: when given output file name, write atomically

David Bremner david at tethera.net
Tue Apr 1 18:16:17 PDT 2014


It is useful to able to tell whether a dump completed successfully in
situtions where we don't have access to the return code.
---
 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);
+    if (ret) {
+	perror ("fdatasync");
+	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);
 
     return ret;
 }
-- 
1.9.0



More information about the notmuch mailing list