[dovecot-cvs] dovecot/src/plugins/quota quota-dict.c, 1.7, 1.8 quota-dirsize.c, 1.5, 1.6 quota-fs.c, 1.8, 1.9 quota-plugin.c, 1.2, 1.3 quota-private.h, 1.4, 1.5 quota-storage.c, 1.5, 1.6 quota.c, 1.5, 1.6 quota.h, 1.3, 1.4

tss-movial at dovecot.org tss-movial at dovecot.org
Wed Mar 1 11:15:09 EET 2006


Update of /var/lib/cvs/dovecot/src/plugins/quota
In directory talvi:/tmp/cvs-serv19257/quota

Modified Files:
	quota-dict.c quota-dirsize.c quota-fs.c quota-plugin.c 
	quota-private.h quota-storage.c quota.c quota.h 
Log Message:
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).



Index: quota-dict.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-dict.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- quota-dict.c	22 Feb 2006 16:02:20 -0000	1.7
+++ quota-dict.c	1 Mar 2006 09:15:03 -0000	1.8
@@ -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 dict_quota;
+extern struct quota_backend quota_backend_dict;
 
-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;
-}
-
-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;
+	root = i_new(struct dict_quota_root, 1);
+	root->root.name = i_strdup(name);
+	root->root.v = quota_backend_dict.v;
+	root->dict = dict;
 
-	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;
-
-	if (iter->sent)
-		return NULL;
-
-	iter->sent = TRUE;
-	return &quota->root;
-}
+	struct dict_quota_root *root = (struct dict_quota_root *)_root;
 
-static int dict_quota_root_iter_deinit(struct quota_root_iter *iter)
-{
-	i_free(iter);
-	return 0;
+	i_free(root->root.name);
+	i_free(root);
 }
 
-static struct quota_root *
-dict_quota_root_lookup(struct quota *_quota, const char *name)
+static bool
+dict_quota_add_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;
+	return TRUE;
 }
 
-static const char *
-dict_quota_root_get_name(struct quota_root *root __attr_unused__)
+static void
+dict_quota_remove_storage(struct quota_root *root __attr_unused__,
+			  struct mail_storage *storage __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_root_iter_init,
-	dict_quota_root_iter_next,
-	dict_quota_root_iter_deinit,
-
-	dict_quota_root_lookup,
-
-	dict_quota_root_get_name,
-	dict_quota_root_get_resources,
+	{
+		dict_quota_init,
+		dict_quota_deinit,
 
-	dict_quota_root_create,
-	dict_quota_get_resource,
-	dict_quota_set_resource,
+		dict_quota_add_storage,
+		dict_quota_remove_storage,
 
-	dict_quota_transaction_begin,
-	dict_quota_transaction_commit,
-	dict_quota_transaction_rollback,
+		dict_quota_root_get_resources,
 
-	dict_quota_try_alloc,
-	dict_quota_try_alloc_bytes,
-	dict_quota_alloc,
-	dict_quota_free,
+		dict_quota_get_resource,
+		dict_quota_set_resource,
 
-	dict_quota_last_error,
+		dict_quota_transaction_begin,
+		dict_quota_transaction_commit,
+		dict_quota_transaction_rollback,
 
-	ARRAY_INIT
+		dict_quota_try_alloc,
+		dict_quota_try_alloc_bytes,
+		dict_quota_alloc,
+		dict_quota_free
+	}
 };

Index: quota-dirsize.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-dirsize.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- quota-dirsize.c	22 Feb 2006 16:02:20 -0000	1.5
+++ quota-dirsize.c	1 Mar 2006 09:15:03 -0000	1.6
@@ -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 dirsize_quota;
+extern struct quota_backend quota_backend_dirsize;
 
-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;
 
-	args = t_strsplit(data, ":");
-	quota->path = p_strdup(pool, args[0]);
+	t_push();
+	args = t_strsplit(setup->data, ":");
 
