void fs_class_register(const struct fs *fs_class);
-void fs_set_error(struct fs *fs, const char *fmt, ...) ATTR_FORMAT(2, 3);
-
+/* Event must be fs_file or fs_iter events */
+void fs_set_error(struct event *event, const char *fmt, ...) ATTR_FORMAT(2, 3);
void fs_file_set_error_async(struct fs_file *file);
ssize_t fs_read_via_stream(struct fs_file *file, void *buf, size_t size);
*metadata_r = &file->metadata;
return 0;
}
- fs_set_error(file->fs, "Metadata not supported by backend");
+ fs_set_error(file->event, "Metadata not supported by backend");
return -1;
}
if (!file->read_or_prefetch_counted &&
}
static void ATTR_FORMAT(2, 0)
-fs_set_verror(struct fs *fs, const char *fmt, va_list args)
+fs_set_verror(struct event *event, const char *fmt, va_list args)
{
- /* the error is always kept in the parentmost fs */
- if (fs->parent != NULL)
- fs_set_verror(fs->parent, fmt, args);
- else {
- char *old_error = fs->last_error;
- fs->last_error = i_strdup_vprintf(fmt, args);
- /* free after strdup in case args point to the old error */
- i_free(old_error);
+ struct event *fs_event = event;
+ struct fs *fs;
+
+ /* NOTE: the event might be a passthrough event. We must log it exactly
+ once so it gets freed. */
+
+ while ((fs = event_get_ptr(fs_event, FS_EVENT_FIELD_FS)) == NULL) {
+ fs_event = event_get_parent(fs_event);
+ i_assert(fs_event != NULL);
}
+
+ /* the error is always kept in the parentmost fs */
+ while (fs->parent != NULL)
+ fs = fs->parent;
+ char *old_error = fs->last_error;
+ fs->last_error = i_strdup_vprintf(fmt, args);
+ i_free(old_error);
+
+ e_debug(event, "%s", fs->last_error);
}
const char *fs_last_error(struct fs *fs)
return -1;
}
if (ret < 0 && file->pending_read_input->stream_errno != 0) {
- fs_set_error(file->fs, "read(%s) failed: %s",
+ fs_set_error(file->event, "read(%s) failed: %s",
i_stream_get_name(file->pending_read_input),
i_stream_get_error(file->pending_read_input));
} else {
o_stream_uncork(file->output);
if ((ret = o_stream_finish(file->output)) <= 0) {
i_assert(ret < 0);
- fs_set_error(file->fs, "write(%s) failed: %s",
+ fs_set_error(file->event, "write(%s) failed: %s",
o_stream_get_name(file->output),
o_stream_get_error(file->output));
success = FALSE;
{
va_list args;
va_start(args, error_fmt);
- fs_set_verror(file->fs, error_fmt, args);
+ fs_set_verror(file->event, error_fmt, args);
fs_write_stream_abort(file, output);
va_end(args);
}
int ret;
if (file->fs->v.stat == NULL) {
- fs_set_error(file->fs, "fs_stat() not supported");
+ fs_set_error(file->event, "fs_stat() not supported");
return -1;
}
i_assert(src->fs == dest->fs);
if (src->fs->v.copy == NULL) {
- fs_set_error(src->fs, "fs_copy() not supported");
+ fs_set_error(src->event, "fs_copy() not supported");
return -1;
}
DLLIST_REMOVE(&fs->iters, iter);
if (fs->v.iter_deinit == NULL) {
- fs_set_error(fs, "FS iteration not supported");
+ fs_set_error(event, "FS iteration not supported");
ret = -1;
} else T_BEGIN {
ret = iter->fs->v.iter_deinit(iter);
return &fs->stats;
}
-void fs_set_error(struct fs *fs, const char *fmt, ...)
+void fs_set_error(struct event *event, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- fs_set_verror(fs, fmt, args);
+ fs_set_verror(event, fmt, args);
va_end(args);
}
void fs_file_set_error_async(struct fs_file *file)
{
- fs_set_error(file->fs, "Asynchronous operation in progress");
+ fs_set_error(file->event, "Asynchronous operation in progress");
errno = EAGAIN;
}
return 0;
else if (ret < 0) {
errno = EIO;
- fs_set_error(&fs->fs, "dict_lookup(%s) failed: %s", file->key, error);
+ fs_set_error(file->file.event, "dict_lookup(%s) failed: %s", file->key, error);
return -1;
} else {
errno = ENOENT;
- fs_set_error(&fs->fs, "Dict key %s doesn't exist", file->key);
+ fs_set_error(file->file.event, "Dict key %s doesn't exist", file->key);
return -1;
}
}
}
if (dict_transaction_commit(&trans, &error) < 0) {
errno = EIO;
- fs_set_error(_file->fs, "Dict transaction commit failed: %s", error);
+ fs_set_error(_file->event, "Dict transaction commit failed: %s", error);
return -1;
}
return 1;
dict_unset(trans, file->key);
if (dict_transaction_commit(&trans, &error) < 0) {
errno = EIO;
- fs_set_error(_file->fs, "Dict transaction commit failed: %s", error);
+ fs_set_error(_file->event, "Dict transaction commit failed: %s", error);
return -1;
}
return 0;
ret = dict_iterate_deinit(&iter->dict_iter, &error);
if (ret < 0)
- fs_set_error(_iter->fs, "Dict iteration failed: %s", error);
+ fs_set_error(_iter->event, "Dict iteration failed: %s", error);
return ret;
}
fs_wait_async(_file->fs);
}
if (ret == -1 && file->input->stream_errno != 0) {
- fs_set_error(_file->fs, "read(%s) failed: %s",
+ fs_set_error(_file->event, "read(%s) failed: %s",
i_stream_get_name(file->input),
i_stream_get_error(file->input));
return -1;
if (fs_stat(_file->parent, st_r) < 0)
return -1;
if ((uoff_t)st_r->st_size < file->metadata_write_size) {
- fs_set_error(_file->fs,
+ fs_set_error(_file->event,
"Just-written %s shrank unexpectedly "
"(%"PRIuUOFF_T" < %"PRIuUOFF_T")",
fs_file_path(_file), st_r->st_size,
i_stream_ref(input);
}
if ((ret = i_stream_get_size(input, TRUE, &input_size)) < 0) {
- fs_set_error(_file->fs, "i_stream_get_size(%s) failed: %s",
+ fs_set_error(_file->event, "i_stream_get_size(%s) failed: %s",
fs_file_path(_file), i_stream_get_error(input));
i_stream_unref(&input);
return -1;
i_stream_unref(&input);
if (ret == 0) {
/* we shouldn't get here */
- fs_set_error(_file->fs, "i_stream_get_size(%s) returned size as unknown",
+ fs_set_error(_file->event, "i_stream_get_size(%s) returned size as unknown",
fs_file_path(_file));
errno = EIO;
return -1;
errors. */
while (stat(path, &st) < 0) {
if (errno != ENOENT) {
- fs_set_error(&fs->fs, "stat(%s) failed: %m", path);
+ fs_set_error(file->file.event, "stat(%s) failed: %m", path);
return -1;
}
p = strrchr(path, '/');
else if (errno == EEXIST)
return 1;
else {
- fs_set_error(file->file.fs, "mkdir_parents(%s) failed: %m", dir);
+ fs_set_error(file->file.event, "mkdir_parents(%s) failed: %m", dir);
return -1;
}
}
/* some other not-unexpected error */
break;
} else {
- fs_set_error(file->file.fs, "rmdir(%s) failed: %m", path);
+ fs_set_error(file->file.event, "rmdir(%s) failed: %m", path);
return -1;
}
}
try_count++;
}
if (fd == -1) {
- fs_set_error(&fs->fs, "safe_mkstemp(%s) failed: %m", str_c(str));
+ fs_set_error(file->file.event, "safe_mkstemp(%s) failed: %m", str_c(str));
return -1;
}
file->temp_path = i_strdup(str_c(str));
static int fs_posix_open(struct posix_fs_file *file)
{
- struct posix_fs *fs = container_of(file->file.fs, struct posix_fs, fs);
const char *path = file->full_path;
i_assert(file->fd == -1);
case FS_OPEN_MODE_READONLY:
file->fd = open(path, O_RDONLY);
if (file->fd == -1)
- fs_set_error(&fs->fs, "open(%s) failed: %m", path);
+ fs_set_error(file->file.event, "open(%s) failed: %m", path);
break;
case FS_OPEN_MODE_APPEND:
file->fd = open(path, O_RDWR | O_APPEND);
if (file->fd == -1)
- fs_set_error(&fs->fs, "open(%s) failed: %m", path);
+ fs_set_error(file->file.event, "open(%s) failed: %m", path);
break;
case FS_OPEN_MODE_CREATE_UNIQUE_128:
case FS_OPEN_MODE_CREATE:
if (file->seek_to_beginning) {
file->seek_to_beginning = FALSE;
if (lseek(file->fd, 0, SEEK_SET) < 0) {
- fs_set_error(_file->fs, "lseek(%s, 0) failed: %m",
+ fs_set_error(_file->event, "lseek(%s, 0) failed: %m",
file->full_path);
return -1;
}
ret = read(file->fd, buf, size);
if (ret < 0)
- fs_set_error(_file->fs, "read(%s) failed: %m", file->full_path);
+ fs_set_error(_file->event, "read(%s) failed: %m", file->full_path);
fs_posix_file_close(_file);
return ret;
}
try_count++;
}
if (ret < 0) {
- fs_set_error(file->file.fs, "link(%s, %s) failed: %m",
+ fs_set_error(file->file.event, "link(%s, %s) failed: %m",
file->temp_path, file->full_path);
}
return ret;
if ((file->open_flags & FS_OPEN_FLAG_FSYNC) != 0 &&
!fs->disable_fsync) {
if (fdatasync(file->fd) < 0) {
- fs_set_error(file->file.fs, "fdatasync(%s) failed: %m",
+ fs_set_error(file->file.event, "fdatasync(%s) failed: %m",
file->full_path);
return -1;
}
i_fatal("gettimeofday() failed: %m");
tv[1] = tv[0];
if ((utimes(file->temp_path, tv)) < 0) {
- fs_set_error(file->file.fs, "utimes(%s) failed: %m",
+ fs_set_error(file->file.event, "utimes(%s) failed: %m",
file->temp_path);
return -1;
}
ret = fs_posix_write_finish_link(file);
old_errno = errno;
if (unlink(file->temp_path) < 0) {
- fs_set_error(file->file.fs, "unlink(%s) failed: %m",
+ fs_set_error(file->file.event, "unlink(%s) failed: %m",
file->temp_path);
}
errno = old_errno;
try_count++;
}
if (ret < 0) {
- fs_set_error(file->file.fs, "rename(%s, %s) failed: %m",
+ fs_set_error(file->file.event, "rename(%s, %s) failed: %m",
file->temp_path, file->full_path);
return -1;
}
if (file->open_mode != FS_OPEN_MODE_APPEND) {
if (write_full(file->fd, data, size) < 0) {
- fs_set_error(_file->fs, "write(%s) failed: %m",
+ fs_set_error(_file->event, "write(%s) failed: %m",
file->full_path);
return -1;
}
/* atomic append - it should either succeed or fail */
ret = write(file->fd, data, size);
if (ret < 0) {
- fs_set_error(_file->fs, "write(%s) failed: %m", file->full_path);
+ fs_set_error(_file->event, "write(%s) failed: %m", file->full_path);
return -1;
}
if ((size_t)ret != size) {
- fs_set_error(_file->fs,
+ fs_set_error(_file->event,
"write(%s) returned %"PRIuSIZE_T"/%"PRIuSIZE_T,
file->full_path, (size_t)ret, size);
errno = ENOSPC;
switch (fs->lock_method) {
case FS_POSIX_LOCK_METHOD_FLOCK:
#ifndef HAVE_FLOCK
- fs_set_error(_file->fs, "flock() not supported by OS "
+ fs_set_error(_file->event, "flock() not supported by OS "
"(for file %s)", file->full_path);
#else
if (secs == 0) {
&fs_lock.file_lock);
}
if (ret < 0) {
- fs_set_error(_file->fs, "flock(%s) failed: %m",
+ fs_set_error(_file->event, "flock(%s) failed: %m",
file->full_path);
}
#endif
DOTLOCK_CREATE_FLAG_NONBLOCK,
&fs_lock.dotlock);
if (ret < 0) {
- fs_set_error(_file->fs,
+ fs_set_error(_file->event,
"file_dotlock_create(%s) failed: %m",
file->full_path);
}
if (stat(file->full_path, &st) < 0) {
if (errno != ENOENT) {
- fs_set_error(_file->fs, "stat(%s) failed: %m",
+ fs_set_error(_file->event, "stat(%s) failed: %m",
file->full_path);
return -1;
}
fs-sis after fs_copy(). */
if (file->fd != -1 && _file->output == NULL) {
if (fstat(file->fd, st_r) < 0) {
- fs_set_error(_file->fs, "fstat(%s) failed: %m", file->full_path);
+ fs_set_error(_file->event, "fstat(%s) failed: %m", file->full_path);
return -1;
}
} else {
if (stat(file->full_path, st_r) < 0) {
- fs_set_error(_file->fs, "stat(%s) failed: %m", file->full_path);
+ fs_set_error(_file->event, "stat(%s) failed: %m", file->full_path);
return -1;
}
}
try_count++;
}
if (ret < 0) {
- fs_set_error(_src->fs, "link(%s, %s) failed: %m",
+ fs_set_error(_src->event, "link(%s, %s) failed: %m",
src->full_path, dest->full_path);
return -1;
}
try_count++;
}
if (ret < 0) {
- fs_set_error(_src->fs, "rename(%s, %s) failed: %m",
+ fs_set_error(_src->event, "rename(%s, %s) failed: %m",
src->full_path, dest->full_path);
return -1;
}
if (unlink(file->full_path) < 0) {
if (!UNLINK_EISDIR(errno)) {
- fs_set_error(_file->fs, "unlink(%s) failed: %m", file->full_path);
+ fs_set_error(_file->event, "unlink(%s) failed: %m", file->full_path);
return -1;
}
/* attempting to delete a directory. convert it to rmdir()
automatically. */
if (rmdir(file->full_path) < 0) {
- fs_set_error(_file->fs, "rmdir(%s) failed: %m", file->full_path);
+ fs_set_error(_file->event, "rmdir(%s) failed: %m", file->full_path);
return -1;
}
}
iter->dir = opendir(iter->path);
if (iter->dir == NULL && errno != ENOENT) {
iter->err = errno;
- fs_set_error(_iter->fs, "opendir(%s) failed: %m", iter->path);
+ fs_set_error(_iter->event, "opendir(%s) failed: %m", iter->path);
}
}
}
if (errno != 0) {
iter->err = errno;
- fs_set_error(_iter->fs, "readdir(%s) failed: %m", iter->path);
+ fs_set_error(_iter->event, "readdir(%s) failed: %m", iter->path);
}
return NULL;
}
if (iter->dir != NULL && closedir(iter->dir) < 0 && iter->err == 0) {
iter->err = errno;
- fs_set_error(_iter->fs, "closedir(%s) failed: %m", iter->path);
+ fs_set_error(_iter->event, "closedir(%s) failed: %m", iter->path);
}
if (iter->err != 0) {
errno = iter->err;
i_free(file);
}
-static bool fs_random_fail(struct fs *_fs, int divider, enum fs_op op)
+static bool fs_random_fail(struct fs *_fs, struct event *event,
+ int divider, enum fs_op op)
{
struct randomfail_fs *fs = (struct randomfail_fs *)_fs;
return FALSE;
if ((unsigned int)i_rand_limit(100 * divider) <= fs->op_probability[op]) {
errno = EIO;
- fs_set_error(_fs, RANDOMFAIL_ERROR);
+ fs_set_error(event, RANDOMFAIL_ERROR);
return TRUE;
}
return FALSE;
fs_file_random_fail_begin(struct randomfail_fs_file *file, enum fs_op op)
{
if (!file->op_pending[op]) {
- if (fs_random_fail(file->file.fs, 2, op))
+ if (fs_random_fail(file->file.fs, file->file.event, 2, op))
return TRUE;
}
file->op_pending[op] = TRUE;
int ret, enum fs_op op)
{
if (ret == 0 || errno != EAGAIN) {
- if (fs_random_fail(file->file.fs, 2, op))
+ if (fs_random_fail(file->file.fs, file->file.event, 2, op))
return -1;
file->op_pending[op] = FALSE;
}
}
static bool
-fs_random_fail_range(struct fs *_fs, enum fs_op op, uoff_t *offset_r)
+fs_random_fail_range(struct fs *_fs, struct event *event,
+ enum fs_op op, uoff_t *offset_r)
{
struct randomfail_fs *fs = (struct randomfail_fs *)_fs;
- if (!fs_random_fail(_fs, 1, op))
+ if (!fs_random_fail(_fs, event, 1, op))
return FALSE;
*offset_r = i_rand_minmax(fs->range_start[op], fs->range_end[op]);
return TRUE;
static bool fs_randomfail_prefetch(struct fs_file *_file, uoff_t length)
{
- if (fs_random_fail(_file->fs, 1, FS_OP_PREFETCH))
+ if (fs_random_fail(_file->fs, _file->event, 1, FS_OP_PREFETCH))
return TRUE;
return fs_prefetch(_file->parent, length);
}
uoff_t offset;
input = fs_read_stream(_file->parent, max_buffer_size);
- if (!fs_random_fail_range(_file->fs, FS_OP_READ, &offset))
+ if (!fs_random_fail_range(_file->fs, _file->event, FS_OP_READ, &offset))
return input;
input2 = i_stream_create_failure_at(input, offset, EIO, RANDOMFAIL_ERROR);
i_stream_unref(&input);
i_assert(_file->output == NULL);
file->super_output = fs_write_stream(_file->parent);
- if (!fs_random_fail_range(_file->fs, FS_OP_WRITE, &offset))
+ if (!fs_random_fail_range(_file->fs, _file->event, FS_OP_WRITE, &offset))
_file->output = file->super_output;
else {
_file->output = o_stream_create_failure_at(file->super_output, offset,
fs_write_stream_abort_parent(_file, &file->super_output);
return -1;
}
- if (fs_random_fail(_file->fs, 1, FS_OP_WRITE)) {
+ if (fs_random_fail(_file->fs, _file->event, 1, FS_OP_WRITE)) {
fs_write_stream_abort_error(_file->parent, &file->super_output, RANDOMFAIL_ERROR);
return -1;
}
static int
fs_randomfail_lock(struct fs_file *_file, unsigned int secs, struct fs_lock **lock_r)
{
- if (fs_random_fail(_file->fs, 1, FS_OP_LOCK))
+ if (fs_random_fail(_file->fs, _file->event, 1, FS_OP_LOCK))
return -1;
return fs_lock(_file->parent, secs, lock_r);
}
uoff_t pos;
iter->super = fs_iter_init_parent(_iter, path, flags);
- if (fs_random_fail_range(_iter->fs, FS_OP_ITER, &pos))
+ if (fs_random_fail_range(_iter->fs, _iter->event, FS_OP_ITER, &pos))
iter->fail_pos = pos + 1;
}
int ret;
if ((ret = fs_iter_deinit(&iter->super, &error)) < 0)
- fs_set_error(_iter->fs, "%s", error);
+ fs_set_error(_iter->event, "%s", error);
if (iter->fail_pos == 1) {
- fs_set_error(_iter->fs, RANDOMFAIL_ERROR);
+ fs_set_error(_iter->event, RANDOMFAIL_ERROR);
errno = EIO;
ret = -1;
}
/* assume filename begins with "<hash>-" */
p = strchr(fname, '-');
if (p == NULL) {
- fs_set_error(file->fs, "open(%s) failed: "
+ fs_set_error(file->event, "open(%s) failed: "
"Filenames must begin with '<hash>-'", path);
return -1;
}
file->fs = fs;
if (mode == FS_OPEN_MODE_APPEND)
- fs_set_error(_file->fs, "APPEND mode not supported");
+ fs_set_error(_file->event, "APPEND mode not supported");
else
file->file.parent = fs_file_init_parent(_file, path, mode | flags);
}
file->fs = fs;
file->open_mode = mode;
if (mode == FS_OPEN_MODE_APPEND) {
- fs_set_error(_file->fs, "APPEND mode not supported");
+ fs_set_error(_file->event, "APPEND mode not supported");
return;
}
int ret;
if ((ret = fs_iter_deinit(&iter->parent, &error)) < 0)
- fs_set_error(_iter->fs, "%s", error);
+ fs_set_error(_iter->event, "%s", error);
return ret;
}