dict-fs.c revision ac9600ec580ca6897d263b2f207f1571a09031fa
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen#include "lib.h"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include "array.h"
4afd50dff16661684ad2acddad7284bcd8c564dbTimo Sirainen#include "fs-api.h"
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen#include "istream.h"
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen#include "str.h"
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen#include "dict-transaction-memory.h"
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen#include "dict-private.h"
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainenstruct fs_dict {
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen struct dict dict;
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen struct fs *fs;
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen char *username;
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen};
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainenstruct fs_dict_iterate_context {
f98cd3b7ccfb026fb56c580b5c7c2e2ee5468049Timo Sirainen struct dict_iterate_context ctx;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen const char **paths;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen unsigned int path_idx;
f98cd3b7ccfb026fb56c580b5c7c2e2ee5468049Timo Sirainen enum dict_iterate_flags flags;
f98cd3b7ccfb026fb56c580b5c7c2e2ee5468049Timo Sirainen pool_t value_pool;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen struct fs_iter *fs_iter;
f98cd3b7ccfb026fb56c580b5c7c2e2ee5468049Timo Sirainen bool failed;
f98cd3b7ccfb026fb56c580b5c7c2e2ee5468049Timo Sirainen};
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainenstatic int
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainenfs_dict_init(struct dict *driver, const char *uri,
f98cd3b7ccfb026fb56c580b5c7c2e2ee5468049Timo Sirainen const struct dict_settings *set,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct dict **dict_r, const char **error_r)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct fs_settings fs_set;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct fs *fs;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen struct fs_dict *dict;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen const char *p, *fs_driver, *fs_args;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
cab0827de053c8b58c6528eb430c089576a49ca9Timo Sirainen p = strchr(uri, ':');
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen if (p == NULL) {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen fs_driver = uri;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen fs_args = "";
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen } else {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen fs_driver = t_strdup_until(uri, p);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen fs_args = p+1;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen }
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
93a7d1ee4b518b5c85f9721dc6539e4dab6aae00Timo Sirainen memset(&fs_set, 0, sizeof(fs_set));
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen fs_set.username = set->username;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen fs_set.base_dir = set->base_dir;
f98cd3b7ccfb026fb56c580b5c7c2e2ee5468049Timo Sirainen if (fs_init(fs_driver, fs_args, &fs_set, &fs, error_r) < 0)
f98cd3b7ccfb026fb56c580b5c7c2e2ee5468049Timo Sirainen return -1;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen dict = i_new(struct fs_dict, 1);
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen dict->dict = *driver;
9f0f2de10e4ea0c99052bf4b2bef8179f2536228Timo Sirainen dict->fs = fs;
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen dict->username = i_strdup(set->username);
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen *dict_r = &dict->dict;
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen return 0;
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen}
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainenstatic void fs_dict_deinit(struct dict *_dict)
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen{
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen struct fs_dict *dict = (struct fs_dict *)_dict;
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen fs_deinit(&dict->fs);
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen i_free(dict->username);
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen i_free(dict);
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen}
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenstatic const char *fs_dict_get_full_key(struct fs_dict *dict, const char *key)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen{
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (strncmp(key, DICT_PATH_SHARED, strlen(DICT_PATH_SHARED)) == 0)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return key + strlen(DICT_PATH_SHARED);
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen else if (strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE)) == 0) {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen return t_strdup_printf("%s/%s", dict->username,
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen key + strlen(DICT_PATH_PRIVATE));
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen } else {
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen i_unreached();
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen }
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen}
383d0e8c24451468d6bea17e4b55d74de744abe6Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainenstatic int fs_dict_lookup(struct dict *_dict, pool_t pool,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen const char *key, const char **value_r)
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen{
458acd7b39c84bae0d18c36ff9ddff9a49b4ae4aTimo Sirainen struct fs_dict *dict = (struct fs_dict *)_dict;
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen struct fs_file *file;
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen struct istream *input;
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen const unsigned char *data;
55a14bce15b9f44441b5f56616d73651a294d770Timo Sirainen size_t size;
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen string_t *str;
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen int ret;
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen file = fs_file_init(dict->fs, fs_dict_get_full_key(dict, key),
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen FS_OPEN_MODE_READONLY);
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen input = fs_read_stream(file, IO_BLOCK_SIZE);
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen i_stream_read(input);
79f416d4000aa4192683207aea58a7b12ce66411Timo Sirainen
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_iterate_context *
fs_dict_iterate_init(struct dict *_dict, const char *const *paths,
enum dict_iterate_flags flags)
{
struct fs_dict *dict = (struct fs_dict *)_dict;
struct fs_dict_iterate_context *iter;
/* these flags are not supported for now */
i_assert((flags & DICT_ITERATE_FLAG_RECURSE) == 0);
i_assert((flags & DICT_ITERATE_FLAG_EXACT_KEY) == 0);
i_assert((flags & (DICT_ITERATE_FLAG_SORT_BY_KEY |
DICT_ITERATE_FLAG_SORT_BY_VALUE)) == 0);
iter = i_new(struct fs_dict_iterate_context, 1);
iter->ctx.dict = _dict;
iter->paths = p_strarray_dup(default_pool, paths);
iter->flags = flags;
iter->value_pool = pool_alloconly_create("iterate value pool", 128);
iter->fs_iter = fs_iter_init(dict->fs,
fs_dict_get_full_key(dict, paths[0]), 0);
return &iter->ctx;
}
static bool fs_dict_iterate(struct dict_iterate_context *ctx,
const char **key_r, const char **value_r)
{
struct fs_dict_iterate_context *iter =
(struct fs_dict_iterate_context *)ctx;
struct fs_dict *dict = (struct fs_dict *)ctx->dict;
const char *path;
int ret;
*key_r = fs_iter_next(iter->fs_iter);
if (*key_r == NULL) {
if (fs_iter_deinit(&iter->fs_iter) < 0) {
iter->failed = TRUE;
return FALSE;
}
if (iter->paths[++iter->path_idx] == NULL)
return FALSE;
path = fs_dict_get_full_key(dict, iter->paths[iter->path_idx]);
iter->fs_iter = fs_iter_init(dict->fs, path, 0);
return fs_dict_iterate(ctx, key_r, value_r);
}
if ((iter->flags & DICT_ITERATE_FLAG_NO_VALUE) != 0) {
*value_r = NULL;
return TRUE;
}
p_clear(iter->value_pool);
path = t_strconcat(iter->paths[iter->path_idx], *key_r, NULL);
if ((ret = fs_dict_lookup(ctx->dict, iter->value_pool, path, value_r)) < 0) {
/* I/O error */
iter->failed = TRUE;
return FALSE;
} else if (ret == 0) {
/* file was just deleted, just skip to next one */
return fs_dict_iterate(ctx, key_r, value_r);
}
return TRUE;
}
static int fs_dict_iterate_deinit(struct dict_iterate_context *ctx)
{
struct fs_dict_iterate_context *iter =
(struct fs_dict_iterate_context *)ctx;
int ret;
if (iter->fs_iter != NULL) {
if (fs_iter_deinit(&iter->fs_iter) < 0)
iter->failed = TRUE;
}
ret = iter->failed ? -1 : 0;
pool_unref(&iter->value_pool);
i_free(iter->paths);
i_free(iter);
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,
fs_dict_iterate_init,
fs_dict_iterate,
fs_dict_iterate_deinit,
fs_dict_transaction_init,
fs_dict_transaction_commit,
dict_transaction_memory_rollback,
dict_transaction_memory_set,
dict_transaction_memory_unset,
NULL,
NULL
}
};