-	for (args++; *args != '\0'; args++) {
+	for (; *args != '\0'; args++) {
 		if (strncmp(*args, "storage=", 8) == 0)
-			quota->storage_limit = strtoull(*args + 8, NULL, 10);
+			root->storage_limit = strtoull(*args + 8, NULL, 10);
 	}
+	t_pop();
 
 	if (getenv("DEBUG") != NULL) {
-		i_info("dirsize quota path = %s", quota->path);
 		i_info("dirsize quota limit = %llukB",
-		       (unsigned long long)quota->storage_limit);
+		       (unsigned long long)root->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);
-}
-
-static struct quota_root_iter *
-dirsize_quota_root_iter_init(struct quota *quota,
-			     struct mailbox *box __attr_unused__)
-{
-	struct dirsize_quota_root_iter *iter;
-
-	iter = i_new(struct dirsize_quota_root_iter, 1);
-	iter->iter.quota = quota;
-	return &iter->iter;
+	return &root->root;
 }
 
-static struct quota_root *
-dirsize_quota_root_iter_next(struct quota_root_iter *_iter)
+static void dirsize_quota_deinit(struct quota_root *_root)
 {
-	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;
-}
+	struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
 
-static int dirsize_quota_root_iter_deinit(struct quota_root_iter *iter)
-{
-	i_free(iter);
-	return 0;
+	i_free(root->root.name);
+	i_free(root);
 }
 
-static struct quota_root *
-dirsize_quota_root_lookup(struct quota *_quota, const char *name)
+static bool
+dirsize_quota_add_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;
+	return TRUE;
 }
 
-static const char *
-dirsize_quota_root_get_name(struct quota_root *root __attr_unused__)
+static void
+dirsize_quota_remove_storage(struct quota_root *root __attr_unused__,
+			     struct mail_storage *storage __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_root_iter_init,
-	dirsize_quota_root_iter_next,
-	dirsize_quota_root_iter_deinit,
-
-	dirsize_quota_root_lookup,
-
-	dirsize_quota_root_get_name,
-	dirsize_quota_root_get_resources,
+	{
+		dirsize_quota_init,
+		dirsize_quota_deinit,
 
-	dirsize_quota_root_create,
-	dirsize_quota_get_resource,
-	dirsize_quota_set_resource,
+		dirsize_quota_add_storage,
+		dirsize_quota_remove_storage,
 
-	dirsize_quota_transaction_begin,
-	dirsize_quota_transaction_commit,
-	dirsize_quota_transaction_rollback,
+		dirsize_quota_root_get_resources,
 
-	dirsize_quota_try_alloc,
-	dirsize_quota_try_alloc_bytes,
-	dirsize_quota_alloc,
-	dirsize_quota_free,
+		dirsize_quota_get_resource,
+		dirsize_quota_set_resource,
 
-	dirsize_quota_last_error,
+		dirsize_quota_transaction_begin,
+		dirsize_quota_transaction_commit,
+		dirsize_quota_transaction_rollback,
 
-	ARRAY_INIT
+		dirsize_quota_try_alloc,
+		dirsize_quota_try_alloc_bytes,
+		dirsize_quota_alloc,
+		dirsize_quota_free
+	}
 };

Index: quota-fs.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-fs.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- quota-fs.c	25 Feb 2006 09:24:46 -0000	1.8
+++ quota-fs.c	1 Mar 2006 09:15:03 -0000	1.9
@@ -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 int path_to_device(const char *path, unsigned int *blk_size_r,
-			  const char **device_path_r, const char **mount_path_r)
+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 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(path, &st) < 0) {
-		i_error("stat(%s) failed: %m", path);
-		return -1;
+	if (stat(dir, &st) < 0) {
+		i_error("stat(%s) failed: %m", dir);
+		return NULL;
 	}
-	*blk_size_r = st.st_blksize;
+	blk_size = 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;
-#endif
-}
-
-static struct quota *fs_quota_init(const char *data)
-{
-	struct fs_quota *quota;
-	const char *device, *mount_point;
-	pool_t pool;
-	unsigned int blk_size = 0;
-
-	if (getenv("DEBUG") != NULL)
-		i_info("fs quota path = %s", data);
-
-	if (path_to_device(data, &blk_size, &device, &mount_point) < 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;
-
-	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);
 	}
 
