bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen#define RANDOMFAIL_ERROR "Random failure injection"
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic const char *fs_op_names[FS_OP_COUNT] = {
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen "wait", "metadata", "prefetch", "read", "write", "lock", "exists",
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen uoff_t range_start[FS_OP_COUNT], range_end[FS_OP_COUNT];
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic bool fs_op_find(const char *str, enum fs_op *op_r)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenfs_randomfail_add_probability(struct randomfail_fs *fs,
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen const char **error_r)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen if (str_to_uint(value, &num) < 0 || num > 100)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenfs_randomfail_add_probability_range(struct randomfail_fs *fs,
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen const char **error_r)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen const char *p;
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen } else if (str_to_uoff(t_strdup_until(value, p), &num1) < 0 ||
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_parse_params(struct randomfail_fs *fs,
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen for (tmp = t_strsplit_spaces(params, ","); *tmp != NULL; tmp++) {
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen if ((ret = fs_randomfail_add_probability(fs, key, value, error_r)) != 0) {
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen if ((ret = fs_randomfail_add_probability_range(fs, key, value, error_r)) != 0) {
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen *error_r = t_strdup_printf("Unknown key '%s'", key);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenfs_randomfail_init(struct fs *_fs, const char *args,
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs *fs = (struct randomfail_fs *)_fs;
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen const char *p, *parent_name, *parent_args, *error;
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen fs_set_error(_fs, "Randomfail parameters missing");
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen if (fs_randomfail_parse_params(fs, t_strdup_until(args, p++), &error) < 0) {
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen fs_set_error(_fs, "Invalid randomfail parameters: %s", error);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen fs_set_error(_fs, "Parent filesystem not given as parameter");
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen parent_name = t_strdup_until(args, parent_args);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen if (fs_init(parent_name, parent_args, set, &_fs->parent, &error) < 0) {
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic void fs_randomfail_deinit(struct fs *_fs)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs *fs = (struct randomfail_fs *)_fs;
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic enum fs_properties fs_randomfail_get_properties(struct fs *_fs)
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainenstatic struct fs_file *fs_randomfail_file_alloc(void)
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen struct randomfail_fs_file *file = i_new(struct randomfail_fs_file, 1);
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainenfs_randomfail_file_init(struct fs_file *_file, const char *path,
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen enum fs_open_mode mode, enum fs_open_flags flags)
86ad48a2e740bd63a9b7299d7f3e229831d9b303Timo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
6d1218e68ce883735ffde9d7907e626ab81b9fb5Timo Sirainen file->file.parent = fs_file_init_parent(_file, path, mode | flags);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic void fs_randomfail_file_deinit(struct fs_file *_file)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainenstatic bool fs_random_fail(struct fs *_fs, int divider, enum fs_op op)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs *fs = (struct randomfail_fs *)_fs;
191153d1a5b0eb0c129139570e3aa5212f28d2acJosef 'Jeff' Sipek if ((unsigned int)i_rand_limit(100 * divider) <= fs->op_probability[op]) {
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainenfs_file_random_fail_begin(struct randomfail_fs_file *file, enum fs_op op)
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainenfs_file_random_fail_end(struct randomfail_fs_file *file,
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenfs_random_fail_range(struct fs *_fs, enum fs_op op, uoff_t *offset_r)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs *fs = (struct randomfail_fs *)_fs;
191153d1a5b0eb0c129139570e3aa5212f28d2acJosef 'Jeff' Sipek *offset_r = i_rand_minmax(fs->range_start[op], fs->range_end[op]);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenfs_randomfail_get_metadata(struct fs_file *_file,
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_file_random_fail_begin(file, FS_OP_METADATA))
19126597f92e5cdde901ac472090aa423e0e5541Timo Sirainen ret = fs_get_metadata(_file->parent, metadata_r);
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen return fs_file_random_fail_end(file, ret, FS_OP_METADATA);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic bool fs_randomfail_prefetch(struct fs_file *_file, uoff_t length)
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_random_fail(_file->fs, 1, FS_OP_PREFETCH))
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic ssize_t fs_randomfail_read(struct fs_file *_file, void *buf, size_t size)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_file_random_fail_begin(file, FS_OP_READ))
d7ff3c153fdf39acd70b6883482a0fdbf9d7917fTimo Sirainen if (fs_file_random_fail_end(file, ret < 0 ? -1 : 0, FS_OP_READ) < 0)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic struct istream *
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenfs_randomfail_read_stream(struct fs_file *_file, size_t max_buffer_size)
19126597f92e5cdde901ac472090aa423e0e5541Timo Sirainen input = fs_read_stream(_file->parent, max_buffer_size);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen if (!fs_random_fail_range(_file->fs, FS_OP_READ, &offset))
a4985564b81d51caaddd38376792432428fd449bStephan Bosch input2 = i_stream_create_failure_at(input, offset, EIO, RANDOMFAIL_ERROR);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_write(struct fs_file *_file, const void *data, size_t size)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_file_random_fail_begin(file, FS_OP_WRITE))
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen return fs_file_random_fail_end(file, ret, FS_OP_EXISTS);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic void fs_randomfail_write_stream(struct fs_file *_file)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
19126597f92e5cdde901ac472090aa423e0e5541Timo Sirainen file->super_output = fs_write_stream(_file->parent);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen if (!fs_random_fail_range(_file->fs, FS_OP_WRITE, &offset))
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen _file->output = o_stream_create_failure_at(file->super_output, offset,
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_write_stream_finish(struct fs_file *_file, bool success)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
b8f4e3aa58caa7ee16cf21a21f92521bf05cbdc4Aki Tuomi fs_write_stream_abort_parent(_file, &file->super_output);
ba1b959d6bd83a0e3d6a3f927844543376019eacTimo Sirainen if (fs_random_fail(_file->fs, 1, FS_OP_WRITE)) {
19126597f92e5cdde901ac472090aa423e0e5541Timo Sirainen fs_write_stream_abort_error(_file->parent, &file->super_output, RANDOMFAIL_ERROR);
19126597f92e5cdde901ac472090aa423e0e5541Timo Sirainen return fs_write_stream_finish(_file->parent, &file->super_output);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenfs_randomfail_lock(struct fs_file *_file, unsigned int secs, struct fs_lock **lock_r)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic void fs_randomfail_unlock(struct fs_lock *_lock ATTR_UNUSED)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_exists(struct fs_file *_file)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_file_random_fail_begin(file, FS_OP_EXISTS))
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen return fs_file_random_fail_end(file, ret, FS_OP_EXISTS);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_stat(struct fs_file *_file, struct stat *st_r)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_file_random_fail_begin(file, FS_OP_STAT))
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen return fs_file_random_fail_end(file, ret, FS_OP_STAT);
a63cd84128875485e40ed804dbf0b0945526989cTimo Sirainenstatic int fs_randomfail_get_nlinks(struct fs_file *_file, nlink_t *nlinks_r)
a63cd84128875485e40ed804dbf0b0945526989cTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
a63cd84128875485e40ed804dbf0b0945526989cTimo Sirainen if (fs_file_random_fail_begin(file, FS_OP_STAT))
a63cd84128875485e40ed804dbf0b0945526989cTimo Sirainen return fs_file_random_fail_end(file, ret, FS_OP_STAT);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_copy(struct fs_file *_src, struct fs_file *_dest)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *dest = (struct randomfail_fs_file *)_dest;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_file_random_fail_begin(dest, FS_OP_COPY))
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen return fs_file_random_fail_end(dest, ret, FS_OP_COPY);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_rename(struct fs_file *_src, struct fs_file *_dest)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *dest = (struct randomfail_fs_file *)_dest;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_file_random_fail_begin(dest, FS_OP_RENAME))
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen return fs_file_random_fail_end(dest, ret, FS_OP_RENAME);
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_delete(struct fs_file *_file)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_file *file = (struct randomfail_fs_file *)_file;
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen if (fs_file_random_fail_begin(file, FS_OP_DELETE))
90dd3c6ed38f3501fff3cf79ac9fb0478b25969fTimo Sirainen return fs_file_random_fail_end(file, ret, FS_OP_DELETE);
4d71d712b42285afbd2a997f3408142ef0d5f899Timo Sirainenstatic struct fs_iter *fs_randomfail_iter_alloc(void)
4d71d712b42285afbd2a997f3408142ef0d5f899Timo Sirainen struct randomfail_fs_iter *iter = i_new(struct randomfail_fs_iter, 1);
4d71d712b42285afbd2a997f3408142ef0d5f899Timo Sirainenfs_randomfail_iter_init(struct fs_iter *_iter, const char *path,
4d71d712b42285afbd2a997f3408142ef0d5f899Timo Sirainen struct randomfail_fs_iter *iter = (struct randomfail_fs_iter *)_iter;
6d1218e68ce883735ffde9d7907e626ab81b9fb5Timo Sirainen iter->super = fs_iter_init_parent(_iter, path, flags);
4d71d712b42285afbd2a997f3408142ef0d5f899Timo Sirainen if (fs_random_fail_range(_iter->fs, FS_OP_ITER, &pos))
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic const char *fs_randomfail_iter_next(struct fs_iter *_iter)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_iter *iter = (struct randomfail_fs_iter *)_iter;
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen iter->super->async_callback = _iter->async_callback;
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen iter->super->async_context = _iter->async_context;
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen _iter->async_have_more = iter->super->async_have_more;
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainenstatic int fs_randomfail_iter_deinit(struct fs_iter *_iter)
5a441a6d97225cbdc67dad7bdd80de3c7bfedaabTimo Sirainen struct randomfail_fs_iter *iter = (struct randomfail_fs_iter *)_iter;