]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-fs: Added "dict" backend, which is a wrapper to using lib-dict.
authorTimo Sirainen <tss@iki.fi>
Mon, 31 Aug 2015 20:54:24 +0000 (23:54 +0300)
committerTimo Sirainen <tss@iki.fi>
Mon, 31 Aug 2015 20:54:24 +0000 (23:54 +0300)
src/lib-fs/Makefile.am
src/lib-fs/fs-api-private.h
src/lib-fs/fs-api.c
src/lib-fs/fs-dict.c [new file with mode: 0644]

index 0605a300e11457e999b58ba6bc1a1bb95b12c42f..20c1bcaac43c6dd6703f0e5b37d4b4661c0a3b28 100644 (file)
@@ -2,11 +2,13 @@ noinst_LTLIBRARIES = libfs.la
 
 AM_CPPFLAGS = \
        -I$(top_srcdir)/src/lib \
+       -I$(top_srcdir)/src/lib-dict \
        -I$(top_srcdir)/src/lib-ssl-iostream \
        -DMODULE_DIR=\""$(moduledir)"\"
 
 libfs_la_SOURCES = \
        fs-api.c \
+       fs-dict.c \
        fs-metawrap.c \
        fs-randomfail.c \
        fs-posix.c \
index 1fd22799c418b3be75c42c9d56fe284eebe778e9..e27ae1e715e543bf9f3dacba9733b8aa5bc1ced7 100644 (file)
@@ -151,6 +151,7 @@ struct fs_iter {
        void *async_context;
 };
 
+extern const struct fs fs_class_dict;
 extern const struct fs fs_class_posix;
 extern const struct fs fs_class_randomfail;
 extern const struct fs fs_class_metawrap;
