summaryrefslogtreecommitdiffstats
path: root/src/lib-fs/fs-sis-queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-fs/fs-sis-queue.c')
-rw-r--r--src/lib-fs/fs-sis-queue.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/lib-fs/fs-sis-queue.c b/src/lib-fs/fs-sis-queue.c
new file mode 100644
index 0000000..8ad7d98
--- /dev/null
+++ b/src/lib-fs/fs-sis-queue.c
@@ -0,0 +1,210 @@
+/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "istream.h"
+#include "ostream.h"
+#include "fs-sis-common.h"
+
+#define QUEUE_DIR_NAME "queue"
+
+struct sis_queue_fs {
+ struct fs fs;
+ char *queue_dir;
+};
+
+struct sis_queue_fs_file {
+ struct fs_file file;
+ struct sis_queue_fs *fs;
+};
+
+#define SISQUEUE_FS(ptr) container_of((ptr), struct sis_queue_fs, fs)
+#define SISQUEUE_FILE(ptr) container_of((ptr), struct sis_queue_fs_file, file)
+
+static struct fs *fs_sis_queue_alloc(void)
+{
+ struct sis_queue_fs *fs;
+
+ fs = i_new(struct sis_queue_fs, 1);
+ fs->fs = fs_class_sis_queue;
+ return &fs->fs;
+}
+
+static int
+fs_sis_queue_init(struct fs *_fs, const char *args,
+ const struct fs_settings *set, const char **error_r)
+{
+ struct sis_queue_fs *fs = SISQUEUE_FS(_fs);
+ const char *p, *parent_name, *parent_args;
+
+ /* <queue_dir>:<parent fs>[:<args>] */
+
+ p = strchr(args, ':');
+ if (p == NULL || p[1] == '\0') {
+ *error_r = "Parent filesystem not given as parameter";
+ return -1;
+ }
+
+ fs->queue_dir = i_strdup_until(args, p);
+ parent_name = p + 1;
+
+ parent_args = strchr(parent_name, ':');
+ if (parent_args == NULL)
+ parent_args = "";
+ else
+ parent_name = t_strdup_until(parent_name, parent_args++);
+ if (fs_init(parent_name, parent_args, set, &_fs->parent, error_r) < 0)
+ return -1;
+ return 0;
+}
+
+static void fs_sis_queue_free(struct fs *_fs)
+{
+ struct sis_queue_fs *fs = SISQUEUE_FS(_fs);
+
+ i_free(fs->queue_dir);
+ i_free(fs);
+}
+
+static struct fs_file *fs_sis_queue_file_alloc(void)
+{
+ struct sis_queue_fs_file *file = i_new(struct sis_queue_fs_file, 1);
+ return &file->file;
+}
+
+static void
+fs_sis_queue_file_init(struct fs_file *_file, const char *path,
+ enum fs_open_mode mode, enum fs_open_flags flags)
+{
+ struct sis_queue_fs_file *file = SISQUEUE_FILE(_file);
+ struct sis_queue_fs *fs = SISQUEUE_FS(_file->fs);
+
+ file->file.path = i_strdup(path);
+ file->fs = fs;
+
+ if (mode == FS_OPEN_MODE_APPEND)
+ fs_set_error(_file->event, ENOTSUP, "APPEND mode not supported");
+ else
+ file->file.parent = fs_file_init_parent(_file, path, mode, flags);
+}
+
+static void fs_sis_queue_file_deinit(struct fs_file *_file)
+{
+ struct sis_queue_fs_file *file = SISQUEUE_FILE(_file);
+
+ fs_file_free(_file);
+ i_free(file->file.path);
+ i_free(file);
+}
+
+static void fs_sis_queue_add(struct sis_queue_fs_file *file)
+{
+ struct sis_queue_fs *fs = SISQUEUE_FS(file->file.fs);
+ struct fs_file *queue_file;
+ const char *fname, *path, *queue_path;
+
+ path = fs_file_path(&file->file);
+ fname = strrchr(path, '/');
+ if (fname != NULL)
+ fname++;
+ else
+ fname = path;
+
+ queue_path = t_strdup_printf("%s/%s", fs->queue_dir, fname);
+ queue_file = fs_file_init_parent(&file->file, queue_path, FS_OPEN_MODE_CREATE, 0);
+ if (fs_write(queue_file, "", 0) < 0 && errno != EEXIST)
+ e_error(file->file.event, "%s", fs_file_last_error(queue_file));
+ fs_file_deinit(&queue_file);
+}
+
+static int fs_sis_queue_write(struct fs_file *_file, const void *data, size_t size)
+{
+ struct sis_queue_fs_file *file = SISQUEUE_FILE(_file);
+
+ if (_file->parent == NULL)
+ return -1;
+ if (fs_write(_file->parent, data, size) < 0)
+ return -1;
+ T_BEGIN {
+ fs_sis_queue_add(file);
+ } T_END;
+ return 0;
+}
+
+static void fs_sis_queue_write_stream(struct fs_file *_file)
+{
+ i_assert(_file->output == NULL);
+
+ if (_file->parent == NULL) {
+ _file->output = o_stream_create_error_str(EINVAL, "%s",
+ fs_file_last_error(_file));
+ } else {
+ _file->output = fs_write_stream(_file->parent);
+ }
+ o_stream_set_name(_file->output, _file->path);
+}
+
+static int fs_sis_queue_write_stream_finish(struct fs_file *_file, bool success)
+{
+ struct sis_queue_fs_file *file = SISQUEUE_FILE(_file);
+
+ if (!success) {
+ if (_file->parent != NULL)
+ fs_write_stream_abort_parent(_file, &_file->output);
+ return -1;
+ }
+
+ if (fs_write_stream_finish(_file->parent, &_file->output) < 0)
+ return -1;
+ T_BEGIN {
+ fs_sis_queue_add(file);
+ } T_END;
+ return 1;
+}
+
+static int fs_sis_queue_delete(struct fs_file *_file)
+{
+ T_BEGIN {
+ fs_sis_try_unlink_hash_file(_file, _file->parent);
+ } T_END;
+ return fs_delete(_file->parent);
+}
+
+const struct fs fs_class_sis_queue = {
+ .name = "sis-queue",
+ .v = {
+ fs_sis_queue_alloc,
+ fs_sis_queue_init,
+ NULL,
+ fs_sis_queue_free,
+ fs_wrapper_get_properties,
+ fs_sis_queue_file_alloc,
+ fs_sis_queue_file_init,
+ fs_sis_queue_file_deinit,
+ fs_wrapper_file_close,
+ fs_wrapper_file_get_path,
+ fs_wrapper_set_async_callback,
+ fs_wrapper_wait_async,
+ fs_wrapper_set_metadata,
+ fs_wrapper_get_metadata,
+ fs_wrapper_prefetch,
+ fs_wrapper_read,
+ fs_wrapper_read_stream,
+ fs_sis_queue_write,
+ fs_sis_queue_write_stream,
+ fs_sis_queue_write_stream_finish,
+ fs_wrapper_lock,
+ fs_wrapper_unlock,
+ fs_wrapper_exists,
+ fs_wrapper_stat,
+ fs_wrapper_copy,
+ fs_wrapper_rename,
+ fs_sis_queue_delete,
+ fs_wrapper_iter_alloc,
+ fs_wrapper_iter_init,
+ NULL,
+ NULL,
+ NULL,
+ fs_wrapper_get_nlinks,
+ }
+};