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
+	}
+};