dovecot-2.2: lib-fs: Added "randomfail" driver.

dovecot at dovecot.org dovecot at dovecot.org
Tue Jun 16 13:43:20 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/c6ed48c7f2a0
changeset: 18867:c6ed48c7f2a0
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Jun 16 16:40:29 2015 +0300
description:
lib-fs: Added "randomfail" driver.
Using this in front of fs drivers allows randomly injecting failures. For
example:

mail_attachment_fs = randomfail:all=10,read=30,read-range=2000-3000:sis posix

This means that all FS operations have a 10% chance of failing, except reads
have a 30% chance of failing. If the read fails, it'll fail somewhere
between offsets 2000-3000 (the default is 0, so it'll fail at the start of
file).

The supported operations are: wait metadata prefetch read write lock exists
stat copy rename delete iter. "all" applies to all of them.

The supported ranges are: read-range, write-range, iter-range.

diffstat:

 src/lib-fs/Makefile.am      |    1 +
 src/lib-fs/fs-api-private.h |    1 +
 src/lib-fs/fs-api.c         |    1 +
 src/lib-fs/fs-randomfail.c  |  551 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 554 insertions(+), 0 deletions(-)

diffs (truncated from 588 to 300 lines):

diff -r 1c7288c054b1 -r c6ed48c7f2a0 src/lib-fs/Makefile.am
--- a/src/lib-fs/Makefile.am	Tue Jun 16 16:22:18 2015 +0300
+++ b/src/lib-fs/Makefile.am	Tue Jun 16 16:40:29 2015 +0300
@@ -8,6 +8,7 @@
 libfs_la_SOURCES = \
 	fs-api.c \
 	fs-metawrap.c \
+	fs-randomfail.c \
 	fs-posix.c \
 	fs-sis.c \
 	fs-sis-common.c \
diff -r 1c7288c054b1 -r c6ed48c7f2a0 src/lib-fs/fs-api-private.h
--- a/src/lib-fs/fs-api-private.h	Tue Jun 16 16:22:18 2015 +0300
+++ b/src/lib-fs/fs-api-private.h	Tue Jun 16 16:40:29 2015 +0300
@@ -135,6 +135,7 @@
 };
 
 extern const struct fs fs_class_posix;
+extern const struct fs fs_class_randomfail;
 extern const struct fs fs_class_metawrap;
 extern const struct fs fs_class_sis;
 extern const struct fs fs_class_sis_queue;
