Mercurial > dovecot > core-2.2
diff src/lib-dict/dict-fs.c @ 17043:773e9ce608ed
lib-dict: Added "fs" wrapper dict backend, which uses lib-fs.
Each dict key is a separate file where the file's contents are the dict
value.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 08 Dec 2013 21:13:22 +0200 |
parents | |
children | add8c00fb3cc |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/dict-fs.c Sun Dec 08 21:13:22 2013 +0200 @@ -0,0 +1,201 @@ +/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "fs-api.h" +#include "istream.h" +#include "str.h" +#include "dict-transaction-memory.h" +#include "dict-private.h" + +struct fs_dict { + struct dict dict; + struct fs *fs; + char *username; +}; + +static int +fs_dict_init(struct dict *driver, const char *uri, + enum dict_data_type value_type ATTR_UNUSED, + const char *username, + const char *base_dir, struct dict **dict_r, + const char **error_r) +{ + struct fs_settings fs_set; + struct fs *fs; + struct fs_dict *dict; + const char *p, *fs_driver, *fs_args; + + p = strchr(uri, ':'); + if (p == NULL) { + fs_driver = uri; + fs_args = ""; + } else { + fs_driver = t_strdup_until(uri, p); + fs_args = p+1; + } + + memset(&fs_set, 0, sizeof(fs_set)); + fs_set.base_dir = base_dir; + if (fs_init(fs_driver, fs_args, &fs_set, &fs, error_r) < 0) + return -1; + + dict = i_new(struct fs_dict, 1); + dict->dict = *driver; + dict->fs = fs; + dict->username = i_strdup(username); + + *dict_r = &dict->dict; + return 0; +} + +static void fs_dict_deinit(struct dict *_dict) +{ + struct fs_dict *dict = (struct fs_dict *)_dict; + + fs_deinit(&dict->fs); + i_free(dict->username); + i_free(dict); +} + +static const char *fs_dict_get_full_key(struct fs_dict *dict, const char *key) +{ + if (strncmp(key, DICT_PATH_SHARED, strlen(DICT_PATH_SHARED)) == 0) + return key + strlen(DICT_PATH_SHARED); + else if (strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE)) == 0) { + return t_strdup_printf("%s/%s", dict->username, + key + strlen(DICT_PATH_PRIVATE)); + } else { + i_unreached(); + } +} + +static int fs_dict_lookup(struct dict *_dict, pool_t pool, + const char *key, const char **value_r) +{ + struct fs_dict *dict = (struct fs_dict *)_dict; + struct fs_file *file; + struct istream *input; + const unsigned char *data; + size_t size; + string_t *str; + int ret; + + file = fs_file_init(dict->fs, fs_dict_get_full_key(dict, key), + FS_OPEN_MODE_READONLY); + input = fs_read_stream(file, IO_BLOCK_SIZE); + i_stream_read(input); + + str = str_new(pool, i_stream_get_data_size(input)+1); + while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) { + str_append_n(str, data, size); + i_stream_skip(input, size); + } + i_assert(ret == -1); + + if (input->stream_errno == 0) { + *value_r = str_c(str); + ret = 1; + } else { + *value_r = NULL; + if (input->stream_errno == ENOENT) + ret = 0; + } + + i_stream_unref(&input); + fs_file_deinit(&file); + return ret; +} + +static struct dict_transaction_context * +fs_dict_transaction_init(struct dict *_dict) +{ + struct dict_transaction_memory_context *ctx; + pool_t pool; + + pool = pool_alloconly_create("file dict transaction", 2048); + ctx = p_new(pool, struct dict_transaction_memory_context, 1); + dict_transaction_memory_init(ctx, _dict, pool); + return &ctx->ctx; +} + +static int fs_dict_write_changes(struct dict_transaction_memory_context *ctx) +{ + struct fs_dict *dict = (struct fs_dict *)ctx->ctx.dict; + struct fs_file *file; + const struct dict_transaction_memory_change *change; + const char *key; + int ret = 0; + + array_foreach(&ctx->changes, change) { + key = fs_dict_get_full_key(dict, change->key); + switch (change->type) { + case DICT_CHANGE_TYPE_SET: + file = fs_file_init(dict->fs, key, + FS_OPEN_MODE_REPLACE); + if (fs_write(file, change->value.str, strlen(change->value.str)) < 0) { + i_error("fs_write(%s) failed: %s", key, + fs_file_last_error(file)); + ret = -1; + } + fs_file_deinit(&file); + break; + case DICT_CHANGE_TYPE_UNSET: + file = fs_file_init(dict->fs, key, FS_OPEN_MODE_READONLY); + if (fs_delete(file) < 0) { + i_error("fs_delete(%s) failed: %s", key, + fs_file_last_error(file)); + ret = -1; + } + fs_file_deinit(&file); + break; + case DICT_CHANGE_TYPE_APPEND: + case DICT_CHANGE_TYPE_INC: + i_unreached(); + } + if (ret < 0) + return -1; + } + return 0; +} + +static int +fs_dict_transaction_commit(struct dict_transaction_context *_ctx, + bool async ATTR_UNUSED, + dict_transaction_commit_callback_t *callback, + void *context) +{ + struct dict_transaction_memory_context *ctx = + (struct dict_transaction_memory_context *)_ctx; + int ret; + + if (fs_dict_write_changes(ctx) < 0) + ret = -1; + else + ret = 1; + pool_unref(&ctx->pool); + + if (callback != NULL) + callback(ret, context); + return ret; +} + +struct dict dict_driver_fs = { + .name = "fs", + { + fs_dict_init, + fs_dict_deinit, + NULL, + fs_dict_lookup, + NULL, + NULL, + NULL, + fs_dict_transaction_init, + fs_dict_transaction_commit, + dict_transaction_memory_rollback, + dict_transaction_memory_set, + dict_transaction_memory_unset, + NULL, + NULL + } +};