Mercurial > dovecot > core-2.2
changeset 19053:6adcfddd354d
lib-fs: Added "dict" backend, which is a wrapper to using lib-dict.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 31 Aug 2015 23:54:24 +0300 |
parents | 55cab04ef2af |
children | c0e61e4f47ec |
files | src/lib-fs/Makefile.am src/lib-fs/fs-api-private.h src/lib-fs/fs-api.c src/lib-fs/fs-dict.c |
diffstat | 4 files changed, 316 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-fs/Makefile.am Mon Aug 31 23:46:05 2015 +0300 +++ b/src/lib-fs/Makefile.am Mon Aug 31 23:54:24 2015 +0300 @@ -2,11 +2,13 @@ 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 \
--- a/src/lib-fs/fs-api-private.h Mon Aug 31 23:46:05 2015 +0300 +++ b/src/lib-fs/fs-api-private.h Mon Aug 31 23:54:24 2015 +0300 @@ -151,6 +151,7 @@ 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;
--- a/src/lib-fs/fs-api.c Mon Aug 31 23:46:05 2015 +0300 +++ b/src/lib-fs/fs-api.c Mon Aug 31 23:54:24 2015 +0300 @@ -63,6 +63,7 @@ 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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-fs/fs-dict.c Mon Aug 31 23:54:24 2015 +0300 @@ -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 + } +};