changeset 20065:50d86fbcfd28

Split parts of lib-dict into lib-dict-extra. Otherwise there's a circular dependency because lib-dict/dict-fs.c depends on lib-fs, while lib-fs/fs-dict.c depends on lib-dict. This becomes a problem when compiling --without-shared-libs, although for some reason it works for me while linking the Dovecot core, but not when linking external plugins.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 21 Apr 2016 18:51:57 +0300
parents f8bf8bd68815
children f215c409c86d
files configure.ac src/Makefile.am src/lib-dict-extra/Makefile.am src/lib-dict-extra/dict-fs.c src/lib-dict-extra/dict-register.c src/lib-dict/Makefile.am src/lib-dict/dict-fs.c src/lib-dict/dict-register.c src/lib-dovecot/Makefile.am
diffstat 9 files changed, 338 insertions(+), 326 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Tue Apr 19 19:08:52 2016 +0300
+++ b/configure.ac	Thu Apr 21 18:51:57 2016 +0300
@@ -2523,7 +2523,7 @@
   LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/libdovecot-login.la'
   LIBDOVECOT_LDA='$(top_builddir)/src/lib-lda/libdovecot-lda.la'
 else
-  LIBDOVECOT_DEPS='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-stats/libstats.la $(top_builddir)/src/lib-http/libhttp.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-sasl/libsasl.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la $(top_builddir)/src/lib-test/libtest.la $(top_builddir)/src/lib/liblib.la'
+  LIBDOVECOT_DEPS='$(top_builddir)/src/lib-dict-extra/libdict_extra.la $(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-stats/libstats.la $(top_builddir)/src/lib-http/libhttp.la $(top_builddir)/src/lib-fs/libfs.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-dns/libdns.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-sasl/libsasl.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-ssl-iostream/libssl_iostream.la $(top_builddir)/src/lib-test/libtest.la $(top_builddir)/src/lib/liblib.la'
   LIBDOVECOT="$LIBDOVECOT_DEPS \$(LIBICONV) \$(MODULE_LIBS)"
   LIBDOVECOT_STORAGE_DEPS='$(top_builddir)/src/lib-storage/libstorage.la'
   LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/liblogin.la'
@@ -2859,6 +2859,7 @@
 src/lib-charset/Makefile
 src/lib-compression/Makefile
 src/lib-dict/Makefile
+src/lib-dict-extra/Makefile
 src/lib-dns/Makefile
 src/lib-fs/Makefile
 src/lib-fts/Makefile
--- a/src/Makefile.am	Tue Apr 19 19:08:52 2016 +0300
+++ b/src/Makefile.am	Thu Apr 21 18:51:57 2016 +0300
@@ -23,6 +23,7 @@
 
 SUBDIRS = \
 	$(LIBDOVECOT_SUBDIRS) \
+	lib-dict-extra \
 	lib-dovecot \
 	lib-fts \
 	lib-imap-client \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-dict-extra/Makefile.am	Thu Apr 21 18:51:57 2016 +0300