diff -r 1c7288c054b1 -r c6ed48c7f2a0 src/lib-fs/fs-api.c
--- a/src/lib-fs/fs-api.c	Tue Jun 16 16:22:18 2015 +0300
+++ b/src/lib-fs/fs-api.c	Tue Jun 16 16:40:29 2015 +0300
@@ -64,6 +64,7 @@
 {
 	i_array_init(&fs_classes, 8);
 	fs_class_register(&fs_class_posix);
+	fs_class_register(&fs_class_randomfail);
 	fs_class_register(&fs_class_metawrap);
 	fs_class_register(&fs_class_sis);
 	fs_class_register(&fs_class_sis_queue);
diff -r 1c7288c054b1 -r c6ed48c7f2a0 src/lib-fs/fs-randomfail.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-fs/fs-randomfail.c	Tue Jun 16 16:40:29 2015 +0300
@@ -0,0 +1,551 @@
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "istream-private.h"
+#include "istream-concat.h"
+#include "istream-failure-at.h"
+#include "ostream-failure-at.h"
+#include "ostream.h"
+#include "fs-api-private.h"
+
+#include <stdlib.h>
+
+#define RANDOMFAIL_ERROR "Random failure injection"
+
+enum fs_op {
+	FS_OP_WAIT,
+	FS_OP_METADATA,
+	FS_OP_PREFETCH,
+	FS_OP_READ,
+	FS_OP_WRITE,
+	FS_OP_LOCK,
+	FS_OP_EXISTS,
+	FS_OP_STAT,
+	FS_OP_COPY,
+	FS_OP_RENAME,
+	FS_OP_DELETE,
+	FS_OP_ITER,
+
+	FS_OP_COUNT
+};
+static const char *fs_op_names[FS_OP_COUNT] = {
+	"wait", "metadata", "prefetch", "read", "write", "lock", "exists",
+	"stat", "copy", "rename", "delete", "iter"
+};
+
+struct randomfail_fs {
+	struct fs fs;
+	unsigned int op_probability[FS_OP_COUNT];
+	uoff_t range_start[FS_OP_COUNT], range_end[FS_OP_COUNT];
+};
+
+struct randomfail_fs_file {
+	struct fs_file file;
+	struct fs_file *super, *super_read;
+	struct istream *input;
+
+	struct ostream *super_output;
+};
+
+struct randomfail_fs_iter {
+	struct fs_iter iter;
+	struct fs_iter *super;
+	unsigned int fail_pos;
+};
+
+static struct fs *fs_randomfail_alloc(void)
+{
+	struct randomfail_fs *fs;
+
+	fs = i_new(struct randomfail_fs, 1);
+	fs->fs = fs_class_randomfail;
+	return &fs->fs;
+}
+
+static bool fs_op_find(const char *str, enum fs_op *op_r)
+{
+	enum fs_op op;
+
+	for (op = 0; op < FS_OP_COUNT; op++) {
+		if (strcmp(fs_op_names[op], str) == 0) {
+			*op_r = op;
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static bool
+fs_randomfail_add_probability(struct randomfail_fs *fs,
+			      const char *key, const char *value,
+			      const char **error_r)
+{
+	unsigned int num;
+	enum fs_op op;
+	bool invalid_value = FALSE;
+
+	if (str_to_uint(value, &num) < 0 || num > 100)
+		invalid_value = TRUE;
+	if (fs_op_find(key, &op)) {
+		if (invalid_value) {
+			*error_r = "Invalid probability value";
+			return -1;
+		}
+		fs->op_probability[op] = num;
+		return 1;
+	}
+	if (strcmp(key, "all") == 0) {
+		if (invalid_value) {
+			*error_r = "Invalid probability value";
+			return -1;
+		}
+		for (op = 0; op < FS_OP_COUNT; op++)
+			fs->op_probability[op] = num;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+fs_randomfail_add_probability_range(struct randomfail_fs *fs,
+				    const char *key, const char *value,
+				    const char **error_r)
+{
+	enum fs_op op;
+	const char *p;
+	uoff_t num1, num2;
+
+	if (strcmp(key, "read-range") == 0)
+		op = FS_OP_READ;
+	else if (strcmp(key, "write-range") == 0)
+		op = FS_OP_WRITE;
+	else if (strcmp(key, "iter-range") == 0)
+		op = FS_OP_ITER;
+	else
+		return 0;
+
+	p = strchr(value, '-');
+	if (p == NULL) {
+		if (str_to_uoff(value, &num1) < 0) {
+			*error_r = "Invalid range value";
+			return -1;
+		}
+		num2 = num1;
+	} else if (str_to_uoff(t_strdup_until(value, p), &num1) < 0 ||
+		   str_to_uoff(p+1, &num2) < 0 || num1 > num2) {
+		*error_r = "Invalid range values";
+		return -1;
+	}
+	fs->range_start[op] = num1;
+	fs->range_end[op] = num2;
+	return 1;
+}
+
+static int fs_randomfail_parse_params(struct randomfail_fs *fs,
+				      const char *params, const char **error_r)
+{
+	const char *const *tmp;
+	int ret;
+
+	for (tmp = t_strsplit_spaces(params, ","); *tmp != NULL; tmp++) {
+		const char *key = *tmp;
+		const char *value = strchr(key, '=');
+
+		if (value == NULL) {
+			*error_r = "Missing '='";
+			return -1;
+		}
+		key = t_strdup_until(key, value++);
+		if ((ret = fs_randomfail_add_probability(fs, key, value, error_r)) != 0) {
+			if (ret < 0)
+				return -1;
+			continue;
+		}
+		if ((ret = fs_randomfail_add_probability_range(fs, key, value, error_r)) != 0) {
+			if (ret < 0)
+				return -1;
+			continue;
+		}
+		*error_r = t_strdup_printf("Unknown key '%s'", key);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+fs_randomfail_init(struct fs *_fs, const char *args,
+		   const struct fs_settings *set)
+{
+	struct randomfail_fs *fs = (struct randomfail_fs *)_fs;
+	const char *p, *parent_name, *parent_args, *error;
+
+	p = strchr(args, ':');
+	if (p == NULL) {
+		fs_set_error(_fs, "Randomfail parameters missing");
+		return -1;
+	}
+	if (fs_randomfail_parse_params(fs, t_strdup_until(args, p++), &error) < 0) {
+		fs_set_error(_fs, "Invalid randomfail parameters: %s", error);
+		return -1;
+	}
+	args = p;
+
+	if (*args == '\0') {
+		fs_set_error(_fs, "Parent filesystem not given as parameter");
+		return -1;
+	}
+
+	parent_args = strchr(args, ':');
+	if (parent_args == NULL) {
+		parent_name = args;
+		parent_args = "";
+	} else {
+		parent_name = t_strdup_until(args, parent_args);
+		parent_args++;
+	}
+	if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) {
+		fs_set_error(_fs, "%s: %s", parent_name, error);
+		return -1;
+	}
+	return 0;
+}
+
+static void fs_randomfail_deinit(struct fs *_fs)
+{
+	struct randomfail_fs *fs = (struct randomfail_fs *)_fs;
+
+	if (_fs->parent != NULL)
+		fs_deinit(&_fs->parent);
+	i_free(fs);
+}
+
+static enum fs_properties fs_randomfail_get_properties(struct fs *_fs)
+{
+	return fs_get_properties(_fs->parent);
+}
+
+static struct fs_file *
+fs_randomfail_file_init(struct fs *_fs, const char *path,
+			enum fs_open_mode mode, enum fs_open_flags flags)
+{
+	struct randomfail_fs_file *file;
+
+	file = i_new(struct randomfail_fs_file, 1);
+	file->file.fs = _fs;
+	file->file.path = i_strdup(path);
+	file->super = fs_file_init(_fs->parent, path, mode | flags);
+	return &file->file;
+}
+
+static void fs_randomfail_file_deinit(struct fs_file *_file)
+{
+	struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
+
+	fs_file_deinit(&file->super);
+	i_free(file->file.path);
+	i_free(file);
+}
+
+static void fs_randomfail_file_close(struct fs_file *_file)
+{
+	struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
+
+	fs_file_close(file->super);
+}
+
+static const char *fs_randomfail_file_get_path(struct fs_file *_file)
+{
+	struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
+
+	return fs_file_path(file->super);
+}
+


More information about the dovecot-cvs mailing list