-	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;
+	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);
 
-#ifdef HAVE_Q_QUOTACTL
-	if (quota->fd != -1) {
-		if (close(quota->fd) < 0)
-			i_error("close(%s) failed: %m", quota->path);
-	}
+	return mount;
 #endif
-	pool_unref(quota->pool);
 }
 
-static struct quota_root_iter *
-fs_quota_root_iter_init(struct quota *quota,
-			struct mailbox *box __attr_unused__)
+static bool fs_quota_add_storage(struct quota_root *_root,
+				 struct mail_storage *storage)
 {
-	struct fs_quota_root_iter *iter;
-
-	iter = i_new(struct fs_quota_root_iter, 1);
-	iter->iter.quota = quota;
-	return &iter->iter;
-}
+	struct fs_quota_root *root = (struct fs_quota_root *)_root;
+	struct fs_quota_mountpoint *mount;
+	const char *dir;
+	bool is_file;
 
-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;
+	dir = mail_storage_get_mailbox_path(storage, "", &is_file);
 
-	if (iter->sent)
-		return NULL;
+	if (getenv("DEBUG") != NULL)
+		i_info("fs quota add storage dir = %s", dir);
 
-	iter->sent = TRUE;
-	return &quota->root;
-}
+	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;
 
-static int fs_quota_root_iter_deinit(struct quota_root_iter *iter)
-{
-	i_free(iter);
-	return 0;
-}
+		fs_quota_mountpoint_free(mount);
+		if (!match) {
+			/* different mountpoints, can't use this */
+			return FALSE;
+		}
+		mount = root->mount;
+	}
 
-static struct quota_root *
-fs_quota_root_lookup(struct quota *_quota, const char *name)
-{
-	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);
+	}
 
-	if (*name == '\0')
-		return &quota->root;
-	else
-		return NULL;
+#ifdef HAVE_Q_QUOTACTL
+	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
+	return TRUE;
 }
 
-static const char *
-fs_quota_root_get_name(struct quota_root *root __attr_unused__)
+static void
+fs_quota_remove_storage(struct quota_root *root __attr_unused__,
+			struct mail_storage *storage __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_root_iter_init,
-	fs_quota_root_iter_next,
-	fs_quota_root_iter_deinit,
-
-	fs_quota_root_lookup,
-
-	fs_quota_root_get_name,
-	fs_quota_root_get_resources,
+	{
+		fs_quota_init,
+		fs_quota_deinit,
 
-	fs_quota_root_create,
-	fs_quota_get_resource,
-	fs_quota_set_resource,
+		fs_quota_add_storage,
+		fs_quota_remove_storage,
 
-	fs_quota_transaction_begin,
-	fs_quota_transaction_commit,
-	fs_quota_transaction_rollback,
+		fs_quota_root_get_resources,
 
-	fs_quota_try_alloc,
-	fs_quota_try_alloc_bytes,
-	fs_quota_alloc,
-	fs_quota_free,
+		fs_quota_get_resource,
+		fs_quota_set_resource,
 
-	fs_quota_last_error,
+		fs_quota_transaction_begin,
+		fs_quota_transaction_commit,
+		fs_quota_transaction_rollback,
 
-	ARRAY_INIT
+		fs_quota_try_alloc,
+		fs_quota_try_alloc_bytes,
+		fs_quota_alloc,
+		fs_quota_free
+	}
 };
 
 #endif

Index: quota-plugin.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-plugin.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- quota-plugin.c	22 Feb 2006 14:52:19 -0000	1.2
+++ quota-plugin.c	1 Mar 2006 09:15:03 -0000	1.3
@@ -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;

Index: quota-private.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-private.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- quota-private.h	22 Feb 2006 16:02:20 -0000	1.4
+++ quota-private.h	1 Mar 2006 09:15:03 -0000	1.5
@@ -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_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);
+	struct quota_backend *backend;
+	char *data;
 