@@ -0,0 +1,14 @@
+noinst_LTLIBRARIES = libdict_extra.la
+
+dict_drivers = @dict_drivers@
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-dict \
+	-I$(top_srcdir)/src/lib-fs \
+	-I$(top_srcdir)/src/lib-settings \
+	$(SQL_CFLAGS)
+
+libdict_extra_la_SOURCES = \
+	dict-fs.c \
+	dict-register.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-dict-extra/dict-fs.c	Thu Apr 21 18:51:57 2016 +0300
@@ -0,0 +1,290 @@
+/* Copyright (c) 2013-2016 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;
+};
+
+struct fs_dict_iterate_context {
+	struct dict_iterate_context ctx;
+	const char **paths;
+	unsigned int path_idx;
+	enum dict_iterate_flags flags;
+	pool_t value_pool;
+	struct fs_iter *fs_iter;
+	bool failed;
+};
+
+static int
+fs_dict_init(struct dict *driver, const char *uri,
+	     const struct dict_settings *set,
+	     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.username = set->username;
+	fs_set.base_dir = set->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(set->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_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,
+		NULL
+	}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-dict-extra/dict-register.c	Thu Apr 21 18:51:57 2016 +0300
@@ -0,0 +1,30 @@
+/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "dict-private.h"
+
+static int refcount = 0;
+
+void dict_drivers_register_builtin(void)
+{
+	if (refcount++ > 0)
+		return;
+	dict_driver_register(&dict_driver_client);
+	dict_driver_register(&dict_driver_file);
+	dict_driver_register(&dict_driver_fs);
+	dict_driver_register(&dict_driver_memcached);
+	dict_driver_register(&dict_driver_memcached_ascii);
+	dict_driver_register(&dict_driver_redis);
+}
+
+void dict_drivers_unregister_builtin(void)
+{
+	if (--refcount > 0)
+		return;
+	dict_driver_unregister(&dict_driver_client);
+	dict_driver_unregister(&dict_driver_file);
+	dict_driver_unregister(&dict_driver_fs);
+	dict_driver_unregister(&dict_driver_memcached);
+	dict_driver_unregister(&dict_driver_memcached_ascii);
+	dict_driver_unregister(&dict_driver_redis);
+}
--- a/src/lib-dict/Makefile.am	Tue Apr 19 19:08:52 2016 +0300
+++ b/src/lib-dict/Makefile.am	Thu Apr 21 18:51:57 2016 +0300
@@ -1,12 +1,9 @@
 noinst_LTLIBRARIES = libdict.la
 noinst_LIBRARIES = libdict_backend.a
 
-dict_drivers = @dict_drivers@
-
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-test \
-	-I$(top_srcdir)/src/lib-fs \
 	-I$(top_srcdir)/src/lib-ldap \
 	-I$(top_srcdir)/src/lib-sql \
 	-I$(top_srcdir)/src/lib-settings \
@@ -16,11 +13,9 @@
 	dict.c \
 	dict-client.c \
 	dict-file.c \
-	dict-fs.c \
 	dict-memcached.c \
 	dict-memcached-ascii.c \
 	dict-redis.c \
-	dict-register.c \
 	dict-transaction-memory.c
 
 libdict_la_SOURCES = \
--- a/src/lib-dict/dict-fs.c	Tue Apr 19 19:08:52 2016 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,290 +0,0 @@
-/* Copyright (c) 2013-2016 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;
-};
-
-struct fs_dict_iterate_context {
-	struct dict_iterate_context ctx;
-	const char **paths;
-	unsigned int path_idx;
-	enum dict_iterate_flags flags;
-	pool_t value_pool;
-	struct fs_iter *fs_iter;
-	bool failed;
-};
-
-static int
-fs_dict_init(struct dict *driver, const char *uri,
-	     const struct dict_settings *set,
-	     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.username = set->username;
-	fs_set.base_dir = set->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(set->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_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,
-		NULL
-	}
-};
--- a/src/lib-dict/dict-register.c	Tue Apr 19 19:08:52 2016 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "dict-private.h"
-
-static int refcount = 0;
-
-void dict_drivers_register_builtin(void)
-{
-	if (refcount++ > 0)
-		return;
-	dict_driver_register(&dict_driver_client);
-	dict_driver_register(&dict_driver_file);
-	dict_driver_register(&dict_driver_fs);
-	dict_driver_register(&dict_driver_memcached);
-	dict_driver_register(&dict_driver_memcached_ascii);
-	dict_driver_register(&dict_driver_redis);
-}
-
-void dict_drivers_unregister_builtin(void)
-{
-	if (--refcount > 0)
-		return;
-	dict_driver_unregister(&dict_driver_client);
-	dict_driver_unregister(&dict_driver_file);
-	dict_driver_unregister(&dict_driver_fs);
-	dict_driver_unregister(&dict_driver_memcached);
-	dict_driver_unregister(&dict_driver_memcached_ascii);
-	dict_driver_unregister(&dict_driver_redis);
-}
--- a/src/lib-dovecot/Makefile.am	Tue Apr 19 19:08:52 2016 +0300
+++ b/src/lib-dovecot/Makefile.am	Thu Apr 21 18:51:57 2016 +0300
@@ -1,6 +1,7 @@
 # when adding libraries, update LIBDOVECOT also in configure.in
 libs = \
 	../lib-master/libmaster.la \
+	../lib-dict-extra/libdict_extra.la \
 	../lib-fs/libfs.la \
 	../lib-settings/libsettings.la \
 	../lib-stats/libstats.la \