dovecot-2.2: lib-fs: fs-metawrap now supports adding more metada...

dovecot at dovecot.org dovecot at dovecot.org
Fri Nov 14 00:16:53 UTC 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/3eba1442493d
changeset: 18085:3eba1442493d
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Nov 14 02:00:12 2014 +0200
description:
lib-fs: fs-metawrap now supports adding more metadata fields while file is being saved.
This potentially adds yet another temporary file write though.

diffstat:

 src/lib-fs/fs-metawrap.c |  115 +++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 101 insertions(+), 14 deletions(-)

diffs (200 lines):

diff -r 4e10ca59e907 -r 3eba1442493d src/lib-fs/fs-metawrap.c
--- a/src/lib-fs/fs-metawrap.c	Fri Nov 14 01:19:37 2014 +0200
+++ b/src/lib-fs/fs-metawrap.c	Fri Nov 14 02:00:12 2014 +0200
@@ -6,9 +6,11 @@
 #include "strescape.h"
 #include "istream.h"
 #include "istream-private.h"
+#include "istream-concat.h"
 #include "istream-metawrap.h"
 #include "ostream.h"
 #include "ostream-metawrap.h"
+#include "iostream-temp.h"
 #include "fs-api-private.h"
 
 #define MAX_METADATA_LINE_LEN 8192
@@ -24,8 +26,13 @@
 	struct fs_file *super, *super_read;
 	enum fs_open_mode open_mode;
 	struct istream *input;
+	bool metadata_read;
+
 	struct ostream *super_output;
-	bool metadata_read;
+	struct ostream *temp_output;
+	string_t *metadata_header;
+	uoff_t metadata_write_size;
+	bool metadata_changed_since_write;
 };
 
 struct metawrap_fs_iter {
@@ -132,6 +139,8 @@
 
 	if (file->super_read != file->super && file->super_read != NULL)
 		fs_file_deinit(&file->super_read);
+	if (file->metadata_header != NULL)
+		str_free(&file->metadata_header);
 	fs_file_deinit(&file->super);
 	i_free(file->file.path);
 	i_free(file);
@@ -179,8 +188,10 @@
 
 	if (!file->fs->wrap_metadata)
 		fs_set_metadata(file->super, key, value);
-	else
+	else {
 		fs_default_set_metadata(_file, key, value);
+		file->metadata_changed_since_write = TRUE;
+	}
 }
 
 static int
@@ -269,15 +280,11 @@
 	return fs_write_via_stream(_file, data, size);
 }
 
-static void fs_metawrap_write_metadata(void *context)
+static void
+fs_metawrap_append_metadata(struct metawrap_fs_file *file, string_t *str)
 {
-	struct metawrap_fs_file *file = context;
 	const struct fs_metadata *metadata;
-	string_t *str = t_str_new(256);
-	ssize_t ret;
 
-	/* FIXME: if fs_set_metadata() is called later the changes are
-	   ignored. we'd need to write via temporary file then. */
 	array_foreach(&file->file.metadata, metadata) {
 		str_append_tabescaped(str, metadata->key);
 		str_append_c(str, ':');
@@ -285,11 +292,23 @@
 		str_append_c(str, '\n');
 	}
 	str_append_c(str, '\n');
+}
+
+static void fs_metawrap_write_metadata(void *context)
+{
+	struct metawrap_fs_file *file = context;
+	string_t *str = t_str_new(256);
+	ssize_t ret;
+
+	fs_metawrap_append_metadata(file, str);
+	file->metadata_write_size = str_len(str);
+
 	ret = o_stream_send(file->file.output, str_data(str), str_len(str));
 	if (ret < 0)
 		o_stream_close(file->file.output);
 	else
 		i_assert((size_t)ret == str_len(str));
+	file->metadata_changed_since_write = FALSE;
 }
 
 static void fs_metawrap_write_stream(struct fs_file *_file)
@@ -298,18 +317,46 @@
 
 	i_assert(_file->output == NULL);
 
