changeset 4084:51306837698b HEAD

Quota API redesign. Still not perfect, but better than before. Quota backends no longer need to be told where the mails exist. fs backend doesn't work correctly with multiple partitions (if you've multiple namespaces).
author Timo Sirainen <timo.sirainen@movial.fi>
date Wed, 01 Mar 2006 11:14:59 +0200
parents ed4f01bb4701
children cce51b1b5d00
files src/plugins/imap-quota/imap-quota-plugin.c src/plugins/quota/quota-dict.c src/plugins/quota/quota-dirsize.c src/plugins/quota/quota-fs.c src/plugins/quota/quota-plugin.c src/plugins/quota/quota-private.h src/plugins/quota/quota-storage.c src/plugins/quota/quota.c src/plugins/quota/quota.h src/plugins/trash/trash-plugin.c
diffstat 10 files changed, 857 insertions(+), 635 deletions(-) [+]
line wrap: on
line diff
--- a/src/plugins/imap-quota/imap-quota-plugin.c	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/imap-quota/imap-quota-plugin.c	Wed Mar 01 11:14:59 2006 +0200
@@ -81,27 +81,19 @@
 	str_append(str, "* QUOTAROOT ");
 	imap_quote_append_string(str, mailbox, FALSE);
 
-	iter = quota_root_iter_init(quota, box);
+	iter = quota_root_iter_init(box);
 	while ((root = quota_root_iter_next(iter)) != NULL) {
 		str_append_c(str, ' ');
 		imap_quote_append_string(str, quota_root_get_name(root), FALSE);
 	}
-	if (quota_root_iter_deinit(iter) < 0) {
-		/* some failure, send as untagged error */
-		client_send_line(cmd->client, t_strconcat(
-			"* BAD ", quota_last_error(quota), NULL));
-	}
+	quota_root_iter_deinit(iter);
 	client_send_line(cmd->client, str_c(str));
 
 	/* send QUOTA reply for each quotaroot */
-	iter = quota_root_iter_init(quota, box);
+	iter = quota_root_iter_init(box);
 	while ((root = quota_root_iter_next(iter)) != NULL)
 		quota_send(cmd, root);
-	if (quota_root_iter_deinit(iter) < 0) {
-		/* some failure, send as untagged error */
-		client_send_line(cmd->client, t_strconcat(
-			"* BAD ", quota_last_error(quota), NULL));
-	}
+	quota_root_iter_deinit(iter);
 
 	mailbox_close(&box);
 
--- a/src/plugins/quota/quota-dict.c	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/quota/quota-dict.c	Wed Mar 01 11:14:59 2006 +0200
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005 Timo Sirainen */
+/* Copyright (C) 2005-2006 Timo Sirainen */
 
 #include "lib.h"
 #include "str.h"
@@ -10,100 +10,53 @@
 #define DICT_QUOTA_LIMIT_PATH DICT_PATH_PRIVATE"quota/limit/"
 #define DICT_QUOTA_CURRENT_PATH DICT_PATH_PRIVATE"quota/current/"
 
-struct dict_quota {
-	struct quota quota;
-
-	pool_t pool;
-	const char *error;
+struct dict_quota_root {
 	struct quota_root root;
-
 	struct dict *dict;
 };
 
-struct dict_quota_root_iter {
-	struct quota_root_iter iter;
-
-	bool sent;
-};
+extern struct quota_backend quota_backend_dict;
 
-extern struct quota dict_quota;
-
-static struct quota *dict_quota_init(const char *data)
+static struct quota_root *
+dict_quota_init(struct quota_setup *setup, const char *name)
 {
-	struct dict_quota *quota;
+	struct dict_quota_root *root;
 	struct dict *dict;
-	pool_t pool;
 
 	if (getenv("DEBUG") != NULL)
-		i_info("dict quota uri = %s", data);
+		i_info("dict quota uri = %s", setup->data);
 
-	dict = dict_init(data, getenv("USER"));
+	dict = dict_init(setup->data, getenv("USER"));
 	if (dict == NULL)
 		return NULL;
 
-	pool = pool_alloconly_create("quota", 1024);
-	quota = p_new(pool, struct dict_quota, 1);
-	quota->pool = pool;
-	quota->quota = dict_quota;
-	quota->dict = dict;
-
-	quota->root.quota = &quota->quota;
-	return &quota->quota;
-}
+	root = i_new(struct dict_quota_root, 1);
+	root->root.name = i_strdup(name);
+	root->root.v = quota_backend_dict.v;
+	root->dict = dict;
 
-static void dict_quota_deinit(struct quota *_quota)
-{
-	struct dict_quota *quota = (struct dict_quota *)_quota;
-
-	pool_unref(quota->pool);
-}
-
-static struct quota_root_iter *
-dict_quota_root_iter_init(struct quota *quota,
-			  struct mailbox *box __attr_unused__)
-{
-	struct dict_quota_root_iter *iter;
-
-	iter = i_new(struct dict_quota_root_iter, 1);
-	iter->iter.quota = quota;
-	return &iter->iter;
+	return &root->root;
 }
 
-static struct quota_root *
-dict_quota_root_iter_next(struct quota_root_iter *_iter)
+static void dict_quota_deinit(struct quota_root *_root)
 {
-	struct dict_quota_root_iter *iter =
-		(struct dict_quota_root_iter *)_iter;
-	struct dict_quota *quota = (struct dict_quota *)_iter->quota;
+	struct dict_quota_root *root = (struct dict_quota_root *)_root;
 
-	if (iter->sent)
-		return NULL;
-
-	iter->sent = TRUE;
-	return &quota->root;
+	i_free(root->root.name);
+	i_free(root);
 }
 
-static int dict_quota_root_iter_deinit(struct quota_root_iter *iter)
+static bool
+dict_quota_add_storage(struct quota_root *root __attr_unused__,
+		       struct mail_storage *storage __attr_unused__)
 {
-	i_free(iter);
-	return 0;
+	return TRUE;
 }
 
-static struct quota_root *
-dict_quota_root_lookup(struct quota *_quota, const char *name)
+static void
+dict_quota_remove_storage(struct quota_root *root __attr_unused__,
+			  struct mail_storage *storage __attr_unused__)
 {
-	struct dict_quota *quota = (struct dict_quota *)_quota;
-
-	if (*name == '\0')
-		return &quota->root;
-	else
-		return NULL;
-}
-
-static const char *
-dict_quota_root_get_name(struct quota_root *root __attr_unused__)
-{
-	return "";
 }
 
 static const char *const *
@@ -115,29 +68,18 @@
 }
 
 static int
