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