-	file->super_output = fs_write_stream(file->super);
-	if (!file->fs->wrap_metadata)
+	if (!file->fs->wrap_metadata) {
+		file->super_output = fs_write_stream(file->super);
 		_file->output = file->super_output;
-	else {
-		_file->output = o_stream_create_metawrap(file->super_output,
+	} else {
+		file->temp_output =
+			iostream_temp_create_named(_file->fs->temp_path_prefix,
+						   IOSTREAM_TEMP_FLAG_TRY_FD_DUP,
+						   fs_file_path(_file));
+		_file->output = o_stream_create_metawrap(file->temp_output,
 			fs_metawrap_write_metadata, file);
 	}
 }
 
+static struct istream *
+fs_metawrap_create_updated_istream(struct metawrap_fs_file *file,
+				   struct istream *input)
+{
+	struct istream *input2, *inputs[3];
+
+	if (file->metadata_header != NULL)
+		str_truncate(file->metadata_header, 0);
+	else
+		file->metadata_header = str_new(default_pool, 1024);
+	fs_metawrap_append_metadata(file, file->metadata_header);
+	inputs[0] = i_stream_create_from_data(str_data(file->metadata_header),
+					       str_len(file->metadata_header));
+
+	i_stream_seek(input, file->metadata_write_size);
+	inputs[1] = i_stream_create_limit(input, (uoff_t)-1);
+	inputs[2] = NULL;
+	input2 = i_stream_create_concat(inputs);
+	i_stream_unref(&inputs[0]);
+	i_stream_unref(&inputs[1]);
+	return input2;
+}
+
 static int fs_metawrap_write_stream_finish(struct fs_file *_file, bool success)
 {
 	struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file;
+	struct istream *input;
 	int ret;
 
 	if (_file->output != NULL) {
@@ -320,13 +367,53 @@
 		else
 			o_stream_unref(&_file->output);
 	}
+	if (!success) {
+		if (file->temp_output != NULL)
+			o_stream_destroy(&file->temp_output);
+		if (file->super_output != NULL)
+			fs_write_stream_abort(file->super, &file->super_output);
+		return -1;
+	}
 
-	if (!success) {
+	if (file->super_output != NULL) {
+		/* no metawrap */
+		i_assert(file->temp_output == NULL);
+		return fs_write_stream_finish(file->super, &file->super_output);
+	}
+	if (file->temp_output == NULL) {
+		/* finishing up */
+		i_assert(file->super_output == NULL);
+		return fs_write_stream_finish(file->super, &file->super_output);
+	}
+	/* finish writing the temporary file */
+	input = iostream_temp_finish(&file->temp_output, IO_BLOCK_SIZE);
+	if (file->metadata_changed_since_write) {
+		/* we'll need to recreate the metadata. do this by creating a
+		   new istream combining the new metadata header and the
+		   old body. */
+		struct istream *input2 = input;
+
+		input = fs_metawrap_create_updated_istream(file, input);
+		i_stream_unref(&input2);
+	}
+	file->super_output = fs_write_stream(file->super);
+	if (o_stream_send_istream(file->super_output, input) >= 0)
+		ret = fs_write_stream_finish(file->super, &file->super_output);
+	else if (input->stream_errno != 0) {
+		fs_set_error(_file->fs, "read(%s) failed: %s",
+			     i_stream_get_name(input),
+			     i_stream_get_error(input));
 		fs_write_stream_abort(file->super, &file->super_output);
 		ret = -1;
 	} else {
-		ret = fs_write_stream_finish(file->super, &file->super_output);
+		i_assert(file->super_output->stream_errno != 0);
+		fs_set_error(_file->fs, "write(%s) failed: %s",
+			     o_stream_get_name(file->super_output),
+			     o_stream_get_error(file->super_output));
+		fs_write_stream_abort(file->super, &file->super_output);
+		ret = -1;
 	}
+	i_stream_unref(&input);
 	return ret;
 }
 


More information about the dovecot-cvs mailing list