aboutsummaryrefslogtreecommitdiffhomepage
path: root/notmuch-dump.c
diff options
context:
space:
mode:
authorGravatar David Bremner <david@tethera.net>2014-03-28 22:14:51 -0300
committerGravatar David Bremner <david@tethera.net>2014-04-12 07:59:44 -0300
commit3c13bc0321baaf340663779d6fce2b1f34c1c2c3 (patch)
tree482e64a162df828622440c1818bf58624bff2435 /notmuch-dump.c
parent69867c33fa946514e9de6efff0541762b2755484 (diff)
dump: support gzipped and atomic output
The main goal is to support gzipped output for future internal calls (e.g. from notmuch-new) to notmuch_database_dump. The additional dependency is not very heavy since xapian already pulls in zlib. We want the dump to be "atomic", in the sense that after running the dump file is either present and complete, or not present. This avoids certain classes of mishaps involving overwriting a good backup with a bad or partial one.
Diffstat (limited to 'notmuch-dump.c')
-rw-r--r--notmuch-dump.c101
1 files changed, 82 insertions, 19 deletions
diff --git a/notmuch-dump.c b/notmuch-dump.c
index 21702d79..2849eaba 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -21,9 +21,11 @@
#include "notmuch-client.h"
#include "hex-escape.h"
#include "string-util.h"
+#include <zlib.h>
+
static int
-database_dump_file (notmuch_database_t *notmuch, FILE *output,
+database_dump_file (notmuch_database_t *notmuch, gzFile output,
const char *query_str, int output_format)
{
notmuch_query_t *query;
@@ -69,7 +71,7 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
}
if (output_format == DUMP_FORMAT_SUP) {
- fprintf (output, "%s (", message_id);
+ gzprintf (output, "%s (", message_id);
}
for (tags = notmuch_message_get_tags (message);
@@ -78,12 +80,12 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
const char *tag_str = notmuch_tags_get (tags);
if (! first)
- fputs (" ", output);
+ gzputs (output, " ");
first = 0;
if (output_format == DUMP_FORMAT_SUP) {
- fputs (tag_str, output);
+ gzputs (output, tag_str);
} else {
if (hex_encode (notmuch, tag_str,
&buffer, &buffer_size) != HEX_SUCCESS) {
@@ -91,12 +93,12 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
tag_str);
return EXIT_FAILURE;
}
- fprintf (output, "+%s", buffer);
+ gzprintf (output, "+%s", buffer);
}
}
if (output_format == DUMP_FORMAT_SUP) {
- fputs (")\n", output);
+ gzputs (output, ")\n");
} else {
if (make_boolean_term (notmuch, "id", message_id,
&buffer, &buffer_size)) {
@@ -104,7 +106,7 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
message_id, strerror (errno));
return EXIT_FAILURE;
}
- fprintf (output, " -- %s\n", buffer);
+ gzprintf (output, " -- %s\n", buffer);
}
notmuch_message_destroy (message);
@@ -121,24 +123,83 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
int
notmuch_database_dump (notmuch_database_t *notmuch,
const char *output_file_name,
- const char *query_str, dump_format_t output_format)
+ const char *query_str,
+ dump_format_t output_format,
+ notmuch_bool_t gzip_output)
{
- FILE *output = stdout;
- int ret;
+ gzFile output = NULL;
+ const char *mode = gzip_output ? "w9" : "wT";
+ const char *name_for_error = output_file_name ? output_file_name : "stdout";
+
+ char *tempname = NULL;
+ int outfd = -1;
+
+ int ret = -1;
if (output_file_name) {
- output = fopen (output_file_name, "w");
- if (output == NULL) {
- fprintf (stderr, "Error opening %s for writing: %s\n",
- output_file_name, strerror (errno));
- return EXIT_FAILURE;
- }
+ tempname = talloc_asprintf (notmuch, "%s.XXXXXX", output_file_name);
+ outfd = mkstemp (tempname);
+ } else {
+ outfd = dup (STDOUT_FILENO);
+ }
+
+ 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",
+ name_for_error, strerror (errno));
+ if (close (outfd))
+ fprintf (stderr, "Error closing %s during shutdown: %s\n",
+ name_for_error, strerror (errno));
+ goto DONE;
}
ret = database_dump_file (notmuch, output, query_str, output_format);
+ if (ret) goto DONE;
+
+ ret = gzflush (output, Z_FINISH);
+ if (ret) {
+ fprintf (stderr, "Error flushing output: %s\n", gzerror (output, NULL));
+ goto DONE;
+ }
+
+ if (output_file_name) {
+ ret = fdatasync (outfd);
+ if (ret) {
+ fprintf (stderr, "Error syncing %s to disk: %s\n",
+ name_for_error, strerror (errno));
+ goto DONE;
+ }
+ }
+
+ if (gzclose_w (output) != Z_OK) {
+ fprintf (stderr, "Error closing %s: %s\n", name_for_error,
+ gzerror (output, NULL));
+ ret = EXIT_FAILURE;
+ output = NULL;
+ goto DONE;
+ }
+
+ if (output_file_name) {
+ ret = rename (tempname, output_file_name);
+ if (ret) {
+ fprintf (stderr, "Error renaming %s to %s: %s\n",
+ tempname, output_file_name, strerror (errno));
+ goto DONE;
+ }
+
+ }
+ DONE:
+ if (ret != EXIT_SUCCESS && output)
+ (void) gzclose_w (output);
- if (output != stdout)
- fclose (output);
+ if (ret != EXIT_SUCCESS && output_file_name)
+ (void) unlink (tempname);
return ret;
}
@@ -158,6 +219,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
int opt_index;
int output_format = DUMP_FORMAT_BATCH_TAG;
+ notmuch_bool_t gzip_output = 0;
notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, &output_format, "format", 'f',
@@ -165,6 +227,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
{ "batch-tag", DUMP_FORMAT_BATCH_TAG },
{ 0, 0 } } },
{ NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 },
+ { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 },
{ 0, 0, 0, 0, 0 }
};
@@ -181,7 +244,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
}
ret = notmuch_database_dump (notmuch, output_file_name, query_str,
- output_format);
+ output_format, gzip_output);
notmuch_database_destroy (notmuch);