-dict_quota_root_create(struct quota *_quota,
-		       const char *name __attr_unused__,
-		       struct quota_root **root_r __attr_unused__)
-{
-	struct dict_quota *quota = (struct dict_quota *)_quota;
-
-        quota->error = "Permission denied";
-	return -1;
-}
-
-static int
-dict_quota_get_resource(struct quota_root *root, const char *name,
+dict_quota_get_resource(struct quota_root *_root, const char *name,
 			uint64_t *value_r, uint64_t *limit_r)
 {
-	struct dict_quota *quota = (struct dict_quota *)root->quota;
+	struct dict_quota_root *root = (struct dict_quota_root *)_root;
 	const char *value;
 	int ret;
 
-	if (quota->dict == NULL)
+	if (root->dict == NULL)
 		return 0;
 
 	t_push();
-	ret = dict_lookup(quota->dict, unsafe_data_stack_pool,
+	ret = dict_lookup(root->dict, unsafe_data_stack_pool,
 			  t_strconcat(DICT_QUOTA_LIMIT_PATH, name, NULL),
 			  &value);
 	*limit_r = value == NULL ? 0 : strtoull(value, NULL, 10);
@@ -146,7 +88,7 @@
 		/* resource doesn't exist */
 		*value_r = 0;
 	} else {
-		ret = dict_lookup(quota->dict, unsafe_data_stack_pool,
+		ret = dict_lookup(root->dict, unsafe_data_stack_pool,
 				  t_strconcat(DICT_QUOTA_CURRENT_PATH,
 					      name, NULL), &value);
 		*value_r = value == NULL ? 0 : strtoull(value, NULL, 10);
@@ -164,30 +106,30 @@
 			const char *name __attr_unused__,
 			uint64_t value __attr_unused__)
 {
-	struct dict_quota *quota = (struct dict_quota *)root->quota;
-
-	quota->error = "Permission denied";
+	quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
 	return -1;
 }
 
-static struct quota_transaction_context *
-dict_quota_transaction_begin(struct quota *_quota)
+static struct quota_root_transaction_context *
+dict_quota_transaction_begin(struct quota_root *_root,
+			     struct quota_transaction_context *_ctx)
 {
-	struct dict_quota *quota = (struct dict_quota *)_quota;
-	struct quota_transaction_context *ctx;
+	struct dict_quota_root *root = (struct dict_quota_root *)_root;
+	struct quota_root_transaction_context *ctx;
 	const char *value;
 
-	ctx = i_new(struct quota_transaction_context, 1);
-	ctx->quota = _quota;
+	ctx = i_new(struct quota_root_transaction_context, 1);
+	ctx->root = _root;
+	ctx->ctx = _ctx;
 
-	if (quota->dict != NULL) {
+	if (root->dict != NULL) {
 		t_push();
-		(void)dict_lookup(quota->dict, unsafe_data_stack_pool,
+		(void)dict_lookup(root->dict, unsafe_data_stack_pool,
 				  DICT_QUOTA_LIMIT_PATH"storage", &value);
 		ctx->storage_limit = value == NULL ? 0 :
 			strtoull(value, NULL, 10);
 
-		(void)dict_lookup(quota->dict, unsafe_data_stack_pool,
+		(void)dict_lookup(root->dict, unsafe_data_stack_pool,
 				  DICT_QUOTA_CURRENT_PATH"storage", &value);
 		ctx->storage_current = value == NULL ? 0 :
 			strtoull(value, NULL, 10);
@@ -200,14 +142,14 @@
 }
 
 static int
-dict_quota_transaction_commit(struct quota_transaction_context *ctx)
+dict_quota_transaction_commit(struct quota_root_transaction_context *ctx)
 {
-	struct dict_quota *quota = (struct dict_quota *)ctx->quota;
+	struct dict_quota_root *root = (struct dict_quota_root *)ctx->root;
 
-	if (quota->dict != NULL) {
+	if (root->dict != NULL) {
 		struct dict_transaction_context *dt;
 
-		dt = dict_transaction_begin(quota->dict);
+		dt = dict_transaction_begin(root->dict);
 		dict_atomic_inc(dt, DICT_QUOTA_CURRENT_PATH"storage",
 				ctx->bytes_diff);
 		if (dict_transaction_commit(dt) < 0)
@@ -219,13 +161,13 @@
 }
 
 static void
-dict_quota_transaction_rollback(struct quota_transaction_context *ctx)
+dict_quota_transaction_rollback(struct quota_root_transaction_context *ctx)
 {
 	i_free(ctx);
 }
 
 static int
-dict_quota_try_alloc_bytes(struct quota_transaction_context *ctx,
+dict_quota_try_alloc_bytes(struct quota_root_transaction_context *ctx,
 			   uoff_t size, bool *too_large_r)
 {
 	*too_large_r = size > ctx->storage_limit;
@@ -238,7 +180,7 @@
 }
 
 static int
-dict_quota_try_alloc(struct quota_transaction_context *ctx,
+dict_quota_try_alloc(struct quota_root_transaction_context *ctx,
 		     struct mail *mail, bool *too_large_r)
 {
 	uoff_t size;
@@ -251,7 +193,7 @@
 }
 
 static void
-dict_quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
+dict_quota_alloc(struct quota_root_transaction_context *ctx, struct mail *mail)
 {
 	uoff_t size;
 
@@ -261,7 +203,7 @@
 }
 
 static void
-dict_quota_free(struct quota_transaction_context *ctx, struct mail *mail)
+dict_quota_free(struct quota_root_transaction_context *ctx, struct mail *mail)
 {
 	uoff_t size;
 
@@ -270,42 +212,28 @@
 		ctx->bytes_diff -= size;
 }
 
-static const char *dict_quota_last_error(struct quota *_quota)
-{
-	struct dict_quota *quota = (struct dict_quota *)_quota;
-
-	return quota->error;
-}
-
-struct quota dict_quota = {
+struct quota_backend quota_backend_dict = {
 	"dict",
 
-	dict_quota_init,
-	dict_quota_deinit,
+	{
+		dict_quota_init,
+		dict_quota_deinit,
 
-	dict_quota_root_iter_init,
-	dict_quota_root_iter_next,
-	dict_quota_root_iter_deinit,
+		dict_quota_add_storage,
+		dict_quota_remove_storage,
 
-	dict_quota_root_lookup,
-
-	dict_quota_root_get_name,
-	dict_quota_root_get_resources,
+		dict_quota_root_get_resources,
 
-	dict_quota_root_create,
-	dict_quota_get_resource,
-	dict_quota_set_resource,
+		dict_quota_get_resource,
+		dict_quota_set_resource,
 
-	dict_quota_transaction_begin,
-	dict_quota_transaction_commit,
-	dict_quota_transaction_rollback,
+		dict_quota_transaction_begin,
+		dict_quota_transaction_commit,
+		dict_quota_transaction_rollback,
 
-	dict_quota_try_alloc,
-	dict_quota_try_alloc_bytes,
-	dict_quota_alloc,
-	dict_quota_free,
-
-	dict_quota_last_error,
-
-	ARRAY_INIT
+		dict_quota_try_alloc,
+		dict_quota_try_alloc_bytes,
+		dict_quota_alloc,
+		dict_quota_free
+	}
 };
--- a/src/plugins/quota/quota-dirsize.c	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/quota/quota-dirsize.c	Wed Mar 01 11:14:59 2006 +0200
@@ -1,9 +1,10 @@
-/* Copyright (C) 2005 Timo Sirainen */
+/* Copyright (C) 2005-2006 Timo Sirainen */
 
 /* Quota reporting based on simply summing sizes of all files in mailbox
    together. */
 
 #include "lib.h"
+#include "array.h"
 #include "str.h"
 #include "quota-private.h"
 
@@ -12,107 +13,60 @@
 #include <dirent.h>
 #include <sys/stat.h>
 
-struct dirsize_quota {
-	struct quota quota;
-
-	pool_t pool;
-	const char *path;
-	const char *error;
+struct dirsize_quota_root {
 	struct quota_root root;
 
 	uint64_t storage_limit;
 };
 
-struct dirsize_quota_root_iter {
-	struct quota_root_iter iter;
-
-	bool sent;
-};
+extern struct quota_backend quota_backend_dirsize;
 
-extern struct quota dirsize_quota;
-
-static struct quota *dirsize_quota_init(const char *data)
+static struct quota_root *
+dirsize_quota_init(struct quota_setup *setup, const char *name)
 {
-	struct dirsize_quota *quota;
+	struct dirsize_quota_root *root;
 	const char *const *args;
-	pool_t pool;
 
-	pool = pool_alloconly_create("quota", 1024);
-	quota = p_new(pool, struct dirsize_quota, 1);
-	quota->pool = pool;
-	quota->quota = dirsize_quota;
+	root = i_new(struct dirsize_quota_root, 1);
+	root->root.name = i_strdup(name);
+	root->root.v = quota_backend_dirsize.v;
+
+	t_push();
+	args = t_strsplit(setup->data, ":");
 
-	args = t_strsplit(data, ":");
-	quota->path = p_strdup(pool, args[0]);
+	for (; *args != '\0'; args++) {
+		if (strncmp(*args, "storage=", 8) == 0)
+			root->storage_limit = strtoull(*args + 8, NULL, 10);
+	}
+	t_pop();
 
-	for (args++; *args != '\0'; args++) {
-		if (strncmp(*args, "storage=", 8) == 0)
-			quota->storage_limit = strtoull(*args + 8, NULL, 10);
+	if (getenv("DEBUG") != NULL) {
+		i_info("dirsize quota limit = %llukB",
+		       (unsigned long long)root->storage_limit);
 	}
 
-	if (getenv("DEBUG") != NULL) {
-		i_info("dirsize quota path = %s", quota->path);
-		i_info("dirsize quota limit = %llukB",
-		       (unsigned long long)quota->storage_limit);
-	}
-
-	quota->root.quota = &quota->quota;
-	return &quota->quota;
-}
-
-static void dirsize_quota_deinit(struct quota *_quota)
-{
-	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;
-
-	pool_unref(quota->pool);
+	return &root->root;
 }
 
-static struct quota_root_iter *
-dirsize_quota_root_iter_init(struct quota *quota,
-			     struct mailbox *box __attr_unused__)
+static void dirsize_quota_deinit(struct quota_root *_root)
 {
-	struct dirsize_quota_root_iter *iter;
+	struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
 
-	iter = i_new(struct dirsize_quota_root_iter, 1);
-	iter->iter.quota = quota;
-	return &iter->iter;
+	i_free(root->root.name);
+	i_free(root);
 }
 
-static struct quota_root *
-dirsize_quota_root_iter_next(struct quota_root_iter *_iter)
+static bool
+dirsize_quota_add_storage(struct quota_root *root __attr_unused__,
+			  struct mail_storage *storage __attr_unused__)
 {
-	struct dirsize_quota_root_iter *iter =
-		(struct dirsize_quota_root_iter *)_iter;
-	struct dirsize_quota *quota = (struct dirsize_quota *)_iter->quota;
-
-	if (iter->sent)
-		return NULL;
-
-	iter->sent = TRUE;
-	return &quota->root;
+	return TRUE;
 }
 
-static int dirsize_quota_root_iter_deinit(struct quota_root_iter *iter)
-{
-	i_free(iter);
-	return 0;
-}
-
-static struct quota_root *
-dirsize_quota_root_lookup(struct quota *_quota, const char *name)
+static void
+dirsize_quota_remove_storage(struct quota_root *root __attr_unused__,
+			     struct mail_storage *storage __attr_unused__)
 {
-	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;
-
-	if (*name == '\0')
-		return &quota->root;
-	else
-		return NULL;
-}
-
-static const char *
-dirsize_quota_root_get_name(struct quota_root *root __attr_unused__)
-{
-	return "";
 }
 
 static const char *const *
@@ -123,17 +77,6 @@
 	return resources;
 }
 
-static int
-dirsize_quota_root_create(struct quota *_quota,
-			  const char *name __attr_unused__,
-			  struct quota_root **root_r __attr_unused__)
-{
-	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;
-
-        quota->error = "Permission denied";
-	return -1;
-}
-
 static int get_dir_usage(const char *dir, uint64_t *value)
 {
 	DIR *dirp;
@@ -191,10 +134,32 @@
 }
 
 static int
-dirsize_quota_get_resource(struct quota_root *root, const char *name,
+get_quota_root_usage(struct dirsize_quota_root *root, uint64_t *value_r)
+{
+	struct mail_storage *const *storages;
+	unsigned int i, count;
+	const char *path;
+	bool is_file;
+
+	storages = array_get(&root->root.storages, &count);
+	for (i = 0; i < count; i++) {
+		path = mail_storage_get_mailbox_path(storages[i], "", &is_file);
+
+		if (get_dir_usage(path, value_r) < 0) {
+			quota_set_error(root->root.setup->quota,
+					"Internal quota calculation error");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+dirsize_quota_get_resource(struct quota_root *_root, const char *name,
 			   uint64_t *value_r, uint64_t *limit_r)
 {
-	struct dirsize_quota *quota = (struct dirsize_quota *)root->quota;
+	struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
 
 	*value_r = 0;
 	*limit_r = 0;
@@ -202,12 +167,11 @@
 	if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0)
 		return 0;
 
-	if (get_dir_usage(quota->path, value_r) < 0) {
-		quota->error = "Internal quota calculation error";
+	if (get_quota_root_usage(root, value_r) < 0)
 		return -1;
-	}
+
 	*value_r /= 1024;
-	*limit_r = quota->storage_limit;
+	*limit_r = root->storage_limit;
 	return 1;
 }
 
@@ -216,36 +180,37 @@
 			   const char *name __attr_unused__,
 			   uint64_t value __attr_unused__)
 {
-	struct dirsize_quota *quota = (struct dirsize_quota *)root->quota;
-
-	quota->error = "Permission denied";
+	quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
 	return -1;
 }
 
-static struct quota_transaction_context *
-dirsize_quota_transaction_begin(struct quota *_quota)
+static struct quota_root_transaction_context *
+dirsize_quota_transaction_begin(struct quota_root *_root,
+				struct quota_transaction_context *_ctx)
 {
-	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;
-	struct quota_transaction_context *ctx;
+	struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
+	struct quota_root_transaction_context *ctx;
 
-	ctx = i_new(struct quota_transaction_context, 1);
-	ctx->quota = _quota;
+	ctx = i_new(struct quota_root_transaction_context, 1);
+	ctx->root = _root;
+	ctx->ctx = _ctx;
 
 	/* Get dir usage only once at the beginning of transaction.
 	   When copying/appending lots of mails we don't want to re-read the
 	   entire directory structure after each mail. */
-	if (get_dir_usage(quota->path, &ctx->storage_current) < 0 ||
+	if (get_quota_root_usage(root, &ctx->storage_current) < 0 ||
 	    ctx->storage_current == (uoff_t)-1) {
                 ctx->storage_current = (uoff_t)-1;
-		quota->error = "Internal quota calculation error";
+		quota_set_error(_root->setup->quota,
+				"Internal quota calculation error");
 	}
 
-	ctx->storage_limit = quota->storage_limit * 1024;
+	ctx->storage_limit = root->storage_limit * 1024;
 	return ctx;
 }
 
 static int
-dirsize_quota_transaction_commit(struct quota_transaction_context *ctx)
+dirsize_quota_transaction_commit(struct quota_root_transaction_context *ctx)
 {
 	int ret = ctx->storage_current == (uoff_t)-1 ? -1 : 0;
 
@@ -254,13 +219,13 @@
 }
 
 static void
-dirsize_quota_transaction_rollback(struct quota_transaction_context *ctx)
+dirsize_quota_transaction_rollback(struct quota_root_transaction_context *ctx)
 {
 	i_free(ctx);
 }
 
 static int
-dirsize_quota_try_alloc_bytes(struct quota_transaction_context *ctx,
+dirsize_quota_try_alloc_bytes(struct quota_root_transaction_context *ctx,
 			      uoff_t size, bool *too_large_r)
 {
 	if (ctx->storage_current == (uoff_t)-1)
@@ -276,7 +241,7 @@
 }
 
 static int
-dirsize_quota_try_alloc(struct quota_transaction_context *ctx,
+dirsize_quota_try_alloc(struct quota_root_transaction_context *ctx,
 			struct mail *mail, bool *too_large_r)
 {
 	uoff_t size;
@@ -292,7 +257,8 @@
 }
 
 static void
-dirsize_quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
+dirsize_quota_alloc(struct quota_root_transaction_context *ctx,
+		    struct mail *mail)
 {
 	uoff_t size;
 
@@ -302,7 +268,8 @@
 }
 
 static void
-dirsize_quota_free(struct quota_transaction_context *ctx, struct mail *mail)
+dirsize_quota_free(struct quota_root_transaction_context *ctx,
+		   struct mail *mail)
 {
 	uoff_t size;
 
@@ -311,42 +278,28 @@
 		ctx->bytes_diff -= size;
 }
 
-static const char *dirsize_quota_last_error(struct quota *_quota)
-{
-	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;
-
-	return quota->error;
-}
-
-struct quota dirsize_quota = {
+struct quota_backend quota_backend_dirsize = {
 	"dirsize",
 
-	dirsize_quota_init,
-	dirsize_quota_deinit,
+	{
+		dirsize_quota_init,
+		dirsize_quota_deinit,
 
-	dirsize_quota_root_iter_init,
-	dirsize_quota_root_iter_next,
-	dirsize_quota_root_iter_deinit,
+		dirsize_quota_add_storage,
+		dirsize_quota_remove_storage,
 
-	dirsize_quota_root_lookup,
-
-	dirsize_quota_root_get_name,
-	dirsize_quota_root_get_resources,
+		dirsize_quota_root_get_resources,
 
-	dirsize_quota_root_create,
-	dirsize_quota_get_resource,
-	dirsize_quota_set_resource,
+		dirsize_quota_get_resource,
+		dirsize_quota_set_resource,
 
-	dirsize_quota_transaction_begin,
-	dirsize_quota_transaction_commit,
-	dirsize_quota_transaction_rollback,
+		dirsize_quota_transaction_begin,
+		dirsize_quota_transaction_commit,
+		dirsize_quota_transaction_rollback,
 
-	dirsize_quota_try_alloc,
-	dirsize_quota_try_alloc_bytes,
-	dirsize_quota_alloc,
-	dirsize_quota_free,
-
-	dirsize_quota_last_error,
-
-	ARRAY_INIT
+		dirsize_quota_try_alloc,
+		dirsize_quota_try_alloc_bytes,
+		dirsize_quota_alloc,
+		dirsize_quota_free
+	}
 };
--- a/src/plugins/quota/quota-fs.c	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/quota/quota-fs.c	Wed Mar 01 11:14:59 2006 +0200
@@ -1,8 +1,9 @@
-/* Copyright (C) 2005 Timo Sirainen */
+/* Copyright (C) 2005-2006 Timo Sirainen */
 
 /* Only for reporting filesystem quota */
 
 #include "lib.h"
+#include "array.h"
 #include "str.h"
 #include "quota-private.h"
 #include "quota-fs.h"
@@ -29,22 +30,23 @@
 #  define MNTTYPE_IGNORE "ignore"
 #endif
 
-struct fs_quota {
-	struct quota quota;
-
-	pool_t pool;
-	const char *device;
-	const char *error;
+struct fs_quota_mountpoint {
+	char *mount_path;
+	char *device_path;
 
 	unsigned int blk_size;
-	uid_t uid;
 
 #ifdef HAVE_Q_QUOTACTL
 	int fd;
-	const char *path;
+	char *path;
 #endif
+};
 
+struct fs_quota_root {
 	struct quota_root root;
+
+	uid_t uid;
+	struct fs_quota_mountpoint *mount;
 };
 
 struct fs_quota_root_iter {
@@ -53,23 +55,62 @@
 	bool sent;
 };
 
-extern struct quota fs_quota;
+extern struct quota_backend quota_backend_fs;
+
+static struct quota_root *
+fs_quota_init(struct quota_setup *setup __attr_unused__, const char *name)
+{
+	struct fs_quota_root *root;
+
+	root = i_new(struct fs_quota_root, 1);
+	root->root.name = i_strdup(name);
+	root->root.v = quota_backend_fs.v;
+	root->uid = geteuid();
+
+	return &root->root;
+}
 
-static int path_to_device(const char *path, unsigned int *blk_size_r,
-			  const char **device_path_r, const char **mount_path_r)
+static void fs_quota_mountpoint_free(struct fs_quota_mountpoint *mount)
 {
+#ifdef HAVE_Q_QUOTACTL
+	if (mount->fd != -1) {
+		if (close(mount->fd) < 0)
+			i_error("close(%s) failed: %m", mount->path);
+	}
+	i_free(mount->path);
+#endif
+
+	i_free(mount->device_path);
+	i_free(mount->mount_path);
+	i_free(mount);
+}
+
+static void fs_quota_deinit(struct quota_root *_root)
+{
+	struct fs_quota_root *root = (struct fs_quota_root *)_root;
+
+	if (root->mount != NULL)
+		fs_quota_mountpoint_free(root->mount);
+	i_free(root->root.name);
+	i_free(root);
+}
+
+static struct fs_quota_mountpoint *fs_quota_mountpoint_get(const char *dir)
+{
+	struct fs_quota_mountpoint *mount;
 #ifdef HAVE_STATFS_MNTFROMNAME
 	struct statfs buf;
 
-	if (statfs(path, &buf) < 0) {
-		i_error("statfs(%s) failed: %m", path);
-		return -1;
+	if (statfs(dir, &buf) < 0) {
+		i_error("statfs(%s) failed: %m", dir);
+		return NULL;
 	}
 
-	*blk_size_r = buf.f_bsize;
-	*device_path_r = t_strdup(buf.f_mntfromname);
-	*mount_path_r = t_strdup(buf.f_mntonname);
-	return 0;
+	mount = i_new(struct fs_quota_mountpoint, 1);
+	mount->blk_size = buf.f_bsize;
+	mount->device_path = i_strdup(buf.f_mntfromname);
+	mount->mount_path = i_strdup(buf.f_mntonname);
+	return mount;
 #else
 #ifdef HAVE_SYS_MNTTAB_H
 	struct mnttab ent;
@@ -77,23 +118,22 @@
 	struct mntent *ent;
 #endif
 	struct stat st, st2;
+	const char *device_path = NULL, *mount_path = NULL;
+	unsigned int blk_size;
 	FILE *f;
 
-	*device_path_r = NULL;
-	*mount_path_r = NULL;
+	if (stat(dir, &st) < 0) {
+		i_error("stat(%s) failed: %m", dir);
+		return NULL;
+	}
+	blk_size = st.st_blksize;
 
-	if (stat(path, &st) < 0) {
-		i_error("stat(%s) failed: %m", path);
-		return -1;
-	}
-	*blk_size_r = st.st_blksize;
-
+#ifdef HAVE_SYS_MNTTAB_H
 	f = fopen(MTAB_PATH, "r");
 	if (f == NULL) {
 		i_error("open(%s) failed: %m", MTAB_PATH);
-		return -1;
+		return NULL;
 	}
-#ifdef HAVE_SYS_MNTTAB_H
 	while ((getmntent(f, &ent)) == 0) {
 		if (strcmp(ent.mnt_fstype, MNTTYPE_SWAP) == 0 ||
 		    strcmp(ent.mnt_fstype, MNTTYPE_IGNORE) == 0)
@@ -101,13 +141,18 @@
 
 		if (stat(ent.mnt_special, &st2) == 0 &&
 		    CMP_DEV_T(st.st_dev, st2.st_dev)) {
-			*device_path_r = t_strdup(ent.mnt_special);
-			*mount_path_r = t_strdup(ent.mnt_mountp);
+			device_path = ent.mnt_special;
+			mount_path = ent.mnt_mountp;
 			break;
 		}
 	}
 	fclose(f);
 #else
+	f = setmntent(MTAB_PATH, "r");
+	if (f == NULL) {
+		i_error("setmntent(%s) failed: %m", MTAB_PATH);
+		return NULL;
+	}
 	while ((ent = getmntent(f)) != NULL) {
 		if (strcmp(ent->mnt_type, MNTTYPE_SWAP) == 0 ||
 		    strcmp(ent->mnt_type, MNTTYPE_IGNORE) == 0)
@@ -115,118 +160,82 @@
 
 		if (stat(ent->mnt_fsname, &st2) == 0 &&
 		    CMP_DEV_T(st.st_dev, st2.st_dev)) {
-			*device_path_r = t_strdup(ent->mnt_fsname);
-			*mount_path_r = t_strdup(ent->mnt_dir);
+			device_path = ent->mnt_fsname;
+			mount_path = ent->mnt_dir;
 			break;
 		}
 	}
 	endmntent(f);
 #endif
-	return 0;
+	if (device_path == NULL) {
+		if (getenv("DEBUG") != NULL) {
+			i_info("fs quota: mount path for %s not found from %s",
+			       dir, MTAB_PATH);
+		}
+		return NULL;
+	}
+
+	mount = i_new(struct fs_quota_mountpoint, 1);
+	mount->blk_size = blk_size;
+	mount->device_path = i_strdup(device_path);
+	mount->mount_path = i_strdup(mount_path);
+
+	return mount;
 #endif
 }
 
-static struct quota *fs_quota_init(const char *data)
+static bool fs_quota_add_storage(struct quota_root *_root,
+				 struct mail_storage *storage)
 {
-	struct fs_quota *quota;
-	const char *device, *mount_point;
-	pool_t pool;
-	unsigned int blk_size = 0;
+	struct fs_quota_root *root = (struct fs_quota_root *)_root;
+	struct fs_quota_mountpoint *mount;
+	const char *dir;
+	bool is_file;
+
+	dir = mail_storage_get_mailbox_path(storage, "", &is_file);
 
 	if (getenv("DEBUG") != NULL)
-		i_info("fs quota path = %s", data);
-
-	if (path_to_device(data, &blk_size, &device, &mount_point) < 0)
-		return NULL;
+		i_info("fs quota add storage dir = %s", dir);
 
-	if (getenv("DEBUG") != NULL) {
-		i_info("fs quota block device = %s",
-		       device == NULL ? "(unknown)" : device);
-		i_info("fs quota mount point = %s",
-		       mount_point == NULL ? "(unknown)" : mount_point);
+	mount = fs_quota_mountpoint_get(dir);
+	if (root->mount == NULL) {
+		if (mount == NULL) {
+			/* Not found */
+			return TRUE;
+		}
+		root->mount = mount;
+	} else {
+		bool match = strcmp(root->mount->mount_path,
+				    mount->mount_path) == 0;
+
+		fs_quota_mountpoint_free(mount);
+		if (!match) {
+			/* different mountpoints, can't use this */
+			return FALSE;
+		}
+		mount = root->mount;
 	}
 
-	if (device == NULL)
-		return NULL;
-
-	pool = pool_alloconly_create("quota", 1024);
-	quota = p_new(pool, struct fs_quota, 1);
-	quota->pool = pool;
-	quota->quota = fs_quota;
-	quota->device = p_strdup(pool, device);
-	quota->uid = geteuid();
-	quota->blk_size = blk_size;
-
-#ifdef HAVE_Q_QUOTACTL
-	quota->path = p_strconcat(pool, mount_point, "/quotas", NULL);
-	quota->fd = open(quota->path, O_RDONLY);
-	if (quota->fd == -1 && errno != ENOENT)
-		i_error("open(%s) failed: %m", quota->path);
-#endif
-
-	quota->root.quota = &quota->quota;
-	return &quota->quota;
-}
-
-static void fs_quota_deinit(struct quota *_quota)
-{
-	struct fs_quota *quota = (struct fs_quota *)_quota;
+	if (getenv("DEBUG") != NULL) {
+		i_info("fs quota block device = %s", mount->device_path);
+		i_info("fs quota mount point = %s", mount->mount_path);
+	}
 
 #ifdef HAVE_Q_QUOTACTL
-	if (quota->fd != -1) {
-		if (close(quota->fd) < 0)
-			i_error("close(%s) failed: %m", quota->path);
+	if (mount->path == NULL) {
+		mount->path = i_strconcat(mount->mount_path, "/quotas", NULL);
+		mount->fd = open(mount->path, O_RDONLY);
+		if (mount->fd == -1 && errno != ENOENT)
+			i_error("open(%s) failed: %m", mount->path);
 	}
 #endif
-	pool_unref(quota->pool);
-}
-
-static struct quota_root_iter *
-fs_quota_root_iter_init(struct quota *quota,
-			struct mailbox *box __attr_unused__)
-{
-	struct fs_quota_root_iter *iter;
-
-	iter = i_new(struct fs_quota_root_iter, 1);
-	iter->iter.quota = quota;
-	return &iter->iter;
+	return TRUE;
 }
 
-static struct quota_root *
-fs_quota_root_iter_next(struct quota_root_iter *_iter)
-{
-	struct fs_quota_root_iter *iter =
-		(struct fs_quota_root_iter *)_iter;
-	struct fs_quota *quota = (struct fs_quota *)_iter->quota;
-
-	if (iter->sent)
-		return NULL;
-
-	iter->sent = TRUE;
-	return &quota->root;
-}
-
-static int fs_quota_root_iter_deinit(struct quota_root_iter *iter)
+static void
+fs_quota_remove_storage(struct quota_root *root __attr_unused__,
+			struct mail_storage *storage __attr_unused__)
 {
-	i_free(iter);
-	return 0;
-}
-
-static struct quota_root *
-fs_quota_root_lookup(struct quota *_quota, const char *name)
-{
-	struct fs_quota *quota = (struct fs_quota *)_quota;
-
-	if (*name == '\0')
-		return &quota->root;
-	else
-		return NULL;
-}
-
-static const char *
-fs_quota_root_get_name(struct quota_root *root __attr_unused__)
-{
-	return "";
 }
 
 static const char *const *
@@ -238,21 +247,10 @@
 }
 
 static int
-fs_quota_root_create(struct quota *_quota,
-		     const char *name __attr_unused__,
-		     struct quota_root **root_r __attr_unused__)
-{
-	struct fs_quota *quota = (struct fs_quota *)_quota;
-
-        quota->error = "Permission denied";
-	return -1;
-}
-
-static int
-fs_quota_get_resource(struct quota_root *root, const char *name,
+fs_quota_get_resource(struct quota_root *_root, const char *name,
 		      uint64_t *value_r, uint64_t *limit_r)
 {
-	struct fs_quota *quota = (struct fs_quota *)root->quota;
+	struct fs_quota_root *root = (struct fs_quota_root *)_root;
 	struct dqblk dqblk;
 #ifdef HAVE_Q_QUOTACTL
 	struct quotctl ctl;
@@ -261,32 +259,33 @@
 	*value_r = 0;
 	*limit_r = 0;
 
-	if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0)
+	if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0 || root->mount == NULL)
 		return 0;
 
 #ifdef HAVE_QUOTACTL
-	if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), quota->device,
-		     quota->uid, (void *)&dqblk) < 0) {
-		i_error("quotactl(Q_GETQUOTA, %s) failed: %m", quota->device);
-		quota->error = "Internal quota error";
+	if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), root->mount->device_path,
+		     root->uid, (void *)&dqblk) < 0) {
+		i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
+			root->mount->device_path);
+		quota_set_error(_root->setup->quota, "Internal quota error");
 		return -1;
 	}
 #else
 	/* Solaris */
-	if (quota->fd == -1)
+	if (root->mount->fd == -1)
 		return 0;
 
 	ctl.op = Q_GETQUOTA;
-	ctl.uid = quota->uid;
+	ctl.uid = root->uid;
 	ctl.addr = (caddr_t)&dqblk;
-	if (ioctl(quota->fd, Q_QUOTACTL, &ctl) < 0) {
-		i_error("ioctl(%s, Q_QUOTACTL) failed: %m", quota->path);
-		quota->error = "Internal quota error";
+	if (ioctl(root->mount->fd, Q_QUOTACTL, &ctl) < 0) {
+		i_error("ioctl(%s, Q_QUOTACTL) failed: %m", root->mount->path);
+		quota_set_error(_root->setup->quota, "Internal quota error");
 		return -1;
 	}
 #endif
-	*value_r =  dqblk.dqb_curblocks * quota->blk_size / 1024;
-	*limit_r = dqblk.dqb_bsoftlimit * quota->blk_size / 1024;
+	*value_r = dqblk.dqb_curblocks * root->mount->blk_size / 1024;
+	*limit_r = dqblk.dqb_bsoftlimit * root->mount->blk_size / 1024;
 	return 1;
 }
 
@@ -295,37 +294,37 @@
 		      const char *name __attr_unused__,
 		      uint64_t value __attr_unused__)
 {
-	struct fs_quota *quota = (struct fs_quota *)root->quota;
-
-	quota->error = "Permission denied";
+	quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
 	return -1;
 }
 
-static struct quota_transaction_context *
-fs_quota_transaction_begin(struct quota *quota)
+static struct quota_root_transaction_context *
+fs_quota_transaction_begin(struct quota_root *root,
+			   struct quota_transaction_context *ctx)
 {
-	struct quota_transaction_context *ctx;
+	struct quota_root_transaction_context *root_ctx;
 
-	ctx = i_new(struct quota_transaction_context, 1);
-	ctx->quota = quota;
-	return ctx;
+	root_ctx = i_new(struct quota_root_transaction_context, 1);
+	root_ctx->root = root;
+	root_ctx->ctx = ctx;
+	return root_ctx;
 }
 
 static int
-fs_quota_transaction_commit(struct quota_transaction_context *ctx)
+fs_quota_transaction_commit(struct quota_root_transaction_context *ctx)
 {
 	i_free(ctx);
 	return 0;
 }
 
 static void
-fs_quota_transaction_rollback(struct quota_transaction_context *ctx)
+fs_quota_transaction_rollback(struct quota_root_transaction_context *ctx)
 {
 	i_free(ctx);
 }
 
 static int
-fs_quota_try_alloc(struct quota_transaction_context *ctx __attr_unused__,
+fs_quota_try_alloc(struct quota_root_transaction_context *ctx __attr_unused__,
 		   struct mail *mail __attr_unused__,
 		   bool *too_large_r __attr_unused__)
 {
@@ -334,7 +333,8 @@
 }
 
 static int
-fs_quota_try_alloc_bytes(struct quota_transaction_context *ctx __attr_unused__,
+fs_quota_try_alloc_bytes(struct quota_root_transaction_context *ctx
+			 	__attr_unused__,
 			 uoff_t size __attr_unused__,
 			 bool *too_large_r __attr_unused__)
 {
@@ -343,57 +343,43 @@
 }
 
 static void
-fs_quota_alloc(struct quota_transaction_context *ctx __attr_unused__,
-		struct mail *mail __attr_unused__)
+fs_quota_alloc(struct quota_root_transaction_context *ctx __attr_unused__,
+	       struct mail *mail __attr_unused__)
 {
 	/* no-op */
 }
 
 static void
-fs_quota_free(struct quota_transaction_context *ctx __attr_unused__,
+fs_quota_free(struct quota_root_transaction_context *ctx __attr_unused__,
 	      struct mail *mail __attr_unused__)
 {
 	/* no-op */
 }
 
-static const char *fs_quota_last_error(struct quota *_quota)
-{
-	struct fs_quota *quota = (struct fs_quota *)_quota;
-
-	return quota->error;
-}
-
-struct quota fs_quota = {
+struct quota_backend quota_backend_fs = {
 	"fs",
 
-	fs_quota_init,
-	fs_quota_deinit,
+	{
+		fs_quota_init,
+		fs_quota_deinit,
 
-	fs_quota_root_iter_init,
-	fs_quota_root_iter_next,
-	fs_quota_root_iter_deinit,
+		fs_quota_add_storage,
+		fs_quota_remove_storage,
 
-	fs_quota_root_lookup,
-
-	fs_quota_root_get_name,
-	fs_quota_root_get_resources,
+		fs_quota_root_get_resources,
 
-	fs_quota_root_create,
-	fs_quota_get_resource,
-	fs_quota_set_resource,
-
-	fs_quota_transaction_begin,
-	fs_quota_transaction_commit,
-	fs_quota_transaction_rollback,
+		fs_quota_get_resource,
+		fs_quota_set_resource,
 
-	fs_quota_try_alloc,
-	fs_quota_try_alloc_bytes,
-	fs_quota_alloc,
-	fs_quota_free,
+		fs_quota_transaction_begin,
+		fs_quota_transaction_commit,
+		fs_quota_transaction_rollback,
 
-	fs_quota_last_error,
-
-	ARRAY_INIT
+		fs_quota_try_alloc,
+		fs_quota_try_alloc_bytes,
+		fs_quota_alloc,
+		fs_quota_free
+	}
 };
 
 #endif
--- a/src/plugins/quota/quota-plugin.c	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/quota/quota-plugin.c	Wed Mar 01 11:14:59 2006 +0200
@@ -11,16 +11,19 @@
 extern void (*hook_mail_storage_created)(struct mail_storage *storage);
 
 void (*quota_next_hook_mail_storage_created)(struct mail_storage *storage);
-struct quota *quota = NULL;
+
+struct quota *quota;
 
 void quota_plugin_init(void)
 {
 	const char *env;
 
 	env = getenv("QUOTA");
-	quota = env == NULL ? NULL : quota_init(env);
+	if (env != NULL) {
+		quota = quota_init();
+		/* Currently we support only one quota setup */
+		(void)quota_setup_init(quota, env, TRUE);
 
-	if (quota != NULL) {
 		quota_next_hook_mail_storage_created =
 			hook_mail_storage_created;
 		hook_mail_storage_created = quota_mail_storage_created;
--- a/src/plugins/quota/quota-private.h	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/quota/quota-private.h	Wed Mar 01 11:14:59 2006 +0200
@@ -9,58 +9,91 @@
 extern unsigned int quota_module_id;
 
 struct quota {
-	const char *name;
+	array_t ARRAY_DEFINE(setups, struct quota_setup *);
+	char *last_error;
+};
 
-	struct quota *(*init)(const char *data);
-	void (*deinit)(struct quota *quota);
+struct quota_setup {
+	struct quota *quota;
+
+	struct quota_backend *backend;
+	char *data;
+
+	/* List of quota roots. It's array because there shouldn't be many. */
+	array_t ARRAY_DEFINE(roots, struct quota_root *);
 
-	struct quota_root_iter *
-		(*root_iter_init)(struct quota *quota, struct mailbox *box);
-	struct quota_root *(*root_iter_next)(struct quota_root_iter *iter);
-	int (*root_iter_deinit)(struct quota_root_iter *iter);
+	unsigned int user_root:1;
+};
+
+struct quota_backend_vfuncs {
+	struct quota_root *(*init)(struct quota_setup *setup, const char *name);
+	void (*deinit)(struct quota_root *root);
 
-	struct quota_root *(*root_lookup)(struct quota *quota,
-					  const char *name);
+	bool (*add_storage)(struct quota_root *root,
+			    struct mail_storage *storage);
+	void (*remove_storage)(struct quota_root *root,
+			       struct mail_storage *storage);
 
-	const char *(*root_get_name)(struct quota_root *root);
-	const char *const *(*root_get_resources)(struct quota_root *root);
-
-	int (*root_create)(struct quota *quota, const char *name,
-			   struct quota_root **root_r);
+	const char *const *(*get_resources)(struct quota_root *root);
 	int (*get_resource)(struct quota_root *root, const char *name,
 			    uint64_t *value_r, uint64_t *limit_r);
 	int (*set_resource)(struct quota_root *root,
 			    const char *name, uint64_t value);
 
-	struct quota_transaction_context *
-		(*transaction_begin)(struct quota *quota);
-	int (*transaction_commit)(struct quota_transaction_context *ctx);
-	void (*transaction_rollback)(struct quota_transaction_context *ctx);
+	struct quota_root_transaction_context *
+		(*transaction_begin)(struct quota_root *root,
+				     struct quota_transaction_context *ctx);
+	int (*transaction_commit)(struct quota_root_transaction_context *ctx);
+	void (*transaction_rollback)
+		(struct quota_root_transaction_context *ctx);
 
-	int (*try_alloc)(struct quota_transaction_context *ctx,
+	int (*try_alloc)(struct quota_root_transaction_context *ctx,
 			 struct mail *mail, bool *too_large_r);
-	int (*try_alloc_bytes)(struct quota_transaction_context *ctx,
+	int (*try_alloc_bytes)(struct quota_root_transaction_context *ctx,
 			       uoff_t size, bool *too_large_r);
-	void (*alloc)(struct quota_transaction_context *ctx, struct mail *mail);
-	void (*free)(struct quota_transaction_context *ctx, struct mail *mail);
+	void (*alloc)(struct quota_root_transaction_context *ctx,
+		      struct mail *mail);
+	void (*free)(struct quota_root_transaction_context *ctx,
+		     struct mail *mail);
+};
 
-	const char *(*last_error)(struct quota *quota);
-
-	/* Module-specific contexts. See quota_module_id. */
-	array_t ARRAY_DEFINE(quota_module_contexts, void);
+struct quota_backend {
+	const char *name;
+	struct quota_backend_vfuncs v;
 };
 
 struct quota_root {
-	struct quota *quota;
+	struct quota_setup *setup;
+
+	/* Unique quota root name. */
+	char *name;
+
+	struct quota_backend_vfuncs v;
+
+	/* Mail storages using this quota root. */
+	array_t ARRAY_DEFINE(storages, struct mail_storage *);
+	/* Module-specific contexts. See quota_module_id. */
+	array_t ARRAY_DEFINE(quota_module_contexts, void);
+
+	unsigned int user_root:1;
 };
 
 struct quota_root_iter {
-	struct quota *quota;
+	struct quota_mail_storage *qstorage;
+	unsigned int idx;
 };
 
 struct quota_transaction_context {
 	struct quota *quota;
 
+	array_t ARRAY_DEFINE(root_transactions,
+			     struct quota_root_transaction_context *);
+};
+
+struct quota_root_transaction_context {
+	struct quota_root *root;
+	struct quota_transaction_context *ctx;
+
 	int count_diff;
 	int64_t bytes_diff;
 
@@ -68,4 +101,16 @@
 	uint64_t storage_current;
 };
 
+/* Register storage to all user's quota roots. */
+void quota_add_user_storage(struct quota *quota, struct mail_storage *storage);
+
+/* Likn root and storage together. Returns TRUE if successful, FALSE if it
+   can't be done (eg. different filesystems with filesystem quota) */
+bool quota_mail_storage_add_root(struct mail_storage *storage,
+				 struct quota_root *root);
+void quota_mail_storage_remove_root(struct mail_storage *storage,
+				    struct quota_root *root);
+
+void quota_set_error(struct quota *quota, const char *errormsg);
+
 #endif
--- a/src/plugins/quota/quota-storage.c	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/quota/quota-storage.c	Wed Mar 01 11:14:59 2006 +0200
@@ -4,7 +4,7 @@
 #include "array.h"
 #include "istream.h"
 #include "mail-storage-private.h"
-#include "quota.h"
+#include "quota-private.h"
 #include "quota-plugin.h"
 
 #include <sys/stat.h>
@@ -15,6 +15,10 @@
 
 struct quota_mail_storage {
 	struct mail_storage_vfuncs super;
+	struct quota *quota;
+
+	/* List of quota roots this storage belongs to. */
+	array_t ARRAY_DEFINE(roots, struct quota_root *);
 };
 
 struct quota_mailbox {
@@ -53,7 +57,7 @@
 	struct quota_transaction_context *qt;
 
 	t = qbox->super.transaction_begin(box, flags);
-	qt = quota_transaction_begin(quota);
+	qt = quota_transaction_begin(box);
 
 	array_idx_set(&t->module_contexts, quota_storage_module_id, &qt);
 	return t;
@@ -247,6 +251,29 @@
 	return box;
 }
 
+static void quota_storage_destroy(struct mail_storage *storage)
+{
+	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
+	struct quota_root *const *roots;
+	struct mail_storage *const *storages;
+	unsigned int i, j, root_count, storage_count;
+
+	/* remove the storage from all roots' storages list */
+	roots = array_get(&qstorage->roots, &root_count);
+	for (i = 0; i < root_count; i++) {
+		storages = array_get(&roots[i]->storages, &storage_count);
+		for (j = 0; j < storage_count; j++) {
+			if (storages[j] == storage) {
+				array_delete(&roots[i]->storages, j, 1);
+				break;
+			}
+		}
+		i_assert(j != storage_count);
+	}
+
+	qstorage->super.destroy(storage);
+}
+
 void quota_mail_storage_created(struct mail_storage *storage)
 {
 	struct quota_mail_storage *qstorage;
@@ -256,8 +283,11 @@
 
 	qstorage = p_new(storage->pool, struct quota_mail_storage, 1);
 	qstorage->super = storage->v;
+	storage->v.destroy = quota_storage_destroy;
 	storage->v.mailbox_open = quota_mailbox_open;
 
+	ARRAY_CREATE(&qstorage->roots, storage->pool, struct quota_root *, 4);
+
 	if (!quota_storage_module_id_set) {
 		quota_storage_module_id = mail_storage_module_id++;
 		quota_storage_module_id_set = TRUE;
@@ -265,4 +295,80 @@
 
 	array_idx_set(&storage->module_contexts,
 		      quota_storage_module_id, &qstorage);
+
+	if ((storage->flags & MAIL_STORAGE_FLAG_SHARED_NAMESPACE) == 0) {
+		/* register to user's quota roots */
+		quota_add_user_storage(quota, storage);
+	}
 }
+
+bool quota_mail_storage_add_root(struct mail_storage *storage,
+				 struct quota_root *root)
+{
+	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
+
+	if (!root->v.add_storage(root, storage))
+		return FALSE;
+
+	array_append(&root->storages, &storage, 1);
+	array_append(&qstorage->roots, &root, 1);
+	return TRUE;
+}
+
+void quota_mail_storage_remove_root(struct mail_storage *storage,
+				    struct quota_root *root)
+{
+	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
+	struct mail_storage *const *storages;
+	struct quota_root *const *roots;
+	unsigned int i, count;
+
+	storages = array_get(&root->storages, &count);
+	for (i = 0; i < count; i++) {
+		if (storages[i] == storage) {
+			array_delete(&root->storages, i, 1);
+			break;
+		}
+	}
+	i_assert(i != count);
+
+	roots = array_get(&qstorage->roots, &count);
+	for (i = 0; i < count; i++) {
+		if (roots[i] == root) {
+			array_delete(&qstorage->roots, i, 1);
+			break;
+		}
+	}
+	i_assert(i != count);
+
+	root->v.remove_storage(root, storage);
+}
+
+struct quota_root_iter *quota_root_iter_init(struct mailbox *box)
+{
+	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(box->storage);
+	struct quota_root_iter *iter;
+
+	iter = i_new(struct quota_root_iter, 1);
+	iter->qstorage = qstorage;
+	return iter;
+}
+
+struct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
+{
+	struct quota_root *const *roots;
+	unsigned int count;
+
+	roots = array_get(&iter->qstorage->roots, &count);
+	i_assert(iter->idx <= count);
+
+	if (iter->idx >= count)
+		return NULL;
+
+	return roots[iter->idx++];
+}
+
+void quota_root_iter_deinit(struct quota_root_iter *iter)
+{
+	i_free(iter);
+}
--- a/src/plugins/quota/quota.c	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/quota/quota.c	Wed Mar 01 11:14:59 2006 +0200
@@ -2,152 +2,361 @@
 
 #include "lib.h"
 #include "array.h"
+#include "hash.h"
 #include "quota-private.h"
 #include "quota-fs.h"
 
 unsigned int quota_module_id = 0;
 
-extern struct quota dirsize_quota;
-extern struct quota dict_quota;
-extern struct quota fs_quota;
+extern struct quota_backend quota_backend_dirsize;
+extern struct quota_backend quota_backend_dict;
+extern struct quota_backend quota_backend_fs;
 
-static struct quota *quota_classes[] = {
-	&dirsize_quota,
-	&dict_quota,
+static struct quota_backend *quota_backends[] = {
+	&quota_backend_dirsize,
+	&quota_backend_dict,
 #ifdef HAVE_FS_QUOTA
-	&fs_quota
+	&quota_backend_fs
 #endif
 };
-#define QUOTA_CLASS_COUNT (sizeof(quota_classes)/sizeof(quota_classes[0]))
+#define QUOTA_CLASS_COUNT (sizeof(quota_backends)/sizeof(quota_backends[0]))
 
-struct quota *quota_init(const char *data)
+void (*hook_quota_root_created)(struct quota_root *root);
+
+struct quota *quota_init(void)
 {
 	struct quota *quota;
-	const char *name, *p;
-	unsigned int i;
 
-	t_push();
-	p = strchr(data, ':');
-	if (p == NULL) {
-		name = data;
-		data = "";
-	} else {
-		name = t_strdup_until(data, p);
-		data = p+1;
-	}
-	for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
-		if (strcmp(quota_classes[i]->name, name) == 0)
-			break;
-	}
-
-	if (i == QUOTA_CLASS_COUNT) {
-		i_error("Unknown quota module: %s", name);
-		quota = NULL;
-	} else {
-		quota = quota_classes[i]->init(data);
-		array_create(&quota->quota_module_contexts,
-			     default_pool, sizeof(void *), 5);
-	}
-	t_pop();
+	quota = i_new(struct quota, 1);
+	ARRAY_CREATE(&quota->setups, default_pool, struct quota_setup *, 4);
 	return quota;
 }
 
 void quota_deinit(struct quota *quota)
 {
-	/* make a copy, since quota is freed */
-	array_t module_contexts = quota->quota_module_contexts;
+	while (array_count(&quota->setups) > 0) {
+		struct quota_setup *const *setup;
+
+		setup = array_idx(&quota->setups, 0);
+		quota_setup_deinit(*setup);
+	}
+
+	array_free(&quota->setups);
+	i_free(quota);
+}
+
+struct quota_setup *
+quota_setup_init(struct quota *quota, const char *data, bool user_root)
+{
+	struct quota_setup *setup;
+	const char *backend_name, *p;
+	unsigned int i;
+
+	setup = i_new(struct quota_setup, 1);
+	setup->quota = quota;
+	setup->data = i_strdup(data);
+	setup->user_root = user_root;
+	ARRAY_CREATE(&setup->roots, default_pool, struct quota_root *, 4);
+
+	t_push();
+	p = strchr(setup->data, ':');
+	if (p == NULL) {
+		backend_name = setup->data;
+		data = "";
+	} else {
+		backend_name = t_strdup_until(setup->data, p);
+		data = p+1;
+	}
+	for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
+		if (strcmp(quota_backends[i]->name, backend_name) == 0) {
+			setup->backend = quota_backends[i];
+			break;
+		}
+	}
+
+	if (setup->backend == NULL)
+		i_fatal("Unknown quota backend: %s", backend_name);
+
+	t_pop();
+
+	array_append(&quota->setups, &setup, 1);
+	return setup;
+}
+
+void quota_setup_deinit(struct quota_setup *setup)
+{
+	struct quota_setup *const *setups;
+	unsigned int i, count;
 
-	quota->deinit(quota);
+	setups = array_get(&setup->quota->setups, &count);
+	for (i = 0; i < count; i++) {
+		if (setups[i] == setup) {
+			array_delete(&setup->quota->setups, i, 1);
+			break;
+		}
+	}
+	i_assert(i != count);
+
+	while (array_count(&setup->roots) > 0) {
+		struct quota_root *const *root;
+
+		root = array_idx(&setup->roots, 0);
+		quota_root_deinit(*root);
+	}
+
+	array_free(&setup->roots);
+	i_free(setup->data);
+	i_free(setup);
+}
+
+struct quota_root *
+quota_root_init(struct quota_setup *setup, const char *name)
+{
+	struct quota_root *root;
+
+	root = setup->backend->v.init(setup, name);
+	root->setup = setup;
+	ARRAY_CREATE(&root->storages, default_pool, struct mail_storage *, 8);
+	array_create(&root->quota_module_contexts,
+		     default_pool, sizeof(void *), 5);
+	array_append(&setup->roots, &root, 1);
+
+	if (hook_quota_root_created != NULL)
+		hook_quota_root_created(root);
+	return root;
+}
+
+void quota_root_deinit(struct quota_root *root)
+{
+	/* make a copy, since root is freed */
+	array_t module_contexts = root->quota_module_contexts;
+	struct mail_storage *const *storage_p;
+	struct quota_root *const *roots;
+	unsigned int i, count;
+
+	/* remove from all storages */
+	while (array_count(&root->storages) > 0) {
+		storage_p = array_idx(&root->storages, 0);
+		quota_mail_storage_remove_root(*storage_p, root);
+	}
+
+	/* remove from setup */
+	roots = array_get(&root->setup->roots, &count);
+	for (i = 0; i < count; i++) {
+		if (roots[i] == root) {
+			array_delete(&root->setup->roots, i, 1);
+			break;
+		}
+	}
+	i_assert(i != count);
+
+	array_free(&root->storages);
+	root->v.deinit(root);
 	array_free(&module_contexts);
 }
 
-struct quota_root_iter *
-quota_root_iter_init(struct quota *quota, struct mailbox *box)
+void quota_add_user_storage(struct quota *quota, struct mail_storage *storage)
 {
-	return quota->root_iter_init(quota, box);
-}
+	struct quota_setup *const *setups;
+	struct quota_root *const *roots;
+	unsigned int i, j, setup_count, root_count;
+	bool found = FALSE;
+
+	setups = array_get(&quota->setups, &setup_count);
+	for (i = 0; i < setup_count; i++) {
+		roots = array_get(&setups[i]->roots, &root_count);
+		for (j = 0; j < root_count; j++) {
+			if (!roots[j]->user_root)
+				continue;
 
-struct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
-{
-	return iter->quota->root_iter_next(iter);
-}
+			if (quota_mail_storage_add_root(storage, roots[j]))
+				found = TRUE;
+		}
+	}
 
-int quota_root_iter_deinit(struct quota_root_iter *iter)
-{
-	return iter->quota->root_iter_deinit(iter);
+	if (!found && setup_count > 0) {
+		/* create a new quota root for the storage */
+		struct quota_root *root;
+
+		root = quota_root_init(setups[0], ""); // FIXME: name?
+		found = quota_mail_storage_add_root(storage, root);
+		i_assert(found);
+	}
 }
 
 struct quota_root *quota_root_lookup(struct quota *quota, const char *name)
 {
-	return quota->root_lookup(quota, name);
+	struct quota_setup *const *setups;
+	struct quota_root *const *roots;
+	unsigned int i, j, setup_count, root_count;
+
+	setups = array_get(&quota->setups, &setup_count);
+	for (i = 0; i < setup_count; i++) {
+		roots = array_get(&setups[i]->roots, &root_count);
+		for (j = 0; j < root_count; j++) {
+			if (strcmp(roots[j]->name, name) == 0)
+				return roots[j];
+		}
+	}
+	return NULL;
 }
 
 const char *quota_root_get_name(struct quota_root *root)
 {
-	return root->quota->root_get_name(root);
+	return root->name;
 }
 
 const char *const *quota_root_get_resources(struct quota_root *root)
 {
-	return root->quota->root_get_resources(root);
-}
-
-int quota_root_create(struct quota *quota, const char *name,
-		      struct quota_root **root_r)
-{
-	return quota->root_create(quota, name, root_r);
+	return root->v.get_resources(root);
 }
 
 int quota_get_resource(struct quota_root *root, const char *name,
 		       uint64_t *value_r, uint64_t *limit_r)
 {
-	return root->quota->get_resource(root, name, value_r, limit_r);
+	return root->v.get_resource(root, name, value_r, limit_r);
 }
 
 int quota_set_resource(struct quota_root *root,
 		       const char *name, uint64_t value)
 {
-	return root->quota->set_resource(root, name, value);
+	return root->v.set_resource(root, name, value);
 }
 
-struct quota_transaction_context *quota_transaction_begin(struct quota *quota)
+struct quota_transaction_context *quota_transaction_begin(struct mailbox *box)
 {
-	return quota->transaction_begin(quota);
+	struct quota_transaction_context *ctx;
+	struct quota_root_transaction_context *root_ctx;
+	struct quota_root_iter *iter;
+	struct quota_root *root;
+
+	ctx = i_new(struct quota_transaction_context, 1);
+	ARRAY_CREATE(&ctx->root_transactions, default_pool,
+		     struct quota_root_transaction_context *, 4);
+
+	iter = quota_root_iter_init(box);
+	while ((root = quota_root_iter_next(iter)) != NULL) {
+		root_ctx = root->v.transaction_begin(root, ctx);
+		array_append(&ctx->root_transactions, &root_ctx, 1);
+	}
+	quota_root_iter_deinit(iter);
+	return ctx;
+}
+
+static void quota_transaction_free(struct quota_transaction_context *ctx)
+{
+	array_free(&ctx->root_transactions);
+	i_free(ctx);
 }
 
 int quota_transaction_commit(struct quota_transaction_context *ctx)
 {
-	return ctx->quota->transaction_commit(ctx);
+	struct quota_root_transaction_context *const *root_transactions;
+	unsigned int i, count;
+	int ret = 0;
+
+	root_transactions = array_get(&ctx->root_transactions, &count);
+	for (i = 0; i < count; i++) {
+		struct quota_root_transaction_context *t =
+			root_transactions[i];
+
+		if (t->root->v.transaction_commit(t) < 0)
+			ret = -1;
+	}
+
+	quota_transaction_free(ctx);
+	return ret;
 }
 
 void quota_transaction_rollback(struct quota_transaction_context *ctx)
 {
-	ctx->quota->transaction_rollback(ctx);
+	struct quota_root_transaction_context *const *root_transactions;
+	unsigned int i, count;
+
+	root_transactions = array_get(&ctx->root_transactions, &count);
+	for (i = 0; i < count; i++) {
+		struct quota_root_transaction_context *t =
+			root_transactions[i];
+
+		t->root->v.transaction_rollback(t);
+	}
+
+	quota_transaction_free(ctx);
 }
 
 int quota_try_alloc(struct quota_transaction_context *ctx,
 		    struct mail *mail, bool *too_large_r)
 {
-	return ctx->quota->try_alloc(ctx, mail, too_large_r);
+	struct quota_root_transaction_context *const *root_transactions;
+	unsigned int i, count;
+	int ret = 1;
+
+	root_transactions = array_get(&ctx->root_transactions, &count);
+	for (i = 0; i < count; i++) {
+		struct quota_root_transaction_context *t =
+			root_transactions[i];
+
+		ret = t->root->v.try_alloc(t, mail, too_large_r);
+		if (ret <= 0)
+			break;
+	}
+	return ret;
 }
 
 int quota_try_alloc_bytes(struct quota_transaction_context *ctx,
 			  uoff_t size, bool *too_large_r)
 {
-	return ctx->quota->try_alloc_bytes(ctx, size, too_large_r);
+	struct quota_root_transaction_context *const *root_transactions;
+	unsigned int i, count;
+	int ret = 1;
+
+	root_transactions = array_get(&ctx->root_transactions, &count);
+	for (i = 0; i < count; i++) {
+		struct quota_root_transaction_context *t =
+			root_transactions[i];
+
+		ret = t->root->v.try_alloc_bytes(t, size, too_large_r);
+		if (ret <= 0)
+			break;
+	}
+	return ret;
 }
 
 void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
 {
-	ctx->quota->alloc(ctx, mail);
+	struct quota_root_transaction_context *const *root_transactions;
+	unsigned int i, count;
+
+	root_transactions = array_get(&ctx->root_transactions, &count);
+	for (i = 0; i < count; i++) {
+		struct quota_root_transaction_context *t =
+			root_transactions[i];
+
+		t->root->v.alloc(t, mail);
+	}
 }
 
 void quota_free(struct quota_transaction_context *ctx, struct mail *mail)
 {
-	ctx->quota->free(ctx, mail);
+	struct quota_root_transaction_context *const *root_transactions;
+	unsigned int i, count;
+
+	root_transactions = array_get(&ctx->root_transactions, &count);
+	for (i = 0; i < count; i++) {
+		struct quota_root_transaction_context *t =
+			root_transactions[i];
+
+		t->root->v.free(t, mail);
+	}
 }
 
 const char *quota_last_error(struct quota *quota)
 {
-	return quota->last_error(quota);
+	return quota->last_error;
 }
+
+void quota_set_error(struct quota *quota, const char *errormsg)
+{
+	i_free(quota->last_error);
+	quota->last_error = i_strdup(errormsg);
+}
--- a/src/plugins/quota/quota.h	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/quota/quota.h	Wed Mar 01 11:14:59 2006 +0200
@@ -14,14 +14,27 @@
 struct quota_root_iter;
 struct quota_transaction_context;
 
-struct quota *quota_init(const char *data);
+extern void (*hook_quota_root_created)(struct quota_root *root);
+
+struct quota *quota_init(void);
 void quota_deinit(struct quota *quota);
 
+/* Create a new quota setup under which quota roots are created.
+   user_root is TRUE if this quota points to user's own mailboxes instead of
+   shared mailboxes. */
+struct quota_setup *
+quota_setup_init(struct quota *quota, const char *data, bool user_root);
+void quota_setup_deinit(struct quota_setup *setup);
+
+/* Create a new quota root. */
+struct quota_root *
+quota_root_init(struct quota_setup *setup, const char *name);
+void quota_root_deinit(struct quota_root *root);
+
 /* List all quota roots. Returned quota roots are freed by quota_deinit(). */
-struct quota_root_iter *
-quota_root_iter_init(struct quota *quota, struct mailbox *box);
+struct quota_root_iter *quota_root_iter_init(struct mailbox *box);
 struct quota_root *quota_root_iter_next(struct quota_root_iter *iter);
-int quota_root_iter_deinit(struct quota_root_iter *iter);
+void quota_root_iter_deinit(struct quota_root_iter *iter);
 
 /* Return quota root or NULL. */
 struct quota_root *quota_root_lookup(struct quota *quota, const char *name);
@@ -31,10 +44,6 @@
 /* Return a list of all resources set for the quota root. */
 const char *const *quota_root_get_resources(struct quota_root *root);
 
-/* Create a new quota root. Returns 0 if OK, -1 if error (eg. permission
-   denied). */
-int quota_root_create(struct quota *quota, const char *name,
-		      struct quota_root **root_r);
 /* Returns 1 if quota value was found, 0 if not, -1 if error. */
 int quota_get_resource(struct quota_root *root,
 		       const char *name, uint64_t *value_r, uint64_t *limit_r);
@@ -43,7 +52,7 @@
 		       const char *name, uint64_t value);
 
 /* Start a new quota transaction. */
-struct quota_transaction_context *quota_transaction_begin(struct quota *quota);
+struct quota_transaction_context *quota_transaction_begin(struct mailbox *box);
 /* Commit quota transaction. Returns 0 if ok, -1 if failed. */
 int quota_transaction_commit(struct quota_transaction_context *ctx);
 /* Rollback quota transaction changes. */
--- a/src/plugins/trash/trash-plugin.c	Tue Feb 28 14:38:20 2006 +0200
+++ b/src/plugins/trash/trash-plugin.c	Wed Mar 01 11:14:59 2006 +0200
@@ -22,8 +22,8 @@
 	*((void **)array_idx_modifyable(&(obj)->quota_module_contexts, \
 					trash_quota_module_id))
 
-struct trash_quota {
-	struct quota super;
+struct trash_quota_root {
+	struct quota_backend_vfuncs super;
 };
 
 struct trash_mailbox {
@@ -42,11 +42,9 @@
 };
 
 /* defined by imap, pop3, lda */
-extern void (*hook_mail_storage_created)(struct mail_storage *storage);
+extern void (*hook_quota_root_created)(struct quota_root *root);
 
-static void (*trash_next_hook_mail_storage_created)
-	(struct mail_storage *storage);
-static bool quota_initialized;
+static void (*trash_next_hook_quota_root_created)(struct quota_root *root);
 static unsigned int trash_quota_module_id;
 
 static pool_t config_pool;
@@ -163,14 +161,14 @@
 }
 
 static int
-trash_quota_try_alloc(struct quota_transaction_context *ctx,
-		      struct mail *mail, bool *too_large_r)
+trash_quota_root_try_alloc(struct quota_root_transaction_context *ctx,
+			   struct mail *mail, bool *too_large_r)
 {
-	struct trash_quota *tquota = TRASH_CONTEXT(quota);
+	struct trash_quota_root *troot = TRASH_CONTEXT(ctx->root);
 	int ret, i;
 
 	for (i = 0; ; i++) {
-		ret = tquota->super.try_alloc(ctx, mail, too_large_r);
+		ret = troot->super.try_alloc(ctx, mail, too_large_r);
 		if (ret != 0 || *too_large_r)
 			return ret;
 
@@ -191,38 +189,32 @@
 	return 0;
 }
 
-static void trash_quota_deinit(struct quota *quota)
+static void trash_quota_root_deinit(struct quota_root *root)
 {
-	struct trash_quota *tquota = TRASH_CONTEXT(quota);
+	struct trash_quota_root *troot = TRASH_CONTEXT(root);
 	void *null = NULL;
 
-	array_idx_set(&quota->quota_module_contexts,
+	array_idx_set(&root->quota_module_contexts,
 		      trash_quota_module_id, &null);
-	tquota->super.deinit(quota);
-	i_free(tquota);
+	troot->super.deinit(root);
+	i_free(troot);
 }
 
-static void trash_mail_storage_created(struct mail_storage *storage)
+static void trash_quota_root_created(struct quota_root *root)
 {
-	struct trash_quota *tquota;
-
-	if (trash_next_hook_mail_storage_created != NULL)
-		trash_next_hook_mail_storage_created(storage);
+	struct trash_quota_root *troot;
 
-	if (quota_initialized || quota == NULL)
-		return;
+	if (trash_next_hook_quota_root_created != NULL)
+		trash_next_hook_quota_root_created(root);
 
-	/* initialize here because plugins could be loaded in wrong order */
-	quota_initialized = TRUE;
-
-	tquota = i_new(struct trash_quota, 1);
-	tquota->super = *quota;
-	quota->deinit = trash_quota_deinit;
-	quota->try_alloc = trash_quota_try_alloc;
+	troot = i_new(struct trash_quota_root, 1);
+	troot->super = root->v;
+	root->v.deinit = trash_quota_root_deinit;
+	root->v.try_alloc = trash_quota_root_try_alloc;
 
 	trash_quota_module_id = quota_module_id++;
-	array_idx_set(&quota->quota_module_contexts,
-		      trash_quota_module_id, &tquota);
+	array_idx_set(&root->quota_module_contexts,
+		      trash_quota_module_id, &troot);
 }
 
 static int trash_mailbox_priority_cmp(const void *p1, const void *p2)
@@ -271,8 +263,7 @@
 
 void trash_plugin_init(void)
 {
-	quota_initialized = FALSE;
-	trash_next_hook_mail_storage_created = hook_mail_storage_created;
+	trash_next_hook_quota_root_created = hook_quota_root_created;
 
 	config_pool = pool_alloconly_create("trash config", 1024);
 	if (read_configuration(home_expand(LOCAL_CONFIG_FILE)) < 0) {
@@ -280,11 +271,11 @@
 			return;
 	}
 
-	hook_mail_storage_created = trash_mail_storage_created;
+	hook_quota_root_created = trash_quota_root_created;
 }
 
 void trash_plugin_deinit(void)
 {
 	pool_unref(config_pool);
-	hook_mail_storage_created = trash_next_hook_mail_storage_created;
+	hook_quota_root_created = trash_next_hook_quota_root_created;
 }