index 10d3c278ac202b65500565ee824d6228469df5cc..ee92bd7288e29ff29670279e2e25071b849f1ae6 100644 (file)
@@ -63,6 +63,7 @@ static void fs_classes_deinit(void)
 static void fs_classes_init(void)
 {
        i_array_init(&fs_classes, 8);
+       fs_class_register(&fs_class_dict);
        fs_class_register(&fs_class_posix);
        fs_class_register(&fs_class_randomfail);
        fs_class_register(&fs_class_metawrap);
diff --git a/src/lib-fs/fs-dict.c b/src/lib-fs/fs-dict.c
new file mode 100644 (file)
index 0000000..a2c0819
--- /dev/null
@@ -0,0 +1,312 @@
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "str.h"
+#include "guid.h"
+#include "base64.h"
+#include "istream.h"
+#include "ostream.h"
+#include "dict.h"
+#include "fs-api-private.h"
+
+enum fs_dict_value_encoding {
+       FS_DICT_VALUE_ENCODING_RAW,
+       FS_DICT_VALUE_ENCODING_BASE64
+};
+
+struct dict_fs {
+       struct fs fs;
+       struct dict *dict;
+       char *path_prefix;
+       enum fs_dict_value_encoding encoding;
+};
+
+struct dict_fs_file {
+       struct fs_file file;
+       pool_t pool;
+       const char *key, *value;
+       buffer_t *write_buffer;
+};
+
+struct dict_fs_iter {
+       struct fs_iter iter;
+       struct dict_iterate_context *dict_iter;
+};
+
+static struct fs *fs_dict_alloc(void)
+{
+       struct dict_fs *fs;
+
+       fs = i_new(struct dict_fs, 1);
+       fs->fs = fs_class_dict;
+       return &fs->fs;
+}
+
+static int
+fs_dict_init(struct fs *_fs, const char *args, const struct fs_settings *set)
+{
+       struct dict_fs *fs = (struct dict_fs *)_fs;
+       struct dict_settings dict_set;
+       const char *p, *encoding_str, *error;
+
+       p = strchr(args, ':');
+       if (p == NULL) {
+               fs_set_error(_fs, "':' missing in args");
+               return -1;
+       }
+       encoding_str = t_strdup_until(args, p++);
+       if (strcmp(encoding_str, "raw") == 0)
+               fs->encoding = FS_DICT_VALUE_ENCODING_RAW;
+       else if (strcmp(encoding_str, "base64") == 0)
+               fs->encoding = FS_DICT_VALUE_ENCODING_BASE64;
+       else {
+               fs_set_error(_fs, "Unknown value encoding '%s'", encoding_str);
+               return -1;
+       }
+
+       memset(&dict_set, 0, sizeof(dict_set));
+       dict_set.username = set->username;
+       dict_set.base_dir = set->base_dir;
+
+       if (dict_init_full(p, &dict_set, &fs->dict, &error) < 0) {
+               fs_set_error(_fs, "dict_init(%s) failed: %s", args, error);
+               return -1;
+       }
+       return 0;
+}
+
+static void fs_dict_deinit(struct fs *_fs)
+{
+       struct dict_fs *fs = (struct dict_fs *)_fs;
+
+       dict_deinit(&fs->dict);
+       i_free(fs);
+}
+
+static enum fs_properties fs_dict_get_properties(struct fs *fs ATTR_UNUSED)
+{
+       return FS_PROPERTY_ITER | FS_PROPERTY_RELIABLEITER;
+}
+
+static struct fs_file *
+fs_dict_file_init(struct fs *_fs, const char *path,
+                 enum fs_open_mode mode, enum fs_open_flags flags ATTR_UNUSED)
+{
+       struct dict_fs *fs = (struct dict_fs *)_fs;
+       struct dict_fs_file *file;
+       guid_128_t guid;
+       pool_t pool;
+
+       i_assert(mode != FS_OPEN_MODE_APPEND); /* not supported */
+       i_assert(mode != FS_OPEN_MODE_CREATE); /* not supported */
+
+       pool = pool_alloconly_create("fs dict file", 128);
+       file = p_new(pool, struct dict_fs_file, 1);
+       file->pool = pool;
+       file->file.fs = _fs;
+       if (mode != FS_OPEN_MODE_CREATE_UNIQUE_128)
+               file->file.path = p_strdup(pool, path);
+       else {
+               guid_128_generate(guid);
+               file->file.path = p_strdup_printf(pool, "%s/%s", path,
+                                                 guid_128_to_string(guid));
+       }
+       file->key = fs->path_prefix == NULL ?
+               p_strdup(pool, file->file.path) :
+               p_strconcat(pool, fs->path_prefix, file->file.path, NULL);
+       return &file->file;
+}
+
+static void fs_dict_file_deinit(struct fs_file *_file)
+{
+       struct dict_fs_file *file = (struct dict_fs_file *)_file;
+
+       i_assert(_file->output == NULL);
+
+       pool_unref(&file->pool);
+}
+
+static bool fs_dict_prefetch(struct fs_file *_file ATTR_UNUSED,
+                            uoff_t length ATTR_UNUSED)
+{
+       /* once async dict_lookup() is implemented, we want to start it here */
+       return TRUE;
+}
+
+static int fs_dict_lookup(struct dict_fs_file *file)
+{
+       struct dict_fs *fs = (struct dict_fs *)file->file.fs;
+       int ret;
+
+       if (file->value != NULL)
+               return 0;
+
+       ret = dict_lookup(fs->dict, file->pool, file->key, &file->value);
+       if (ret > 0)
+               return 0;
+       else if (ret < 0) {
+               errno = EIO;
+               fs_set_error(&fs->fs, "Dict lookup failed");
+               return -1;
+       } else {
+               errno = ENOENT;
+               fs_set_error(&fs->fs, "Dict key doesn't exist");
+               return -1;
+       }
+}
+
+static struct istream *
+fs_dict_read_stream(struct fs_file *_file, size_t max_buffer_size ATTR_UNUSED)
+{
+       struct dict_fs_file *file = (struct dict_fs_file *)_file;
+       struct istream *input;
+
+       if (fs_dict_lookup(file) < 0)
+               input = i_stream_create_error_str(errno, "%s", fs_last_error(_file->fs));
+       else
+               input = i_stream_create_from_data(file->value, strlen(file->value));
+       i_stream_set_name(input, file->key);
+       return input;
+}
+
+static void fs_dict_write_stream(struct fs_file *_file)
+{
+       struct dict_fs_file *file = (struct dict_fs_file *)_file;
+
+       i_assert(_file->output == NULL);
+
+       file->write_buffer = buffer_create_dynamic(file->pool, 128);
+       _file->output = o_stream_create_buffer(file->write_buffer);
+       o_stream_set_name(_file->output, file->key);
+}
+
+static int fs_dict_write_stream_finish(struct fs_file *_file, bool success)
+{
+       struct dict_fs_file *file = (struct dict_fs_file *)_file;
+       struct dict_fs *fs = (struct dict_fs *)_file->fs;
+       struct dict_transaction_context *trans;
+
+       o_stream_destroy(&_file->output);
+       if (!success)
+               return -1;
+
+       trans = dict_transaction_begin(fs->dict);
+       switch (fs->encoding) {
+       case FS_DICT_VALUE_ENCODING_RAW:
+               dict_set(trans, file->key, str_c(file->write_buffer));
+               break;
+       case FS_DICT_VALUE_ENCODING_BASE64: {
+               const unsigned int base64_size =
+                       MAX_BASE64_ENCODED_SIZE(file->write_buffer->used);
+               string_t *base64 = t_str_new(base64_size);
+               base64_encode(file->write_buffer->data,
+                             file->write_buffer->used, base64);
+               dict_set(trans, file->key, str_c(base64));
+       }
+       }
+       if (dict_transaction_commit(&trans) < 0) {
+               errno = EIO;
+               fs_set_error(_file->fs, "Dict transaction commit failed");
+               return -1;
+       }
+       return 1;
+}
+
+static int fs_dict_stat(struct fs_file *_file, struct stat *st_r)
+{
+       struct dict_fs_file *file = (struct dict_fs_file *)_file;
+
+       memset(st_r, 0, sizeof(*st_r));
+
+       if (fs_dict_lookup(file) < 0)
+               return -1;
+       st_r->st_size = strlen(file->value);
+       return 0;
+}
+
+static int fs_dict_delete(struct fs_file *_file)
+{
+       struct dict_fs_file *file = (struct dict_fs_file *)_file;
+       struct dict_fs *fs = (struct dict_fs *)_file->fs;
+       struct dict_transaction_context *trans;
+
+       trans = dict_transaction_begin(fs->dict);
+       dict_unset(trans, file->key);
+       if (dict_transaction_commit(&trans) < 0) {
+               errno = EIO;
+               fs_set_error(_file->fs, "Dict transaction commit failed");
+               return -1;
+       }
+       return 0;
+}
+
+static struct fs_iter *
+fs_dict_iter_init(struct fs *_fs, const char *path, enum fs_iter_flags flags)
+{
+       struct dict_fs *fs = (struct dict_fs *)_fs;
+       struct dict_fs_iter *iter;
+
+       iter = i_new(struct dict_fs_iter, 1);
+       iter->iter.fs = _fs;
+       iter->iter.flags = flags;
+       if (fs->path_prefix != NULL)
+               path = t_strconcat(fs->path_prefix, path, NULL);
+
+       iter->dict_iter = dict_iterate_init(fs->dict, path, 0);
+       return &iter->iter;
+}
+
+static const char *fs_dict_iter_next(struct fs_iter *_iter)
+{
+       struct dict_fs_iter *iter = (struct dict_fs_iter *)_iter;
+       const char *key, *value;
+
+       if (!dict_iterate(iter->dict_iter, &key, &value))
+               return NULL;
+       return key;
+}
+
+static int fs_dict_iter_deinit(struct fs_iter *_iter)
+{
+       struct dict_fs_iter *iter = (struct dict_fs_iter *)_iter;
+       int ret;
+
+       ret = dict_iterate_deinit(&iter->dict_iter);
+       if (ret < 0)
+               fs_set_error(_iter->fs, "Dict iteration failed");
+       i_free(iter);
+       return ret;
+}
+
+const struct fs fs_class_dict = {
+       .name = "dict",
+       .v = {
+               fs_dict_alloc,
+               fs_dict_init,
+               fs_dict_deinit,
+               fs_dict_get_properties,
+               fs_dict_file_init,
+               fs_dict_file_deinit,
+               NULL,
+               NULL,
+               NULL, NULL,
+               NULL, NULL,
+               fs_dict_prefetch,
+               NULL,
+               fs_dict_read_stream,
+               NULL,
+               fs_dict_write_stream,
+               fs_dict_write_stream_finish,
+               NULL,
+               NULL,
+               NULL,
+               fs_dict_stat,
+               fs_default_copy,
+               NULL,
+               fs_dict_delete,
+               fs_dict_iter_init,
+               fs_dict_iter_next,
+               fs_dict_iter_deinit
+       }
+};