changeset 20851:c2c1190aa566

fs-metawrap: Don't assert-crash when trying to write an empty file. Fixes: Panic: file fs-metawrap.c: line 401 (fs_metawrap_write_stream_finish): assertion failed: (file->super_output->offset > 0 || file->super_output->stream_errno != 0)
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 10 Oct 2016 23:53:55 +0300
parents 1d96b08f9c78
children 1c4be30c9e9b
files src/lib-fs/fs-metawrap.c src/lib-fs/test-fs-metawrap.c
diffstat 2 files changed, 35 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-fs/fs-metawrap.c	Tue Oct 11 00:31:17 2016 +0300
+++ b/src/lib-fs/fs-metawrap.c	Mon Oct 10 23:53:55 2016 +0300
@@ -288,23 +288,31 @@
 	str_append_c(str, '\n');
 }
 
-static void fs_metawrap_write_metadata(void *context)
+static void
+fs_metawrap_write_metadata_to(struct metawrap_fs_file *file,
+			      struct ostream *output)
 {
-	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));
+	ret = o_stream_send(output, str_data(str), str_len(str));
 	if (ret < 0)
-		o_stream_close(file->file.output);
+		o_stream_close(output);
 	else
 		i_assert((size_t)ret == str_len(str));
 	file->metadata_changed_since_write = FALSE;
 }
 
+static void fs_metawrap_write_metadata(void *context)
+{
+	struct metawrap_fs_file *file = context;
+
+	fs_metawrap_write_metadata_to(file, file->file.output);
+}
+
 static void fs_metawrap_write_stream(struct fs_file *_file)
 {
 	struct metawrap_fs_file *file = (struct metawrap_fs_file *)_file;
@@ -384,6 +392,10 @@
 		return fs_write_stream_finish_async(_file->parent);
 	}
 	/* finish writing the temporary file */
+	if (file->temp_output->offset == 0) {
+		/* empty file */
+		fs_metawrap_write_metadata_to(file, file->temp_output);
+	}
 	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
--- a/src/lib-fs/test-fs-metawrap.c	Tue Oct 11 00:31:17 2016 +0300
+++ b/src/lib-fs/test-fs-metawrap.c	Mon Oct 10 23:53:55 2016 +0300
@@ -6,9 +6,10 @@
 #include "fs-test.h"
 #include "test-common.h"
 
+static const struct fs_settings fs_set;
+
 static void test_fs_metawrap_stat(void)
 {
-	struct fs_settings fs_set;
 	struct fs *fs;
 	struct fs_file *file;
 	struct test_fs_file *test_file;
@@ -19,7 +20,6 @@
 
 	test_begin("fs metawrap stat");
 
-	memset(&fs_set, 0, sizeof(fs_set));
 	if (fs_init("metawrap", "test", &fs_set, &fs, &error) < 0)
 		i_fatal("fs_init() failed: %s", error);
 
@@ -53,11 +53,28 @@
 	test_fs_async("double-metawrap", FS_PROPERTY_METADATA, "metawrap", "metawrap:test");
 }
 
+static void test_fs_metawrap_write_empty(void)
+{
+	struct fs *fs;
+	const char *error;
+
+	test_begin("fs metawrap write empty file");
+	if (fs_init("metawrap", "test", &fs_set, &fs, &error) < 0)
+		i_fatal("fs_init() failed: %s", error);
+	struct fs_file *file = fs_file_init(fs, "foo", FS_OPEN_MODE_REPLACE);
+	struct ostream *output = fs_write_stream(file);
+	test_assert(fs_write_stream_finish(file, &output) > 0);
+	fs_file_deinit(&file);
+	fs_deinit(&fs);
+	test_end();
+}
+
 int main(void)
 {
 	static void (*test_functions[])(void) = {
 		test_fs_metawrap_stat,
 		test_fs_metawrap_async,
+		test_fs_metawrap_write_empty,
 		NULL
 	};
 	return test_run(test_functions);