-	struct quota_root *(*root_lookup)(struct quota *quota,
-					  const char *name);
+	/* List of quota roots. It's array because there shouldn't be many. */
+	array_t ARRAY_DEFINE(roots, struct quota_root *);
 
-	const char *(*root_get_name)(struct quota_root *root);
-	const char *const *(*root_get_resources)(struct quota_root *root);
+	unsigned int user_root:1;
+};
 
-	int (*root_create)(struct quota *quota, const char *name,
-			   struct quota_root **root_r);
+struct quota_backend_vfuncs {
+	struct quota_root *(*init)(struct quota_setup *setup, const char *name);
+	void (*deinit)(struct quota_root *root);
+
+	bool (*add_storage)(struct quota_root *root,
+			    struct mail_storage *storage);
+	void (*remove_storage)(struct quota_root *root,
+			       struct mail_storage *storage);
+
+	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);
+struct quota_backend {
+	const char *name;
+	struct quota_backend_vfuncs v;
+};
+
+struct quota_root {
+	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);
-};
 
-struct quota_root {
-	struct quota *quota;
+	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

Index: quota-storage.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-storage.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- quota-storage.c	22 Feb 2006 16:02:20 -0000	1.5
+++ quota-storage.c	1 Mar 2006 09:15:03 -0000	1.6
@@ -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);
 }

Index: quota.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- quota.c	22 Feb 2006 16:02:20 -0000	1.5
+++ quota.c	1 Mar 2006 09:15:03 -0000	1.6
@@ -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;
+
+	quota = i_new(struct quota, 1);
+	ARRAY_CREATE(&quota->setups, default_pool, struct quota_setup *, 4);
+	return quota;
+}
+
+void quota_deinit(struct quota *quota)
+{
+	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(data, ':');
+	p = strchr(setup->data, ':');
 	if (p == NULL) {
-		name = data;
+		backend_name = setup->data;
 		data = "";
 	} else {
-		name = t_strdup_until(data, p);
+		backend_name = t_strdup_until(setup->data, p);
 		data = p+1;
 	}
 	for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
-		if (strcmp(quota_classes[i]->name, name) == 0)
+		if (strcmp(quota_backends[i]->name, backend_name) == 0) {
+			setup->backend = quota_backends[i];
 			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);
-	}
+	if (setup->backend == NULL)
+		i_fatal("Unknown quota backend: %s", backend_name);
+
 	t_pop();
-	return quota;
+
+	array_append(&quota->setups, &setup, 1);
+	return setup;
 }
 
-void quota_deinit(struct quota *quota)
+void quota_setup_deinit(struct quota_setup *setup)
 {
-	/* make a copy, since quota is freed */
-	array_t module_contexts = quota->quota_module_contexts;
+	struct quota_setup *const *setups;
+	unsigned int i, count;
 
-	quota->deinit(quota);
-	array_free(&module_contexts);
+	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_iter *
-quota_root_iter_init(struct quota *quota, struct mailbox *box)
+struct quota_root *
+quota_root_init(struct quota_setup *setup, const char *name)
 {
-	return quota->root_iter_init(quota, box);
+	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;
 }
 
-struct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
+void quota_root_deinit(struct quota_root *root)
 {
-	return iter->quota->root_iter_next(iter);
+	/* 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);
 }
 
-int quota_root_iter_deinit(struct quota_root_iter *iter)
+void quota_add_user_storage(struct quota *quota, struct mail_storage *storage)
 {
-	return iter->quota->root_iter_deinit(iter);
+	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;
+
+			if (quota_mail_storage_add_root(storage, roots[j]))
+				found = TRUE;
+		}
+	}
+
+	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);
 }

Index: quota.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- quota.h	22 Feb 2006 16:02:20 -0000	1.3
+++ quota.h	1 Mar 2006 09:15:03 -0000	1.4
@@ -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. */



More information about the dovecot-cvs mailing list