Backport of quota plugin from CVS HEAD to Dovecot v1.0. Should work fine, but since this changes the configuration it didn't make it into v1.0. Run configure after applying this patch so Makefiles get updated. diff -ruN dovecot-1.0.rc22/dovecot-example.conf dovecot-1.0.rc22-quota-rewrite/dovecot-example.conf --- dovecot-1.0.rc22/dovecot-example.conf 2007-02-02 13:17:48.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/dovecot-example.conf 2007-02-16 22:27:23.427064161 +0200 @@ -1005,6 +1005,21 @@ # dict: Keep quota stored in dictionary (eg. SQL) # maildir: Maildir++ quota # fs: Read-only support for filesystem quota + # + # Quota limits are set using "quota_rule" parameters, either in here or in + # userdb. It's also possible to give mailbox-specific limits, for example: + # quota_rule = *:storage=1048576 + # quota_rule2 = Trash:storage=102400 + # User has now 1GB quota, but when saving to Trash mailbox the user gets + # additional 100MB. + # + # Multiple quota roots are also possible, for example: + # quota = dict:user::proxy::quota + # quota2 = dict:domain:%d:proxy::quota_domain + # quota_rule = *:storage=102400 + # quota2_rule = *:storage=1048576 + # Gives each user their own 100MB quota and one shared 1GB quota within + # the domain. #quota = maildir # ACL plugin. vfile backend reads ACLs from "dovecot-acl" file from maildir diff -ruN dovecot-1.0.rc22/src/plugins/imap-quota/imap-quota-plugin.c dovecot-1.0.rc22-quota-rewrite/src/plugins/imap-quota/imap-quota-plugin.c --- dovecot-1.0.rc22/src/plugins/imap-quota/imap-quota-plugin.c 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/imap-quota/imap-quota-plugin.c 2007-02-16 22:27:23.427064161 +0200 @@ -28,7 +28,7 @@ str_append(str, " ("); list = quota_root_get_resources(root); for (i = 0; *list != NULL; list++) { - ret = quota_get_resource(root, *list, &value, &limit); + ret = quota_get_resource(root, "", *list, &value, &limit); if (ret > 0) { if (i > 0) str_append_c(str, ' '); @@ -37,8 +37,8 @@ (unsigned long long)limit); i++; } else if (ret < 0) { - client_send_line(cmd->client, t_strconcat( - "* BAD ", quota_last_error(quota_set), NULL)); + client_send_line(cmd->client, + "* BAD Internal quota calculation error"); } } str_append_c(str, ')'); @@ -83,7 +83,7 @@ str_append(str, "* QUOTAROOT "); imap_quote_append_string(str, mailbox, FALSE); - iter = quota_root_iter_init(box); + iter = quota_root_iter_init(quota_set, box); while ((root = quota_root_iter_next(iter)) != NULL) { str_append_c(str, ' '); imap_quote_append_string(str, quota_root_get_name(root), FALSE); @@ -92,7 +92,7 @@ client_send_line(cmd->client, str_c(str)); /* send QUOTA reply for each quotaroot */ - iter = quota_root_iter_init(box); + iter = quota_root_iter_init(quota_set, box); while ((root = quota_root_iter_next(iter)) != NULL) quota_send(cmd, root); quota_root_iter_deinit(iter); @@ -132,7 +132,7 @@ { struct quota_root *root; struct imap_arg *args, *arg; - const char *root_name, *name; + const char *root_name, *name, *error; uint64_t value; /* */ @@ -166,9 +166,8 @@ } value = strtoull(IMAP_ARG_STR_NONULL(&arg[1]), NULL, 10); - if (quota_set_resource(root, name, value) < 0) { - client_send_command_error(cmd, - quota_last_error(quota_set)); + if (quota_set_resource(root, name, value, &error) < 0) { + client_send_command_error(cmd, error); return TRUE; } } diff -ruN dovecot-1.0.rc22/src/plugins/imap-quota/Makefile.am dovecot-1.0.rc22-quota-rewrite/src/plugins/imap-quota/Makefile.am --- dovecot-1.0.rc22/src/plugins/imap-quota/Makefile.am 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/imap-quota/Makefile.am 2007-02-16 22:27:23.427064161 +0200 @@ -8,12 +8,12 @@ imap_moduledir = $(moduledir)/imap -lib11_imap_quota_plugin_la_LDFLAGS = -module -avoid-version +lib02_imap_quota_plugin_la_LDFLAGS = -module -avoid-version imap_module_LTLIBRARIES = \ - lib11_imap_quota_plugin.la + lib02_imap_quota_plugin.la -lib11_imap_quota_plugin_la_SOURCES = \ +lib02_imap_quota_plugin_la_SOURCES = \ imap-quota-plugin.c noinst_HEADERS = \ diff -ruN dovecot-1.0.rc22/src/plugins/Makefile.am dovecot-1.0.rc22-quota-rewrite/src/plugins/Makefile.am --- dovecot-1.0.rc22/src/plugins/Makefile.am 2007-01-25 17:46:47.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/Makefile.am 2007-02-16 22:27:23.427064161 +0200 @@ -2,4 +2,4 @@ ZLIB = zlib endif -SUBDIRS = acl convert quota imap-quota lazy-expunge mail-log trash $(ZLIB) +SUBDIRS = acl convert quota imap-quota lazy-expunge mail-log $(ZLIB) diff -ruN dovecot-1.0.rc22/src/plugins/Makefile.in dovecot-1.0.rc22-quota-rewrite/src/plugins/Makefile.in --- dovecot-1.0.rc22/src/plugins/Makefile.in 2007-02-06 18:07:05.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/Makefile.in 2007-02-16 22:27:23.427064161 +0200 @@ -54,7 +54,7 @@ ETAGS = etags CTAGS = ctags DIST_SUBDIRS = acl convert quota imap-quota lazy-expunge mail-log \ - trash zlib + zlib DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -182,7 +182,7 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @BUILD_ZLIB_TRUE@ZLIB = zlib -SUBDIRS = acl convert quota imap-quota lazy-expunge mail-log trash $(ZLIB) +SUBDIRS = acl convert quota imap-quota lazy-expunge mail-log $(ZLIB) all: all-recursive .SUFFIXES: diff -ruN dovecot-1.0.rc22/src/plugins/quota/Makefile.am dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/Makefile.am --- dovecot-1.0.rc22/src/plugins/quota/Makefile.am 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/Makefile.am 2007-02-16 22:27:23.428064156 +0200 @@ -7,14 +7,14 @@ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/maildir -lib10_quota_plugin_la_LDFLAGS = -module -avoid-version +lib01_quota_plugin_la_LDFLAGS = -module -avoid-version module_LTLIBRARIES = \ - lib10_quota_plugin.la + lib01_quota_plugin.la -lib10_quota_plugin_la_SOURCES = \ +lib01_quota_plugin_la_SOURCES = \ quota.c \ - quota-count.c \ + quota-count.c \ quota-fs.c \ quota-dict.c \ quota-dirsize.c \ @@ -31,6 +31,6 @@ install-exec-local: for d in imap pop3 lda; do \ $(mkdir_p) $(DESTDIR)$(moduledir)/$$d; \ - rm -f $(DESTDIR)$(moduledir)/$$d/lib10_quota_plugin.so; \ - $(LN_S) ../lib10_quota_plugin.so $(DESTDIR)$(moduledir)/$$d; \ + rm -f $(DESTDIR)$(moduledir)/$$d/lib01_quota_plugin.so; \ + $(LN_S) ../lib01_quota_plugin.so $(DESTDIR)$(moduledir)/$$d; \ done diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota.c dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota.c --- dovecot-1.0.rc22/src/plugins/quota/quota.c 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota.c 2007-02-16 22:28:12.272812480 +0200 @@ -5,6 +5,16 @@ #include "hash.h" #include "quota-private.h" #include "quota-fs.h" +#include + +#define RULE_NAME_ALL_MAILBOXES "*" + +struct quota_root_iter { + struct quota *quota; + struct mailbox *box; + + unsigned int i; +}; unsigned int quota_module_id = 0; @@ -13,7 +23,7 @@ extern struct quota_backend quota_backend_fs; extern struct quota_backend quota_backend_maildir; -static struct quota_backend *quota_backends[] = { +static const struct quota_backend *quota_backends[] = { #ifdef HAVE_FS_QUOTA "a_backend_fs, #endif @@ -23,448 +33,534 @@ }; #define QUOTA_CLASS_COUNT (sizeof(quota_backends)/sizeof(quota_backends[0])) -void (*hook_quota_root_created)(struct quota_root *root); +static int quota_default_test_alloc(struct quota_transaction_context *ctx, + uoff_t size, bool *too_large_r); struct quota *quota_init(void) { struct quota *quota; quota = i_new(struct quota, 1); - ARRAY_CREATE("a->setups, default_pool, struct quota_setup *, 4); + quota->test_alloc = quota_default_test_alloc; + quota->debug = getenv("DEBUG") != NULL; + ARRAY_CREATE("a->roots, system_pool, struct quota_root *, 4); + ARRAY_CREATE("a->storages, system_pool, struct mail_storage *, 8); + return quota; } void quota_deinit(struct quota *quota) { - while (array_count("a->setups) > 0) { - struct quota_setup *const *setup; + struct quota_root **root; - setup = array_idx("a->setups, 0); - quota_setup_deinit(*setup); + while (array_count("a->roots) > 0) { + root = array_idx_modifyable("a->roots, 0); + quota_root_deinit(*root); } - array_free("a->setups); + array_free("a->roots); + array_free("a->storages); i_free(quota); } -struct quota_setup * -quota_setup_init(struct quota *quota, const char *data, bool user_root) +static const struct quota_backend *quota_backend_find(const char *name) { - 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); + for (i = 0; i < QUOTA_CLASS_COUNT; i++) { + if (strcmp(quota_backends[i]->name, name) == 0) + return quota_backends[i]; + } + + return NULL; +} + +struct quota_root *quota_root_init(struct quota *quota, const char *root_def) +{ + struct quota_root *root; + const struct quota_backend *backend; + const char *p, *args, *backend_name; t_push(); - p = strchr(setup->data, ':'); + + /* [:[:]] */ + p = strchr(root_def, ':'); if (p == NULL) { - backend_name = setup->data; - data = ""; + backend_name = root_def; + args = NULL; } else { - backend_name = t_strdup_until(setup->data, p); - data = p+1; + backend_name = t_strdup_until(root_def, p); + args = 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; + + backend = quota_backend_find(backend_name); + if (backend == NULL) + i_fatal("Unknown quota backend: %s", backend_name); + + t_pop(); + + root = backend->v.alloc(); + root->quota = quota; + root->backend = *backend; + root->pool = pool_alloconly_create("quota root", 512); + + if (args != NULL) { + /* save root's name */ + p = strchr(args, ':'); + if (p == NULL) { + root->name = p_strdup(root->pool, args); + args = NULL; + } else { + root->name = p_strdup_until(root->pool, args, p); + args = p + 1; } + } else { + root->name = ""; } - if (setup->backend == NULL) - i_fatal("Unknown quota backend: %s", backend_name); + ARRAY_CREATE(&root->rules, system_pool, struct quota_rule, 4); + array_create(&root->quota_module_contexts, default_pool, + sizeof(void *), 5); - t_pop(); + array_append("a->roots, &root, 1); - array_append("a->setups, &setup, 1); - return setup; + if (backend->v.init != NULL) { + if (backend->v.init(root, args) < 0) { + quota_root_deinit(root); + return NULL; + } + } + return root; } -void quota_setup_deinit(struct quota_setup *setup) +void quota_root_deinit(struct quota_root *root) { - struct quota_setup *const *setups; + pool_t pool = root->pool; + struct quota_root *const *roots; unsigned int i, count; - setups = array_get(&setup->quota->setups, &count); + roots = array_get(&root->quota->roots, &count); for (i = 0; i < count; i++) { - if (setups[i] == setup) { - array_delete(&setup->quota->setups, i, 1); - break; - } + if (roots[i] == root) + array_delete(&root->quota->roots, i, 1); } - 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(&root->rules); + array_free(&root->quota_module_contexts); - array_free(&setup->roots); - i_free(setup->data); - i_free(setup); + root->backend.v.deinit(root); + pool_unref(pool); } -struct quota_root * -quota_root_init(struct quota_setup *setup, const char *name) +static struct quota_rule * +quota_root_rule_find(struct quota_root *root, 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); + struct quota_rule *rules; + unsigned int i, count; - if (hook_quota_root_created != NULL) - hook_quota_root_created(root); - return root; + rules = array_get_modifyable(&root->rules, &count); + for (i = 0; i < count; i++) { + if (strcmp(rules[i].mailbox_name, name) == 0) + return &rules[i]; + } + return NULL; } -void quota_root_deinit(struct quota_root *root) +int quota_root_add_rule(struct quota_root *root, const char *rule_def, + const char **error_r) { - /* 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; + struct quota_rule *rule; + const char **args; + int ret = 0; + + if (*rule_def == '\0') { + *error_r = "Empty rule"; + return -1; + } + + /* : */ + t_push(); + args = t_strsplit(rule_def, ":"); - /* 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); + rule = quota_root_rule_find(root, *args); + if (rule == NULL) { + if (strcmp(*args, RULE_NAME_ALL_MAILBOXES) == 0) + rule = &root->default_rule; + else { + rule = array_append_space(&root->rules); + rule->mailbox_name = p_strdup(root->pool, *args); + } } - /* 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); + for (args++; *args != NULL; args++) { + if (strncmp(*args, "storage=", 8) == 0) + rule->bytes_limit = strtoll(*args + 8, NULL, 10) * 1024; + else if (strncmp(*args, "messages=", 9) == 0) + rule->count_limit = strtoll(*args + 9, NULL, 10); + else { + *error_r = p_strdup_printf(root->pool, + "Invalid rule limit: %s", *args); + ret = -1; break; } } - i_assert(i != count); - array_free(&root->storages); - root->v.deinit(root); - array_free(&module_contexts); + if (root->quota->debug) { + i_info("Quota rule: root=%s mailbox=%s " + "storage=%lldkB messages=%lld", root->name, + rule->mailbox_name != NULL ? rule->mailbox_name : "", + (long long)rule->bytes_limit / 1024, + (long long)rule->count_limit); + } + + t_pop(); + return ret; +} + +static bool quota_root_get_rule_limits(struct quota_root *root, + const char *mailbox_name, + uint64_t *bytes_limit_r, + uint64_t *count_limit_r) +{ + struct quota_rule *rule; + int64_t bytes_limit, count_limit; + bool found; + + bytes_limit = root->default_rule.bytes_limit; + count_limit = root->default_rule.count_limit; + + /* if default rule limits are 0, this rule applies only to specific + mailboxes */ + found = bytes_limit != 0 || count_limit != 0; + + rule = quota_root_rule_find(root, mailbox_name); + if (rule != NULL) { + bytes_limit += rule->bytes_limit; + count_limit += rule->count_limit; + found = TRUE; + } + + *bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit; + *count_limit_r = count_limit <= 0 ? 0 : count_limit; + return found; } void quota_add_user_storage(struct quota *quota, struct mail_storage *storage) { - struct quota_setup *const *setups; struct quota_root *const *roots; - unsigned int i, j, setup_count, root_count; - bool found = FALSE; + struct mail_storage *const *storages; + struct quota_backend **backends; + const char *path, *path2; + unsigned int i, j, count; + bool is_file; + + /* first check if there already exists a storage with the exact same + path. we don't want to count them twice. */ + path = mail_storage_get_mailbox_path(storage, "", &is_file); + if (path != NULL) { + storages = array_get("a->storages, &count); + for (i = 0; i < count; i++) { + path2 = mail_storage_get_mailbox_path(storages[i], "", + &is_file); + if (path2 != NULL && strcmp(path, path2) == 0) { + /* duplicate */ + return; + } + } + } - setups = array_get("a->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; + array_append("a->storages, &storage, 1); - if (quota_mail_storage_add_root(storage, roots[j])) - found = TRUE; + roots = array_get("a->roots, &count); + /* @UNSAFE: get different backends into one array */ + backends = t_new(struct quota_backend *, count + 1); + for (i = 0; i < count; i++) { + for (j = 0; backends[j] != NULL; j++) { + if (backends[j]->name == roots[i]->backend.name) + break; } + if (backends[j] == NULL) + backends[j] = &roots[i]->backend; } - 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); + for (i = 0; backends[i] != NULL; i++) { + if (backends[i]->v.storage_added != NULL) + backends[i]->v.storage_added(quota, storage); } } -struct quota_root *quota_root_lookup(struct quota *quota, const char *name) +void quota_remove_user_storage(struct quota *quota, + struct mail_storage *storage) { - struct quota_setup *const *setups; - struct quota_root *const *roots; - unsigned int i, j, setup_count, root_count; - - setups = array_get("a->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]; + struct mail_storage *const *storages; + unsigned int i, count; + + storages = array_get("a->storages, &count); + for (i = 0; i < count; i++) { + if (storages[i] == storage) { + array_delete("a->storages, i, 1); + break; } } - return NULL; } -const char *quota_root_get_name(struct quota_root *root) -{ - return root->name; -} - -const char *const *quota_root_get_resources(struct quota_root *root) +struct quota_root_iter * +quota_root_iter_init(struct quota *quota, struct mailbox *box) { - 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->v.get_resource(root, name, value_r, limit_r); -} + struct quota_root_iter *iter; -int quota_set_resource(struct quota_root *root, - const char *name, uint64_t value) -{ - return root->v.set_resource(root, name, value); + iter = i_new(struct quota_root_iter, 1); + iter->quota = quota; + iter->box = box; + return iter; } -struct quota_transaction_context *quota_transaction_begin(struct mailbox *box) +struct quota_root *quota_root_iter_next(struct quota_root_iter *iter) { - 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); + struct quota_root *const *roots, *root = NULL; + unsigned int count; + uint64_t value, limit; + int ret; - iter = quota_root_iter_init(box); - while ((root = quota_root_iter_next(iter)) != NULL) { - root_ctx = root->v.transaction_begin(root, ctx, box); - array_append(&ctx->root_transactions, &root_ctx, 1); + roots = array_get(&iter->quota->roots, &count); + if (iter->i >= count) + return NULL; + + for (; iter->i < count; iter->i++) { + ret = quota_get_resource(roots[iter->i], "", + QUOTA_NAME_STORAGE_KILOBYTES, + &value, &limit); + if (ret == 0) { + ret = quota_get_resource(roots[iter->i], "", + QUOTA_NAME_MESSAGES, + &value, &limit); + } + if (ret > 0) { + root = roots[iter->i]; + break; + } } - quota_root_iter_deinit(iter); - return ctx; + + iter->i++; + return root; } -static void quota_transaction_free(struct quota_transaction_context *ctx) +void quota_root_iter_deinit(struct quota_root_iter *iter) { - array_free(&ctx->root_transactions); - i_free(ctx); + i_free(iter); } -int quota_transaction_commit(struct quota_transaction_context *ctx) +struct quota_root *quota_root_lookup(struct quota *quota, const char *name) { - struct quota_root_transaction_context *const *root_transactions; + struct quota_root *const *roots; unsigned int i, count; - int ret = 0; - root_transactions = array_get(&ctx->root_transactions, &count); + roots = array_get("a->roots, &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; + if (strcmp(roots[i]->name, name) == 0) + return roots[i]; } - - quota_transaction_free(ctx); - return ret; + return NULL; } -void quota_transaction_rollback(struct quota_transaction_context *ctx) +const char *quota_root_get_name(struct quota_root *root) { - 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); + return root->name; } -int quota_try_alloc(struct quota_transaction_context *ctx, - struct mail *mail, bool *too_large_r) +const char *const *quota_root_get_resources(struct quota_root *root) { - 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; + return root->backend.v.get_resources(root); } -int quota_try_alloc_bytes(struct quota_transaction_context *ctx, - uoff_t size, bool *too_large_r) +int quota_get_resource(struct quota_root *root, const char *mailbox_name, + const char *name, uint64_t *value_r, uint64_t *limit_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]; + uint64_t bytes_limit, count_limit; + bool kilobytes = FALSE; + int ret; - ret = t->root->v.try_alloc_bytes(t, size, too_large_r); - if (ret <= 0) - break; - } - return ret; + if (strcmp(name, QUOTA_NAME_STORAGE_KILOBYTES) == 0) { + name = QUOTA_NAME_STORAGE_BYTES; + kilobytes = TRUE; + } + + (void)quota_root_get_rule_limits(root, mailbox_name, + &bytes_limit, &count_limit); + if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) + *limit_r = bytes_limit; + else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) + *limit_r = count_limit; + else + *limit_r = 0; + + ret = root->backend.v.get_resource(root, name, value_r, limit_r); + if (kilobytes && ret > 0) { + *value_r /= 1024; + *limit_r /= 1024; + } + return ret <= 0 ? ret : + (*limit_r == 0 ? 0 : 1); +} + +int quota_set_resource(struct quota_root *root __attr_unused__, + const char *name __attr_unused__, + uint64_t value __attr_unused__, const char **error_r) +{ + /* the quota information comes from userdb (or even config file), + so there's really no way to support this until some major changes + are done */ + *error_r = MAIL_STORAGE_ERR_NO_PERMISSION; + return -1; } -int quota_test_alloc_bytes(struct quota_transaction_context *ctx, - uoff_t size, bool *too_large_r) +struct quota_transaction_context *quota_transaction_begin(struct quota *quota, + struct mailbox *box) { - struct quota_root_transaction_context *const *root_transactions; + struct quota_transaction_context *ctx; + struct quota_root *const *roots; + const char *mailbox_name; 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]; + uint64_t current, limit, left; + int ret; - ret = t->root->v.test_alloc_bytes(t, size, too_large_r); - if (ret <= 0) - break; + mailbox_name = mailbox_get_name(box); + + ctx = i_new(struct quota_transaction_context, 1); + ctx->quota = quota; + ctx->box = box; + ctx->bytes_left = (uint64_t)-1; + ctx->count_left = (uint64_t)-1; + + if (quota->counting) { + /* we got here through quota_count_storage() */ + return ctx; } - return ret; -} - -void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail) -{ - struct quota_root_transaction_context *const *root_transactions; - unsigned int i, count; - root_transactions = array_get(&ctx->root_transactions, &count); + /* find the lowest quota limits from all roots and use them */ + roots = array_get("a->roots, &count); for (i = 0; i < count; i++) { - struct quota_root_transaction_context *t = - root_transactions[i]; - - t->root->v.alloc(t, mail); + ret = quota_get_resource(roots[i], mailbox_name, + QUOTA_NAME_STORAGE_BYTES, + ¤t, &limit); + if (ret > 0) { + left = limit < current ? 0 : limit - current; + if (ctx->bytes_left > left) + ctx->bytes_left = left; + } else if (ret < 0) { + ctx->failed = TRUE; + break; + } + + ret = quota_get_resource(roots[i], mailbox_name, + QUOTA_NAME_MESSAGES, ¤t, &limit); + if (ret > 0) { + left = limit < current ? 0 : limit - current; + if (ctx->count_left > left) + ctx->count_left = left; + } else if (ret < 0) { + ctx->failed = TRUE; + break; + } } + return ctx; } -void quota_free(struct quota_transaction_context *ctx, struct mail *mail) +int quota_transaction_commit(struct quota_transaction_context *ctx) { - struct quota_root_transaction_context *const *root_transactions; + struct quota_root *const *roots; 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]; - - t->root->v.free(t, mail); + if (ctx->failed) + ret = -1; + else { + roots = array_get(&ctx->quota->roots, &count); + for (i = 0; i < count; i++) { + if (roots[i]->backend.v.update(roots[i], ctx) < 0) + ret = -1; + } } -} - -const char *quota_last_error(struct quota *quota) -{ - return quota->last_error != NULL ? quota->last_error : "Unknown error"; -} -void quota_set_error(struct quota *quota, const char *errormsg) -{ - i_free(quota->last_error); - quota->last_error = i_strdup(errormsg); + i_free(ctx); + return ret; } -void -quota_default_transaction_rollback(struct quota_root_transaction_context *ctx) +void quota_transaction_rollback(struct quota_transaction_context *ctx) { i_free(ctx); } -int quota_default_try_alloc_bytes(struct quota_root_transaction_context *ctx, - uoff_t size, bool *too_large_r) +int quota_try_alloc(struct quota_transaction_context *ctx, + struct mail *mail, bool *too_large_r) { int ret; - ret = quota_default_test_alloc_bytes(ctx, size, too_large_r); - if (ret <= 0 || ctx->disabled) + ret = quota_test_alloc(ctx, mail_get_physical_size(mail), too_large_r); + if (ret <= 0) return ret; - ctx->count_diff++; - ctx->bytes_diff += size; + quota_alloc(ctx, mail); return 1; } -int quota_default_test_alloc_bytes(struct quota_root_transaction_context *ctx, - uoff_t size, bool *too_large_r) +int quota_test_alloc(struct quota_transaction_context *ctx, + uoff_t size, bool *too_large_r) { - if (ctx->disabled) { - *too_large_r = FALSE; - return 1; - } - if (ctx->bytes_current == (uint64_t)-1) { - /* failure in transaction initialization */ - return -1; - } - - *too_large_r = size > ctx->bytes_limit; - - if (ctx->bytes_current + ctx->bytes_diff + size > ctx->bytes_limit) - return 0; - if (ctx->count_current + ctx->count_diff + 1 > ctx->count_limit) - return 0; - return 1; + return ctx->quota->test_alloc(ctx, size, too_large_r); } -int quota_default_try_alloc(struct quota_root_transaction_context *ctx, - struct mail *mail, bool *too_large_r) +static int quota_default_test_alloc(struct quota_transaction_context *ctx, + uoff_t size, bool *too_large_r) { - uoff_t size; + struct quota_root *const *roots; + unsigned int i, count; - if (ctx->disabled) - return 1; + *too_large_r = FALSE; - size = mail_get_physical_size(mail); - if (size == (uoff_t)-1) { - mail_storage_set_critical(mail->box->storage, - "Quota: Couldn't get new message's size"); + if (ctx->failed) return -1; + if (ctx->count_left != 0 && ctx->bytes_left >= ctx->bytes_used + size) + return 1; + + roots = array_get(&ctx->quota->roots, &count); + for (i = 0; i < count; i++) { + uint64_t bytes_limit, count_limit; + + if (!quota_root_get_rule_limits(roots[i], + mailbox_get_name(ctx->box), + &bytes_limit, &count_limit)) + continue; + + /* if size is bigger than any limit, then + it is bigger than the lowest limit */ + if (size > bytes_limit) { + *too_large_r = TRUE; + break; + } } - return quota_default_try_alloc_bytes(ctx, size, too_large_r); + return 0; } -void quota_default_alloc(struct quota_root_transaction_context *ctx, - struct mail *mail) +void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail) { uoff_t size; - if (ctx->disabled) - return; - size = mail_get_physical_size(mail); if (size != (uoff_t)-1) - ctx->bytes_diff += size; - ctx->count_diff++; + ctx->bytes_used += size; + + ctx->count_used++; } -void quota_default_free(struct quota_root_transaction_context *ctx, - struct mail *mail) +void quota_free(struct quota_transaction_context *ctx, struct mail *mail) { uoff_t size; - if (ctx->disabled) - return; - size = mail_get_physical_size(mail); if (size != (uoff_t)-1) - ctx->bytes_diff -= size; - ctx->count_diff--; + ctx->bytes_used -= size; + + ctx->count_used--; } diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota-count.c dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-count.c --- dovecot-1.0.rc22/src/plugins/quota/quota-count.c 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-count.c 2007-02-16 22:28:12.275812465 +0200 @@ -1,6 +1,7 @@ /* Copyright (C) 2006 Timo Sirainen */ #include "lib.h" +#include "array.h" #include "mail-search.h" #include "mail-storage.h" #include "quota-private.h" @@ -46,24 +47,20 @@ return ret; } -int quota_count_storage(struct mail_storage *storage, - uint64_t *bytes_r, uint64_t *count_r) +static int quota_count_storage(struct mail_storage *storage, + uint64_t *bytes, uint64_t *count) { struct mailbox_list_context *ctx; - struct mailbox_list *list; + struct mailbox_list *info; int ret = 0; - *bytes_r = *count_r = 0; - ctx = mail_storage_mailbox_list_init(storage, "", "*", - MAILBOX_LIST_FAST_FLAGS | - MAILBOX_LIST_INBOX); - while ((list = mail_storage_mailbox_list_next(ctx)) != NULL) { - if ((list->flags & (MAILBOX_NONEXISTENT | - MAILBOX_PLACEHOLDER | + MAILBOX_LIST_FAST_FLAGS); + while ((info = mail_storage_mailbox_list_next(ctx)) != NULL) { + if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) == 0) { - ret = quota_count_mailbox(storage, list->name, - bytes_r, count_r); + ret = quota_count_mailbox(storage, info->name, + bytes, count); if (ret < 0) break; } @@ -73,3 +70,26 @@ return ret; } + +int quota_count(struct quota *quota, uint64_t *bytes_r, uint64_t *count_r) +{ + struct mail_storage *const *storages; + unsigned int i, count; + int ret = 0; + + i_assert(!quota->counting); + + *bytes_r = *count_r = 0; + + quota->counting = TRUE; + + storages = array_get("a->storages, &count); + for (i = 0; i < count; i++) { + ret = quota_count_storage(storages[i], bytes_r, count_r); + if (ret < 0) + break; + } + quota->counting = FALSE; + + return ret; +} diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota-dict.c dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-dict.c --- dovecot-1.0.rc22/src/plugins/quota/quota-dict.c 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-dict.c 2007-02-16 22:28:12.310812284 +0200 @@ -1,7 +1,6 @@ /* Copyright (C) 2005-2006 Timo Sirainen */ #include "lib.h" -#include "array.h" #include "str.h" #include "dict.h" #include "quota-private.h" @@ -15,283 +14,154 @@ struct dict_quota_root { struct quota_root root; struct dict *dict; - - uint64_t message_bytes_limit; - uint64_t message_count_limit; - - unsigned int counting:1; }; extern struct quota_backend quota_backend_dict; -static struct quota_root * -dict_quota_init(struct quota_setup *setup, const char *name) +static struct quota_root *dict_quota_alloc(void) { struct dict_quota_root *root; - struct dict *dict; - const char *uri, *const *args; - unsigned long long message_bytes_limit = 0, message_count_limit = 0; - uri = strchr(setup->data, ' '); - if (uri == NULL) { - i_fatal("dict quota: URI missing from parameters: %s", - setup->data); - } + root = i_new(struct dict_quota_root, 1); + return &root->root; +} - t_push(); - args = t_strsplit(t_strdup_until(setup->data, uri++), ":"); - for (; *args != '\0'; args++) { - if (strncmp(*args, "storage=", 8) == 0) { - message_bytes_limit = - strtoull(*args + 8, NULL, 10) * 1024; - } else if (strncmp(*args, "messages=", 9) == 0) - message_count_limit = strtoull(*args + 9, NULL, 10); - } - t_pop(); +static int dict_quota_init(struct quota_root *_root, const char *args) +{ + struct dict_quota_root *root = (struct dict_quota_root *)_root; + const char *username, *p; - if (getenv("DEBUG") != NULL) { - i_info("dict quota: uri = %s", uri); - i_info("dict quota: byte limit = %llu", message_bytes_limit); - i_info("dict quota: count limit = %llu", message_count_limit); + p = args == NULL ? NULL : strchr(args, ':'); + if (p == NULL) { + i_error("dict quota: URI missing from parameters"); + return -1; } - dict = dict_init(uri, getenv("USER")); - if (dict == NULL) - i_fatal("dict quota: dict_init() failed"); + username = t_strdup_until(args, p); + args = p+1; - root = i_new(struct dict_quota_root, 1); - root->root.name = i_strdup(name); - root->root.v = quota_backend_dict.v; - root->dict = dict; - - root->message_bytes_limit = - message_bytes_limit == 0 ? (uint64_t)-1 : message_bytes_limit; - root->message_count_limit = - message_count_limit == 0 ? (uint64_t)-1 : message_count_limit; - return &root->root; + if (*username == '\0') + username = getenv("USER"); + + if (getenv("DEBUG") != NULL) + i_info("dict quota: user = %s, uri = %s", username, args); + + /* FIXME: we should use 64bit integer as datatype instead but before + it can actually be used don't bother */ + root->dict = dict_init(args, username); + return root->dict != NULL ? 0 : -1; } static void dict_quota_deinit(struct quota_root *_root) { struct dict_quota_root *root = (struct dict_quota_root *)_root; - i_free(root->root.name); + if (root->dict != NULL) + dict_deinit(&root->dict); i_free(root); } -static bool -dict_quota_add_storage(struct quota_root *root __attr_unused__, - struct mail_storage *storage __attr_unused__) -{ - return TRUE; -} - -static void -dict_quota_remove_storage(struct quota_root *root __attr_unused__, - struct mail_storage *storage __attr_unused__) -{ -} - static const char *const * dict_quota_root_get_resources(struct quota_root *root __attr_unused__) { - static const char *resources[] = { QUOTA_NAME_STORAGE, NULL }; + static const char *resources[] = { + QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL + }; return resources; } -static struct mail_storage * -dict_quota_root_get_storage(struct quota_root *root) -{ - /* FIXME: figure out how to support multiple storages */ - struct mail_storage *const *storages; - unsigned int count; - - storages = array_get(&root->storages, &count); - i_assert(count > 0); - - return storages[0]; -} - -static int dict_quota_lookup(struct dict_quota_root *root, const char *path, - uint64_t *value_r) +static int +dict_quota_count(struct dict_quota_root *root, + bool want_bytes, uint64_t *value_r) { struct dict_transaction_context *dt; - const char *value; uint64_t bytes, count; - int ret; - - i_assert(!root->counting); - - t_push(); - ret = dict_lookup(root->dict, unsafe_data_stack_pool, path, &value); - if (ret > 0) { - *value_r = strtoull(value, NULL, 10); - t_pop(); - return 0; - } - t_pop(); - if (ret < 0) - return -1; - - /* not found, recalculate the quota */ - root->counting = TRUE; - ret = quota_count_storage(dict_quota_root_get_storage(&root->root), - &bytes, &count); - root->counting = FALSE; - - if (ret < 0) + if (quota_count(root->root.quota, &bytes, &count) < 0) return -1; t_push(); dt = dict_transaction_begin(root->dict); - if (root->message_bytes_limit != (uint64_t)-1) - dict_set(dt, DICT_QUOTA_CURRENT_BYTES_PATH, dec2str(bytes)); - if (root->message_count_limit != (uint64_t)-1) - dict_set(dt, DICT_QUOTA_CURRENT_COUNT_PATH, dec2str(count)); + dict_set(dt, DICT_QUOTA_CURRENT_BYTES_PATH, dec2str(bytes)); + dict_set(dt, DICT_QUOTA_CURRENT_COUNT_PATH, dec2str(count)); t_pop(); if (dict_transaction_commit(dt) < 0) i_error("dict_quota: Couldn't update quota"); - if (strcmp(path, DICT_QUOTA_CURRENT_BYTES_PATH) == 0) - *value_r = bytes; - else { - i_assert(strcmp(path, DICT_QUOTA_CURRENT_COUNT_PATH) == 0); - *value_r = count; - } - return 0; + *value_r = want_bytes ? bytes : count; + return 1; } static int dict_quota_get_resource(struct quota_root *_root, const char *name, - uint64_t *value_r, uint64_t *limit_r) + uint64_t *value_r, uint64_t *limit __attr_unused__) { struct dict_quota_root *root = (struct dict_quota_root *)_root; + const char *value; + bool want_bytes; + int ret; - if (strcmp(name, QUOTA_NAME_STORAGE) == 0) { - if (root->message_bytes_limit == (uint64_t)-1) - return 0; - - *limit_r = root->message_bytes_limit / 1024; - if (dict_quota_lookup(root, DICT_QUOTA_CURRENT_BYTES_PATH, - value_r) < 0) - return -1; - *value_r /= 1024; - } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) { - if (root->message_count_limit == (uint64_t)-1) - return 0; - - *limit_r = root->message_count_limit; - if (dict_quota_lookup(root, DICT_QUOTA_CURRENT_COUNT_PATH, - value_r) < 0) - return -1; - } else { + if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) + want_bytes = TRUE; + else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) + want_bytes = FALSE; + else return 0; - } - - return 1; -} - -static int -dict_quota_set_resource(struct quota_root *root, - const char *name __attr_unused__, - uint64_t value __attr_unused__) -{ - quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION); - return -1; -} -static struct quota_root_transaction_context * -dict_quota_transaction_begin(struct quota_root *_root, - struct quota_transaction_context *_ctx, - struct mailbox *box __attr_unused__) -{ - struct dict_quota_root *root = (struct dict_quota_root *)_root; - struct quota_root_transaction_context *ctx; + t_push(); + ret = dict_lookup(root->dict, unsafe_data_stack_pool, + want_bytes ? DICT_QUOTA_CURRENT_BYTES_PATH : + DICT_QUOTA_CURRENT_COUNT_PATH, &value); + if (ret < 0) + *value_r = 0; + else if (ret == 0) + ret = dict_quota_count(root, want_bytes, value_r); + else { + long long tmp; - ctx = i_new(struct quota_root_transaction_context, 1); - ctx->root = _root; - ctx->ctx = _ctx; - - ctx->bytes_limit = root->message_bytes_limit; - ctx->count_limit = root->message_count_limit; - - if (root->counting) { - /* created by quota_count_storage(), we don't care about - the quota there */ - ctx->bytes_limit = (uint64_t)-1; - ctx->count_limit = (uint64_t)-1; - return ctx; + /* don't break in case the quota value is negative. */ + tmp = strtoll(value, NULL, 10); + *value_r = tmp < 0 ? 0 : tmp; } - t_push(); - if (ctx->bytes_limit != (uint64_t)-1) { - if (dict_quota_lookup(root, DICT_QUOTA_CURRENT_BYTES_PATH, - &ctx->bytes_current) < 0) - ctx->bytes_current = 0; - } - if (ctx->count_limit != (uint64_t)-1) { - if (dict_quota_lookup(root, DICT_QUOTA_CURRENT_COUNT_PATH, - &ctx->count_current) < 0) - ctx->bytes_current = 0; - } t_pop(); - return ctx; + return ret; } static int -dict_quota_transaction_commit(struct quota_root_transaction_context *ctx) +dict_quota_update(struct quota_root *_root, + struct quota_transaction_context *ctx) { - struct dict_quota_root *root = (struct dict_quota_root *)ctx->root; + struct dict_quota_root *root = (struct dict_quota_root *) _root; struct dict_transaction_context *dt; dt = dict_transaction_begin(root->dict); - if (ctx->bytes_limit != (uint64_t)-1) { + if (ctx->bytes_used != 0) { dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH, - ctx->bytes_diff); + ctx->bytes_used); } - if (ctx->count_limit != (uint64_t)-1) { + if (ctx->count_used != 0) { dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH, - ctx->count_diff); + ctx->count_used); } + if (dict_transaction_commit(dt) < 0) - i_error("dict_quota: Couldn't update quota"); - - i_free(ctx); + return -1; return 0; } -static void -dict_quota_transaction_rollback(struct quota_root_transaction_context *ctx) -{ - i_free(ctx); -} - struct quota_backend quota_backend_dict = { "dict", { + dict_quota_alloc, dict_quota_init, dict_quota_deinit, - - dict_quota_add_storage, - dict_quota_remove_storage, - + NULL, dict_quota_root_get_resources, - dict_quota_get_resource, - dict_quota_set_resource, - - dict_quota_transaction_begin, - dict_quota_transaction_commit, - dict_quota_transaction_rollback, - - quota_default_try_alloc, - quota_default_try_alloc_bytes, - quota_default_test_alloc_bytes, - quota_default_alloc, - quota_default_free + dict_quota_update } }; diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota-dirsize.c dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-dirsize.c --- dovecot-1.0.rc22/src/plugins/quota/quota-dirsize.c 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-dirsize.c 2007-02-16 22:28:12.359812032 +0200 @@ -13,66 +13,27 @@ #include #include -struct dirsize_quota_root { - struct quota_root root; - - uint64_t storage_limit; +struct quota_count_path { + const char *path; + bool is_file; }; extern struct quota_backend quota_backend_dirsize; -static struct quota_root * -dirsize_quota_init(struct quota_setup *setup, const char *name) +static struct quota_root *dirsize_quota_alloc(void) { - struct dirsize_quota_root *root; - const char *const *args; - - 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, ":"); - - for (; *args != '\0'; args++) { - if (strncmp(*args, "storage=", 8) == 0) - root->storage_limit = strtoull(*args + 8, NULL, 10); - } - t_pop(); - - if (getenv("DEBUG") != NULL) { - i_info("dirsize quota limit = %llukB", - (unsigned long long)root->storage_limit); - } - - return &root->root; + return i_new(struct quota_root, 1); } static void dirsize_quota_deinit(struct quota_root *_root) { - struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root; - - i_free(root->root.name); - i_free(root); -} - -static bool -dirsize_quota_add_storage(struct quota_root *root __attr_unused__, - struct mail_storage *storage __attr_unused__) -{ - return TRUE; -} - -static void -dirsize_quota_remove_storage(struct quota_root *root __attr_unused__, - struct mail_storage *storage __attr_unused__) -{ + i_free(_root); } static const char *const * dirsize_quota_root_get_resources(struct quota_root *root __attr_unused__) { - static const char *resources[] = { QUOTA_NAME_STORAGE, NULL }; + static const char *resources[] = { QUOTA_NAME_STORAGE_KILOBYTES, NULL }; return resources; } @@ -133,8 +94,7 @@ return ret; } -static int get_usage(struct dirsize_quota_root *root, - const char *path, bool is_file, uint64_t *value_r) +static int get_usage(const char *path, bool is_file, uint64_t *value_r) { struct stat st; @@ -148,21 +108,14 @@ } *value_r += st.st_size; } else { - if (get_dir_usage(path, value_r) < 0) { - quota_set_error(root->root.setup->quota, - "Internal quota calculation error"); + if (get_dir_usage(path, value_r) < 0) return -1; - } } return 0; } -struct quota_count_path { - const char *path; - bool is_file; -}; - -static void quota_count_path_add(array_t *paths, const char *path, bool is_file) +static void quota_count_path_add(array_t *paths, + const char *path, bool is_file) { ARRAY_SET_TYPE(paths, struct quota_count_path); struct quota_count_path *count_path; @@ -172,7 +125,7 @@ for (i = 0; i < count; i++) { if (strncmp(count_path[i].path, path, strlen(count_path[i].path)) == 0) { - /* this path is already being counted */ + /* this path has already been counted */ return; } if (strncmp(count_path[i].path, path, strlen(path)) == 0) { @@ -190,7 +143,7 @@ } static int -get_quota_root_usage(struct dirsize_quota_root *root, uint64_t *value_r) +get_quota_root_usage(struct quota_root *root, uint64_t *value_r) { struct mail_storage *const *storages; array_t ARRAY_DEFINE(paths, struct quota_count_path); @@ -202,7 +155,7 @@ t_push(); ARRAY_CREATE(&paths, pool_datastack_create(), struct quota_count_path, 8); - storages = array_get(&root->root.storages, &count); + storages = array_get(&root->quota->storages, &count); for (i = 0; i < count; i++) { path = mail_storage_get_mailbox_path(storages[i], "", &is_file); quota_count_path_add(&paths, path, is_file); @@ -214,9 +167,10 @@ } /* now sum up the found paths */ + *value_r = 0; count_paths = array_get(&paths, &count); for (i = 0; i < count; i++) { - if (get_usage(root, count_paths[i].path, count_paths[i].is_file, + if (get_usage(count_paths[i].path, count_paths[i].is_file, value_r) < 0) { t_pop(); return -1; @@ -224,98 +178,39 @@ } t_pop(); - return 0; } static int dirsize_quota_get_resource(struct quota_root *_root, const char *name, - uint64_t *value_r, uint64_t *limit_r) + uint64_t *value_r, uint64_t *limit __attr_unused__) { - struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root; - - *value_r = 0; - *limit_r = 0; - - if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0) + if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0) return 0; - if (get_quota_root_usage(root, value_r) < 0) + if (get_quota_root_usage(_root, value_r) < 0) return -1; - *value_r /= 1024; - *limit_r = root->storage_limit; return 1; } -static int -dirsize_quota_set_resource(struct quota_root *root, - const char *name __attr_unused__, - uint64_t value __attr_unused__) -{ - quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION); - return -1; -} - -static struct quota_root_transaction_context * -dirsize_quota_transaction_begin(struct quota_root *_root, - struct quota_transaction_context *_ctx, - struct mailbox *box __attr_unused__) -{ - struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root; - struct quota_root_transaction_context *ctx; - - 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_quota_root_usage(root, &ctx->bytes_current) < 0 || - ctx->bytes_current == (uint64_t)-1) { - ctx->bytes_current = (uint64_t)-1; - quota_set_error(_root->setup->quota, - "Internal quota calculation error"); - } - - ctx->bytes_limit = root->storage_limit * 1024; - ctx->count_limit = (uint64_t)-1; - return ctx; -} - -static int -dirsize_quota_transaction_commit(struct quota_root_transaction_context *ctx) +static int +dirsize_quota_update(struct quota_root *root __attr_unused__, + struct quota_transaction_context *ctx __attr_unused__) { - int ret = ctx->bytes_current == (uint64_t)-1 ? -1 : 0; - - i_free(ctx); - return ret; + return 0; } struct quota_backend quota_backend_dirsize = { "dirsize", { - dirsize_quota_init, + dirsize_quota_alloc, + NULL, dirsize_quota_deinit, - - dirsize_quota_add_storage, - dirsize_quota_remove_storage, - + NULL, dirsize_quota_root_get_resources, - dirsize_quota_get_resource, - dirsize_quota_set_resource, - - dirsize_quota_transaction_begin, - dirsize_quota_transaction_commit, - quota_default_transaction_rollback, - - quota_default_try_alloc, - quota_default_try_alloc_bytes, - quota_default_test_alloc_bytes, - quota_default_alloc, - quota_default_free + dirsize_quota_update } }; diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota-fs.c dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-fs.c --- dovecot-1.0.rc22/src/plugins/quota/quota-fs.c 2007-02-05 14:08:50.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-fs.c 2007-02-16 22:28:12.406811790 +0200 @@ -56,22 +56,13 @@ struct fs_quota_mountpoint *mount; }; -struct fs_quota_root_iter { - struct quota_root_iter iter; - - bool sent; -}; - extern struct quota_backend quota_backend_fs; -static struct quota_root * -fs_quota_init(struct quota_setup *setup __attr_unused__, const char *name) +static struct quota_root *fs_quota_alloc(void) { 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; @@ -99,7 +90,6 @@ if (root->mount != NULL) fs_quota_mountpoint_free(root->mount); - i_free(root->root.name); i_free(root); } @@ -120,42 +110,66 @@ return mount; } -static bool fs_quota_add_storage(struct quota_root *_root, - struct mail_storage *storage) +static struct fs_quota_root * +fs_quota_root_find_mountpoint(struct quota *quota, + const struct fs_quota_mountpoint *mount) +{ + struct quota_root *const *roots; + struct fs_quota_root *empty = NULL; + unsigned int i, count; + + roots = array_get("a->roots, &count); + for (i = 0; i < count; i++) { + if (roots[i]->backend.name == quota_backend_fs.name) { + struct fs_quota_root *root = + (struct fs_quota_root *)roots[i]; + + if (root->mount == NULL) + empty = root; + else if (strcmp(root->mount->mount_path, + mount->mount_path) == 0) + return root; + } + } + return empty; +} + +static void fs_quota_storage_added(struct quota *quota, + struct mail_storage *storage) { - struct fs_quota_root *root = (struct fs_quota_root *)_root; struct fs_quota_mountpoint *mount; + struct quota_root *_root; + struct fs_quota_root *root; const char *dir; bool is_file; dir = mail_storage_get_mailbox_path(storage, "", &is_file); - - if (getenv("DEBUG") != NULL) - i_info("fs quota add storage dir = %s", dir); - 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; + if (getenv("DEBUG") != NULL) { + i_info("fs quota add storage dir = %s", dir); + i_info("fs quota block device = %s", mount->device_path); + i_info("fs quota mount point = %s", mount->mount_path); + } + root = fs_quota_root_find_mountpoint(quota, mount); + if (root != NULL && root->mount != NULL) { + /* already exists */ fs_quota_mountpoint_free(mount); - if (!match) { - /* different mountpoints, can't use this */ - return FALSE; - } - mount = root->mount; + return; } - 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 (root == NULL) { + /* create a new root for this mountpoint */ + _root = quota_root_init(quota, quota_backend_fs.name); + root = (struct fs_quota_root *)_root; + root->root.name = + p_strdup_printf(root->root.pool, "%s%d", + quota_backend_fs.name, + array_count("a->roots)); + } else { + /* this is the default root. */ } + root->mount = mount; #ifdef HAVE_Q_QUOTACTL if (mount->path == NULL) { @@ -165,19 +179,12 @@ i_error("open(%s) failed: %m", mount->path); } #endif - return TRUE; -} - -static void -fs_quota_remove_storage(struct quota_root *root __attr_unused__, - struct mail_storage *storage __attr_unused__) -{ } static const char *const * fs_quota_root_get_resources(struct quota_root *root __attr_unused__) { - static const char *resources[] = { QUOTA_NAME_STORAGE, NULL }; + static const char *resources[] = { QUOTA_NAME_STORAGE_KILOBYTES, NULL }; return resources; } @@ -195,7 +202,8 @@ *value_r = 0; *limit_r = 0; - if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0 || root->mount == NULL) + if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0 || + root->mount == NULL) return 0; #if defined (HAVE_QUOTACTL) && defined(HAVE_SYS_QUOTA_H) @@ -210,14 +218,12 @@ root->uid, (caddr_t)&xdqblk) < 0) { i_error("quotactl(Q_XGETQUOTA, %s) failed: %m", root->mount->device_path); - quota_set_error(_root->setup->quota, - "Internal quota error"); return -1; } /* values always returned in 512 byte blocks */ - *value_r = xdqblk.d_bcount >> 1; - *limit_r = xdqblk.d_blk_softlimit >> 1; + *value_r = xdqblk.d_bcount * 512; + *limit_r = xdqblk.d_blk_softlimit * 512; } else #endif { @@ -233,13 +239,11 @@ "(--with-linux-quota configure option)", _LINUX_QUOTA_VERSION); } - quota_set_error(_root->setup->quota, - "Internal quota error"); return -1; } - *value_r = dqblk.dqb_curblocks / 1024; - *limit_r = dqblk.dqb_bsoftlimit; + *value_r = dqblk.dqb_curblocks; + *limit_r = dqblk.dqb_bsoftlimit * 1024; } #elif defined(HAVE_QUOTACTL) /* BSD, AIX */ @@ -247,11 +251,10 @@ root->uid, (void *)&dqblk) < 0) { i_error("quotactl(Q_GETQUOTA, %s) failed: %m", root->mount->mount_path); - quota_set_error(_root->setup->quota, "Internal quota error"); return -1; } - *value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE / 1024; - *limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE / 1024; + *value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE; + *limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE; #else /* Solaris */ if (root->mount->fd == -1) @@ -262,42 +265,18 @@ ctl.addr = (caddr_t)&dqblk; 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; } - *value_r = (uint64_t)dqblk.dqb_curblocks * 1024 / DEV_BSIZE; - *limit_r = (uint64_t)dqblk.dqb_bsoftlimit * 1024 / DEV_BSIZE; + *value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE; + *limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE; #endif return 1; } -static int -fs_quota_set_resource(struct quota_root *root, - const char *name __attr_unused__, - uint64_t value __attr_unused__) +static int +fs_quota_update(struct quota_root *root __attr_unused__, + struct quota_transaction_context *ctx __attr_unused__) { - quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION); - return -1; -} - -static struct quota_root_transaction_context * -fs_quota_transaction_begin(struct quota_root *root, - struct quota_transaction_context *ctx, - struct mailbox *box __attr_unused__) -{ - struct quota_root_transaction_context *root_ctx; - - root_ctx = i_new(struct quota_root_transaction_context, 1); - root_ctx->root = root; - root_ctx->ctx = ctx; - root_ctx->disabled = TRUE; - return root_ctx; -} - -static int -fs_quota_transaction_commit(struct quota_root_transaction_context *ctx) -{ - i_free(ctx); return 0; } @@ -305,26 +284,15 @@ "fs", { - fs_quota_init, + fs_quota_alloc, + NULL, fs_quota_deinit, - fs_quota_add_storage, - fs_quota_remove_storage, + fs_quota_storage_added, fs_quota_root_get_resources, - fs_quota_get_resource, - fs_quota_set_resource, - - fs_quota_transaction_begin, - fs_quota_transaction_commit, - quota_default_transaction_rollback, - - quota_default_try_alloc, - quota_default_try_alloc_bytes, - quota_default_test_alloc_bytes, - quota_default_alloc, - quota_default_free + fs_quota_update } }; diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota.h dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota.h --- dovecot-1.0.rc22/src/plugins/quota/quota.h 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota.h 2007-02-16 22:28:12.442811604 +0200 @@ -5,7 +5,9 @@ struct mailbox; /* Message storage size kilobytes. */ -#define QUOTA_NAME_STORAGE "STORAGE" +#define QUOTA_NAME_STORAGE_KILOBYTES "STORAGE" +/* Message storage size bytes. This is used only internally. */ +#define QUOTA_NAME_STORAGE_BYTES "STORAGE_BYTES" /* Number of messages. */ #define QUOTA_NAME_MESSAGES "MESSAGES" @@ -14,25 +16,20 @@ struct quota_root_iter; struct quota_transaction_context; -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); +struct quota_root *quota_root_init(struct quota *quota, const char *root_def); void quota_root_deinit(struct quota_root *root); +/* Add a new rule too the quota root. Returns 0 if ok, -1 if rule is invalid. */ +int quota_root_add_rule(struct quota_root *root, const char *rule_def, + const char **error_r); + /* List all quota roots. Returned quota roots are freed by quota_deinit(). */ -struct quota_root_iter *quota_root_iter_init(struct mailbox *box); +struct quota_root_iter * +quota_root_iter_init(struct quota *quota, struct mailbox *box); struct quota_root *quota_root_iter_next(struct quota_root_iter *iter); void quota_root_iter_deinit(struct quota_root_iter *iter); @@ -45,14 +42,15 @@ const char *const *quota_root_get_resources(struct quota_root *root); /* Returns 1 if quota value was found, 0 if not, -1 if error. */ -int quota_get_resource(struct quota_root *root, +int quota_get_resource(struct quota_root *root, const char *mailbox_name, const char *name, uint64_t *value_r, uint64_t *limit_r); /* Returns 0 if OK, -1 if error (eg. permission denied, invalid name). */ -int quota_set_resource(struct quota_root *root, - const char *name, uint64_t value); +int quota_set_resource(struct quota_root *root, const char *name, + uint64_t value, const char **error_r); /* Start a new quota transaction. */ -struct quota_transaction_context *quota_transaction_begin(struct mailbox *box); +struct quota_transaction_context *quota_transaction_begin(struct quota *quota, + 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. */ @@ -63,16 +61,11 @@ too_large_r is set to TRUE. */ int quota_try_alloc(struct quota_transaction_context *ctx, struct mail *mail, bool *too_large_r); -int quota_try_alloc_bytes(struct quota_transaction_context *ctx, - uoff_t size, bool *too_large_r); -/* Like quota_try_alloc_bytes(), but don't actually update the quota. */ -int quota_test_alloc_bytes(struct quota_transaction_context *ctx, - uoff_t size, bool *too_large_r); +/* Like quota_try_alloc(), but don't actually allocate anything. */ +int quota_test_alloc(struct quota_transaction_context *ctx, + uoff_t size, bool *too_large_r); /* Update quota by allocating/freeing space used by mail. */ void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail); void quota_free(struct quota_transaction_context *ctx, struct mail *mail); -/* Returns the last error message. */ -const char *quota_last_error(struct quota *quota); - #endif diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota-maildir.c dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-maildir.c --- dovecot-1.0.rc22/src/plugins/quota/quota-maildir.c 2007-01-26 14:59:10.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-maildir.c 2007-02-16 22:28:12.470811460 +0200 @@ -16,14 +16,13 @@ #include #include -#define MAILDIR_TRASH_MAILBOX "Trash" #define MAILDIRSIZE_FILENAME "maildirsize" #define MAILDIRSIZE_STALE_SECS (60*15) struct maildir_quota_root { struct quota_root root; - char *ignore; + const char *maildirsize_path; uint64_t message_bytes_limit; uint64_t message_count_limit; @@ -31,13 +30,13 @@ uint64_t total_count; int fd; + time_t recalc_last_stamp; + unsigned int limits_initialized:1; unsigned int master_message_limits:1; }; struct maildir_list_context { - struct maildir_quota_root *root; - struct mailbox_list_context *ctx; struct mailbox_list *list; @@ -60,8 +59,8 @@ MEMBER(use_excl_lock) FALSE }; -static int maildir_sum_dir(struct mail_storage *storage, const char *dir, - uint64_t *total_bytes, uint64_t *total_count) +static int maildir_sum_dir(const char *dir, uint64_t *total_bytes, + uint64_t *total_count) { DIR *dirp; struct dirent *dp; @@ -75,8 +74,7 @@ if (dirp == NULL) { if (errno == ENOENT || errno == ESTALE) return 0; - mail_storage_set_critical(storage, "opendir(%s) failed: %m", - dir); + i_error("opendir(%s) failed: %m", dir); return -1; } @@ -115,28 +113,25 @@ *total_bytes += st.st_size; *total_count += 1; } else if (errno != ENOENT && errno != ESTALE) { - mail_storage_set_critical(storage, - "stat(%s) failed: %m", str_c(path)); + i_error("stat(%s) failed: %m", str_c(path)); ret = -1; } } } if (closedir(dirp) < 0) { - mail_storage_set_critical(storage, "closedir(%s) failed: %m", - dir); + i_error("closedir(%s) failed: %m", dir); return -1; } return ret; } static struct maildir_list_context * -maildir_list_init(struct maildir_quota_root *root, struct mail_storage *storage) +maildir_list_init(struct mail_storage *storage) { struct maildir_list_context *ctx; ctx = i_new(struct maildir_list_context, 1); - ctx->root = root; ctx->path = str_new(default_pool, 512); ctx->ctx = mail_storage_mailbox_list_init(storage, "", "*", MAILBOX_LIST_FAST_FLAGS | @@ -158,10 +153,6 @@ return NULL; } - if (ctx->root->ignore != NULL && - strcmp(ctx->list->name, ctx->root->ignore) == 0) - continue; - t_push(); path = mail_storage_get_mailbox_path(ctx->ctx->storage, ctx->list->name, @@ -179,8 +170,7 @@ /* ignore if the directory got lost, stale or if it was actually a file and not a directory */ if (errno != ENOENT && errno != ESTALE && errno != ENOTDIR) { - mail_storage_set_critical(ctx->ctx->storage, - "stat(%s) failed: %m", str_c(ctx->path)); + i_error("stat(%s) failed: %m", str_c(ctx->path)); ctx->state = 0; } } @@ -199,14 +189,13 @@ } static int -maildirs_check_have_changed(struct maildir_quota_root *root, - struct mail_storage *storage, time_t latest_mtime) +maildirs_check_have_changed(struct mail_storage *storage, time_t latest_mtime) { struct maildir_list_context *ctx; time_t mtime; int ret = 0; - ctx = maildir_list_init(root, storage); + ctx = maildir_list_init(storage); while (maildir_list_next(ctx, &mtime) != NULL) { if (mtime > latest_mtime) { ret = 1; @@ -218,8 +207,7 @@ return ret; } -static int maildirsize_write(struct maildir_quota_root *root, - struct mail_storage *storage, const char *path) +static int maildirsize_write(struct maildir_quota_root *root, const char *path) { struct dotlock *dotlock; string_t *str; @@ -227,8 +215,7 @@ i_assert(root->fd == -1); - dotlock_settings.use_excl_lock = - (storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0; + dotlock_settings.use_excl_lock = getenv("DOTLOCK_USE_EXCL") != NULL; fd = file_dotlock_open(&dotlock_settings, path, DOTLOCK_CREATE_FLAG_NONBLOCK, &dotlock); if (fd == -1) { @@ -237,8 +224,7 @@ return -1; } - mail_storage_set_critical(storage, - "file_dotlock_open(%s) failed: %m", path); + i_error("file_dotlock_open(%s) failed: %m", path); return -1; } @@ -257,8 +243,7 @@ (unsigned long long)root->total_bytes, (unsigned long long)root->total_count); if (write_full(fd, str_data(str), str_len(str)) < 0) { - mail_storage_set_critical(storage, - "write_full(%s) failed: %m", path); + i_error("write_full(%s) failed: %m", path); file_dotlock_delete(&dotlock); return -1; } @@ -266,38 +251,34 @@ /* keep the fd open since we might want to update it later */ if (file_dotlock_replace(&dotlock, DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) { - mail_storage_set_critical(storage, - "file_dotlock_replace(%s) failed: %m", path); + i_error("file_dotlock_replace(%s) failed: %m", path); return -1; } root->fd = fd; return 0; } -static const char *maildirsize_get_path(struct mail_storage *storage) +static void maildirsize_recalculate_init(struct maildir_quota_root *root) { - return t_strconcat(mail_storage_get_mailbox_control_dir(storage, ""), - "/"MAILDIRSIZE_FILENAME, NULL); + root->total_bytes = root->total_count = 0; + root->recalc_last_stamp = 0; } -static int maildirsize_recalculate(struct maildir_quota_root *root, - struct mail_storage *storage) +static int maildirsize_recalculate_storage(struct maildir_quota_root *root, + struct mail_storage *storage) { struct maildir_list_context *ctx; - const char *dir, *path; - time_t mtime, last_stamp = 0; + const char *dir; + time_t mtime; int ret = 0; - root->total_bytes = root->total_count = 0; - - ctx = maildir_list_init(root, storage); + ctx = maildir_list_init(storage); while ((dir = maildir_list_next(ctx, &mtime)) != NULL) { - if (mtime > last_stamp) - last_stamp = mtime; + if (mtime > root->recalc_last_stamp) + root->recalc_last_stamp = mtime; t_push(); - if (maildir_sum_dir(storage, dir, - &root->total_bytes, + if (maildir_sum_dir(dir, &root->total_bytes, &root->total_count) < 0) ret = -1; t_pop(); @@ -305,27 +286,58 @@ if (maildir_list_deinit(ctx) < 0) ret = -1; - if (ret == 0) - ret = maildirs_check_have_changed(root, storage, last_stamp); + return ret; +} - t_push(); - path = maildirsize_get_path(storage); +static int maildirsize_recalculate_finish(struct maildir_quota_root *root, + int ret) +{ if (ret == 0) { /* maildir didn't change, we can write the maildirsize file */ - ret = maildirsize_write(root, storage, path); + ret = maildirsize_write(root, root->maildirsize_path); } if (ret != 0) { /* make sure it gets rebuilt later */ - if (unlink(path) < 0 && errno != ENOENT && errno != ESTALE) { - mail_storage_set_critical(storage, - "unlink(%s) failed: %m", path); + if (unlink(root->maildirsize_path) < 0 && + errno != ENOENT && errno != ESTALE) { + i_error("unlink(%s) failed: %m", + root->maildirsize_path); } } - t_pop(); return ret; } +static int maildirsize_recalculate(struct maildir_quota_root *root) +{ + struct mail_storage *const *storages; + unsigned int i, count; + int ret = 0; + + maildirsize_recalculate_init(root); + + /* count mails from all storages */ + storages = array_get(&root->root.quota->storages, &count); + for (i = 0; i < count; i++) { + if (maildirsize_recalculate_storage(root, storages[i]) < 0) { + ret = -1; + break; + } + } + + if (ret == 0) { + /* check if any of the directories have changed */ + for (i = 0; i < count; i++) { + ret = maildirs_check_have_changed(storages[i], + root->recalc_last_stamp); + if (ret != 0) + break; + } + } + + return maildirsize_recalculate_finish(root, ret); +} + static int maildirsize_parse(struct maildir_quota_root *root, int fd, const char *const *lines) { @@ -408,32 +420,26 @@ return 1; } -static int maildirsize_read(struct maildir_quota_root *root, - struct mail_storage *storage) +static int maildirsize_read(struct maildir_quota_root *root) { - const char *path; char buf[5120+1]; unsigned int size; int fd, ret = 0; t_push(); - path = maildirsize_get_path(storage); if (root->fd != -1) { - if (close(root->fd) < 0) { - mail_storage_set_critical(storage, - "close(%s) failed: %m", path); - } + if (close(root->fd) < 0) + i_error("close(%s) failed: %m", root->maildirsize_path); root->fd = -1; } - fd = nfs_safe_open(path, O_RDWR | O_APPEND); + fd = nfs_safe_open(root->maildirsize_path, O_RDWR | O_APPEND); if (fd == -1) { if (errno == ENOENT) ret = 0; else { ret = -1; - mail_storage_set_critical(storage, - "open(%s) failed: %m", path); + i_error("open(%s) failed: %m", root->maildirsize_path); } t_pop(); return ret; @@ -446,8 +452,7 @@ if (ret < 0) { if (errno == ESTALE) break; - mail_storage_set_critical(storage, "read(%s) failed: %m", - path); + i_error("read(%s) failed: %m", root->maildirsize_path); } size += ret; } @@ -478,12 +483,26 @@ return ret; } -static int maildirquota_refresh(struct maildir_quota_root *root, - struct mail_storage *storage) +static void maildirquota_init_limits(struct maildir_quota_root *root) +{ + root->limits_initialized = TRUE; + + if (root->root.default_rule.bytes_limit != 0 || + root->root.default_rule.count_limit != 0) { + root->master_message_limits = TRUE; + root->message_bytes_limit = root->root.default_rule.bytes_limit; + root->message_count_limit = root->root.default_rule.count_limit; + } +} + +static int maildirquota_refresh(struct maildir_quota_root *root) { int ret; - ret = maildirsize_read(root, storage); + if (!root->limits_initialized) + maildirquota_init_limits(root); + + ret = maildirsize_read(root); if (ret == 0) { if (root->message_bytes_limit == (uint64_t)-1 && root->message_count_limit == (uint64_t)-1) { @@ -491,13 +510,12 @@ return 0; } - ret = maildirsize_recalculate(root, storage); + ret = maildirsize_recalculate(root); } return ret < 0 ? -1 : 0; } static int maildirsize_update(struct maildir_quota_root *root, - struct mail_storage *storage, int count_diff, int64_t bytes_diff) { const char *str; @@ -519,52 +537,22 @@ if (errno == ESTALE) { /* deleted/replaced already, ignore */ } else { - mail_storage_set_critical(storage, - "write_full(%s) failed: %m", - maildirsize_get_path(storage)); + i_error("write_full(%s) failed: %m", + root->maildirsize_path); } } t_pop(); return ret; } -static struct quota_root * -maildir_quota_init(struct quota_setup *setup, const char *name __attr_unused__) +static struct quota_root *maildir_quota_alloc(void) { struct maildir_quota_root *root; - const char *const *args; - unsigned long long size; root = i_new(struct maildir_quota_root, 1); - root->root.name = i_strdup(name); - root->root.v = quota_backend_maildir.v; root->fd = -1; root->message_bytes_limit = (uint64_t)-1; root->message_count_limit = (uint64_t)-1; - - t_push(); - args = t_strsplit(setup->data, ":"); - - for (args++; *args != '\0'; args++) { - if (strncmp(*args, "storage=", 8) == 0) { - size = strtoull(*args + 8, NULL, 10) * 1024; - if (size != 0) - root->message_bytes_limit = size; - root->master_message_limits = TRUE; - } else if (strncmp(*args, "messages=", 9) == 0) { - size = strtoull(*args + 9, NULL, 10); - if (size != 0) - root->message_count_limit = size; - root->master_message_limits = TRUE; - } else if (strncmp(*args, "ignore=", 7) == 0) { - i_free(root->ignore); - root->ignore = i_strdup(*args + 7); - } else { - i_error("maildir quota: Unknown setting: %s", *args); - } - } - t_pop(); - return &root->root; } @@ -574,37 +562,52 @@ if (root->fd != -1) (void)close(root->fd); - - i_free(root->ignore); - i_free(root->root.name); i_free(root); } -static bool -maildir_quota_add_storage(struct quota_root *root __attr_unused__, - struct mail_storage *_storage) -{ - if (strcmp(_storage->name, "maildir") == 0) { - struct maildir_storage *storage = - (struct maildir_storage *)_storage; +static void +maildir_quota_root_storage_added(struct quota_root *_root, + struct mail_storage *storage) +{ + struct maildir_quota_root *root = (struct maildir_quota_root *)_root; + const char *control_dir; - /* For newly generated filenames add ,S=size. */ - storage->save_size_in_filename = TRUE; - } - return TRUE; + if (root->maildirsize_path != NULL) + return; + + control_dir = mail_storage_get_mailbox_control_dir(storage, ""); + root->maildirsize_path = + p_strconcat(_root->pool, control_dir, + "/"MAILDIRSIZE_FILENAME, NULL); } static void -maildir_quota_remove_storage(struct quota_root *root __attr_unused__, - struct mail_storage *storage __attr_unused__) +maildir_quota_storage_added(struct quota *quota, + struct mail_storage *_storage) { + struct maildir_storage *storage = + (struct maildir_storage *)_storage; + struct quota_root **roots; + unsigned int i, count; + + if (strcmp(_storage->name, "maildir") != 0) + return; + + roots = array_get_modifyable("a->roots, &count); + for (i = 0; i < count; i++) { + if (roots[i]->backend.name == quota_backend_maildir.name) + maildir_quota_root_storage_added(roots[i], _storage); + } + + /* For newly generated filenames add ,S=size. */ + storage->save_size_in_filename = TRUE; } static const char *const * maildir_quota_root_get_resources(struct quota_root *root __attr_unused__) { static const char *resources_both[] = { - QUOTA_NAME_STORAGE, + QUOTA_NAME_STORAGE_KILOBYTES, QUOTA_NAME_MESSAGES, NULL }; @@ -612,128 +615,49 @@ return resources_both; } -static struct mail_storage * -maildir_quota_root_get_storage(struct quota_root *root) -{ - /* FIXME: figure out how to support multiple storages */ - struct mail_storage *const *storages; - unsigned int count; - - storages = array_get(&root->storages, &count); - i_assert(count > 0); - - return storages[0]; -} - static int maildir_quota_get_resource(struct quota_root *_root, const char *name, - uint64_t *value_r, uint64_t *limit_r) + uint64_t *value_r, uint64_t *limit __attr_unused__) { struct maildir_quota_root *root = (struct maildir_quota_root *)_root; - if (maildirquota_refresh(root, - maildir_quota_root_get_storage(_root)) < 0) + if (maildirquota_refresh(root) < 0) return -1; - if (strcmp(name, QUOTA_NAME_STORAGE) == 0) { - if (root->message_bytes_limit == (uint64_t)-1) - return 0; - - *limit_r = root->message_bytes_limit / 1024; - *value_r = root->total_bytes / 1024; - } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) { - if (root->message_count_limit == (uint64_t)-1) - return 0; - - *limit_r = root->message_count_limit; + if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) + *value_r = root->total_bytes; + else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) *value_r = root->total_count; - } else { + else return 0; - } return 1; } static int -maildir_quota_set_resource(struct quota_root *root, - const char *name __attr_unused__, - uint64_t value __attr_unused__) -{ - quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION); - return -1; -} - -static struct quota_root_transaction_context * -maildir_quota_transaction_begin(struct quota_root *_root, - struct quota_transaction_context *_ctx, - struct mailbox *box) -{ - struct maildir_quota_root *root = (struct maildir_quota_root *)_root; - struct quota_root_transaction_context *ctx; - - ctx = i_new(struct quota_root_transaction_context, 1); - ctx->root = _root; - ctx->ctx = _ctx; - - if (root->ignore != NULL && - strcmp(mailbox_get_name(box), root->ignore) == 0) { - ctx->bytes_limit = (uint64_t)-1; - ctx->count_limit = (uint64_t)-1; - return ctx; - } - - if (maildirquota_refresh(root, - maildir_quota_root_get_storage(_root)) < 0) { - /* failed calculating the current quota */ - ctx->bytes_current = (uint64_t)-1; - } else { - ctx->bytes_limit = root->message_bytes_limit; - ctx->count_limit = root->message_count_limit; - ctx->bytes_current = root->total_bytes; - ctx->count_current = root->total_count; - } - return ctx; -} - -static int -maildir_quota_transaction_commit(struct quota_root_transaction_context *ctx) +maildir_quota_update(struct quota_root *_root, + struct quota_transaction_context *ctx) { struct maildir_quota_root *root = - (struct maildir_quota_root *)ctx->root; - int ret = ctx->bytes_current == (uint64_t)-1 ? -1 : 0; + (struct maildir_quota_root *) _root; - if (root->fd != -1 && ret == 0) { + if (root->fd != -1) { /* if writing fails, we don't care all that much */ - (void)maildirsize_update(root, - maildir_quota_root_get_storage(ctx->root), - ctx->count_diff, ctx->bytes_diff); + (void)maildirsize_update(root, ctx->count_used, + ctx->bytes_used); } - i_free(ctx); - return ret; + return 0; } struct quota_backend quota_backend_maildir = { "maildir", { - maildir_quota_init, + maildir_quota_alloc, + NULL, maildir_quota_deinit, - - maildir_quota_add_storage, - maildir_quota_remove_storage, - + maildir_quota_storage_added, maildir_quota_root_get_resources, - maildir_quota_get_resource, - maildir_quota_set_resource, - - maildir_quota_transaction_begin, - maildir_quota_transaction_commit, - quota_default_transaction_rollback, - - quota_default_try_alloc, - quota_default_try_alloc_bytes, - quota_default_test_alloc_bytes, - quota_default_alloc, - quota_default_free + maildir_quota_update } }; diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota-plugin.c dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-plugin.c --- dovecot-1.0.rc22/src/plugins/quota/quota-plugin.c 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-plugin.c 2007-02-16 22:28:12.485811382 +0200 @@ -1,4 +1,4 @@ -/* Copyright (C) 2005 Timo Sirainen */ +/* Copyright (C) 2005 Timo Sirainen & Tianyan Liu */ #include "lib.h" #include "mail-storage.h" @@ -14,20 +14,68 @@ struct quota *quota_set; +static void quota_root_add_rules(const char *root_name, + struct quota_root *root) +{ + const char *rule_name, *rule, *error; + unsigned int i; + + t_push(); + + rule_name = t_strconcat(root_name, "_RULE", NULL); + for (i = 2;; i++) { + rule = getenv(rule_name); + + if (rule == NULL) + break; + + if (quota_root_add_rule(root, rule, &error) < 0) { + i_fatal("Quota root %s: Invalid rule: %s", + root_name, rule); + } + rule_name = t_strdup_printf("%s_RULE%d", root_name, i); + } + + t_pop(); +} + void quota_plugin_init(void) { + struct quota_root *root; + unsigned int i; const char *env; env = getenv("QUOTA"); - if (env != NULL) { - quota_set = quota_init(); - /* Currently we support only one quota setup */ - (void)quota_setup_init(quota_set, env, TRUE); - - quota_next_hook_mail_storage_created = - hook_mail_storage_created; - hook_mail_storage_created = quota_mail_storage_created; + if (env == NULL) + return; + + quota_set = quota_init(); + + root = quota_root_init(quota_set, env); + if (root == NULL) + i_fatal("Couldn't create quota root: %s", env); + quota_root_add_rules("QUOTA", root); + + t_push(); + for (i = 2;; i++) { + const char *root_name; + + root_name = t_strdup_printf("QUOTA%d", i); + env = getenv(root_name); + + if (env == NULL) + break; + + root = quota_root_init(quota_set, env); + if (root == NULL) + i_fatal("Couldn't create quota root: %s", env); + quota_root_add_rules(root_name, root); } + t_pop(); + + quota_next_hook_mail_storage_created = + hook_mail_storage_created; + hook_mail_storage_created = quota_mail_storage_created; } void quota_plugin_deinit(void) diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota-private.h dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-private.h --- dovecot-1.0.rc22/src/plugins/quota/quota-private.h 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-private.h 2007-02-16 22:28:12.508811264 +0200 @@ -9,128 +9,81 @@ extern unsigned int quota_module_id; struct quota { - array_t ARRAY_DEFINE(setups, struct quota_setup *); - char *last_error; -}; - -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 *); + array_t ARRAY_DEFINE(storages, struct mail_storage *); + + int (*test_alloc)(struct quota_transaction_context *ctx, + uoff_t size, bool *too_large_r); - unsigned int user_root:1; + unsigned int debug:1; + unsigned int counting:1; }; struct quota_backend_vfuncs { - struct quota_root *(*init)(struct quota_setup *setup, const char *name); + struct quota_root *(*alloc)(void); + int (*init)(struct quota_root *root, const char *args); 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); + /* called once for each backend */ + void (*storage_added)(struct quota *quota, + struct mail_storage *storage); const char *const *(*get_resources)(struct quota_root *root); + /* the limit is set by default, so it shouldn't normally need to + be changed. */ 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_root_transaction_context * - (*transaction_begin)(struct quota_root *root, - struct quota_transaction_context *ctx, - struct mailbox *box); - int (*transaction_commit)(struct quota_root_transaction_context *ctx); - void (*transaction_rollback) - (struct quota_root_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_root_transaction_context *ctx, - uoff_t size, bool *too_large_r); - int (*test_alloc_bytes)(struct quota_root_transaction_context *ctx, - uoff_t size, bool *too_large_r); - void (*alloc)(struct quota_root_transaction_context *ctx, - struct mail *mail); - void (*free)(struct quota_root_transaction_context *ctx, - struct mail *mail); + uint64_t *value_r, uint64_t *limit); + + int (*update)(struct quota_root *root, + struct quota_transaction_context *ctx); }; struct quota_backend { + /* quota backends equal if backend1.name == backend2.name */ const char *name; struct quota_backend_vfuncs v; }; +struct quota_rule { + char *mailbox_name; + + int64_t bytes_limit, count_limit; +}; + struct quota_root { - struct quota_setup *setup; + pool_t pool; /* Unique quota root name. */ - char *name; + const char *name; - struct quota_backend_vfuncs v; + /* pointer to the quota that owns this root */ + struct quota *quota; + + struct quota_backend backend; + struct quota_rule default_rule; + array_t ARRAY_DEFINE(rules, struct quota_rule); - /* 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_mail_storage *qstorage; - unsigned int idx; }; struct quota_transaction_context { - 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; + struct quota *quota; + struct mailbox *box; - int count_diff; - int64_t bytes_diff; + int64_t bytes_used, count_used; + uint64_t bytes_left, count_left; - uint64_t bytes_limit, count_limit; - uint64_t bytes_current, count_current; + struct mail *tmp_mail; - unsigned int disabled:1; + unsigned int failed:1; }; /* Register storage to all user's quota roots. */ void quota_add_user_storage(struct quota *quota, struct mail_storage *storage); +void quota_remove_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); - -/* default simple implementations for bytes/count updating */ -void -quota_default_transaction_rollback(struct quota_root_transaction_context *ctx); -int quota_default_try_alloc(struct quota_root_transaction_context *ctx, - struct mail *mail, bool *too_large_r); -int quota_default_try_alloc_bytes(struct quota_root_transaction_context *ctx, - uoff_t size, bool *too_large_r); -int quota_default_test_alloc_bytes(struct quota_root_transaction_context *ctx, - uoff_t size, bool *too_large_r); -void quota_default_alloc(struct quota_root_transaction_context *ctx, - struct mail *mail); -void quota_default_free(struct quota_root_transaction_context *ctx, - struct mail *mail); - -int quota_count_storage(struct mail_storage *storage, - uint64_t *bytes_r, uint64_t *count_r); +int quota_count(struct quota *quota, uint64_t *bytes_r, uint64_t *count_r); #endif diff -ruN dovecot-1.0.rc22/src/plugins/quota/quota-storage.c dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-storage.c --- dovecot-1.0.rc22/src/plugins/quota/quota-storage.c 2007-01-25 19:02:51.000000000 +0200 +++ dovecot-1.0.rc22-quota-rewrite/src/plugins/quota/quota-storage.c 2007-02-16 22:28:12.531811145 +0200 @@ -16,10 +16,6 @@ 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 { @@ -58,7 +54,7 @@ struct quota_transaction_context *qt; t = qbox->super.transaction_begin(box, flags); - qt = quota_transaction_begin(box); + qt = quota_transaction_begin(quota_set, box); array_idx_set(&t->module_contexts, quota_storage_module_id, &qt); return t; @@ -76,6 +72,8 @@ return -1; } else { (void)quota_transaction_commit(qt); + if (qt->tmp_mail != NULL) + mail_free(&qt->tmp_mail); return 0; } } @@ -87,6 +85,9 @@ struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx); qbox->super.transaction_rollback(ctx); + + if (qt->tmp_mail != NULL) + mail_free(&qt->tmp_mail); quota_transaction_rollback(qt); } @@ -124,8 +125,8 @@ mail_storage_set_error(t->box->storage, "Quota exceeded"); return -1; } else { - mail_storage_set_error(t->box->storage, "%s", - quota_last_error(quota_set)); + mail_storage_set_error(t->box->storage, + "Internal quota calculation error"); return -1; } } @@ -135,26 +136,25 @@ enum mail_flags flags, struct mail_keywords *keywords, struct mail *dest_mail) { + struct quota_transaction_context *qt = QUOTA_CONTEXT(t); struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box); - struct mail *copy_dest_mail; - int ret; - if (dest_mail != NULL) - copy_dest_mail = dest_mail; - else - copy_dest_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, NULL); + if (dest_mail == NULL) { + /* we always want to know the mail size */ + if (qt->tmp_mail == NULL) { + qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, + NULL); + } + dest_mail = qt->tmp_mail; + } qbox->save_hack = FALSE; - if (qbox->super.copy(t, mail, flags, keywords, copy_dest_mail) < 0) + if (qbox->super.copy(t, mail, flags, keywords, dest_mail) < 0) return -1; /* if copying used saving internally, we already checked the quota and set qbox->save_hack = TRUE. */ - ret = qbox->save_hack ? 0 : quota_check(t, copy_dest_mail); - - if (copy_dest_mail != dest_mail) - mail_free(©_dest_mail); - return ret; + return qbox->save_hack ? 0 : quota_check(t, dest_mail); } static int @@ -183,14 +183,14 @@ full mail. */ bool too_large; - ret = quota_test_alloc_bytes(qt, st->st_size, &too_large); + ret = quota_test_alloc(qt, st->st_size, &too_large); if (ret == 0) { mail_storage_set_error(t->box->storage, "Quota exceeded"); return -1; } else if (ret < 0) { - mail_storage_set_error(t->box->storage, "%s", - quota_last_error(quota_set)); + mail_storage_set_error(t->box->storage, + "Internal quota calculation error"); return -1; } } @@ -298,22 +298,8 @@ 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); - } + + quota_remove_user_storage(quota_set, storage); qstorage->super.destroy(storage); } @@ -331,8 +317,6 @@ storage->v.mailbox_open = quota_mailbox_open; storage->v.mailbox_delete = quota_mailbox_delete; - 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; @@ -346,74 +330,3 @@ quota_add_user_storage(quota_set, 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); -}