Mercurial > dovecot > original-hg > dovecot-1.2
changeset 4509:e5e79558ac2d HEAD
Added support for multiple quota roots, rules and storages. The configuration
has changed, userdb now needs to return "quota_rule" settings instead. Much
of the code changes were done by Tianyan Liu.
author | Timo Sirainen <timo.sirainen@movial.fi> |
---|---|
date | Sun, 30 Jul 2006 20:58:38 +0300 |
parents | 807217630d80 |
children | 96f0677c2bca |
files | dovecot-example.conf src/plugins/quota/quota-dict.c src/plugins/quota/quota-dirsize.c src/plugins/quota/quota-fs.c src/plugins/quota/quota-maildir.c src/plugins/quota/quota-plugin.c src/plugins/quota/quota-private.h src/plugins/quota/quota-storage.c src/plugins/quota/quota.c src/plugins/quota/quota.h |
diffstat | 10 files changed, 734 insertions(+), 1077 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Mon Jul 24 04:57:15 2006 +0300 +++ b/dovecot-example.conf Sun Jul 30 20:58:38 2006 +0300 @@ -968,6 +968,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::/etc/dovecot-user-quota.conf + # quota2 = dict:domain:%d:/etc/dovecot-domain-quota.conf + # 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
--- a/src/plugins/quota/quota-dict.c Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota-dict.c Sun Jul 30 20:58:38 2006 +0300 @@ -14,113 +14,76 @@ struct dict_quota_root { struct quota_root root; struct dict *dict; - - uint64_t message_bytes_limit; - uint64_t message_count_limit; }; 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; + + root = i_new(struct dict_quota_root, 1); + return &root->root; +} - uri = strchr(setup->data, ' '); - if (uri == NULL) { - i_error("dict quota: URI missing from parameters: %s", - setup->data); - return NULL; +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; + + p = args == NULL ? NULL : strchr(args, ':'); + if (p == NULL) { + i_error("dict quota: URI missing from parameters"); + return -1; } - 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_bytes_limit = strtoull(*args + 9, NULL, 10); - } - t_pop(); + username = t_strdup_until(args, p); + args = p+1; + + if (*username == '\0') + username = getenv("USER"); - 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); - } - - dict = dict_init(uri, getenv("USER")); - if (dict == NULL) - return NULL; + if (getenv("DEBUG") != NULL) + i_info("dict quota: uri = %s", args); - 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; + 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, QUOTA_NAME_MESSAGES, NULL + }; return resources; } 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; 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; t_push(); ret = dict_lookup(root->dict, unsafe_data_stack_pool, DICT_QUOTA_CURRENT_BYTES_PATH, &value); *value_r = ret <= 0 ? 0 : strtoull(value, NULL, 10) / 1024; t_pop(); } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) { - if (root->message_count_limit == (uint64_t)-1) - return 0; - - *limit_r = root->message_count_limit; t_push(); ret = dict_lookup(root->dict, unsafe_data_stack_pool, DICT_QUOTA_CURRENT_COUNT_PATH, &value); @@ -130,101 +93,37 @@ return 0; } - return 1; + return ret; } 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) +dict_quota_update(struct quota_root *_root, + 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_root_transaction_context, 1); - ctx->root = _root; - ctx->ctx = _ctx; - - ctx->bytes_limit = root->message_bytes_limit; - ctx->count_limit = root->message_count_limit; - - t_push(); - if (ctx->bytes_limit != (uint64_t)-1) { - (void)dict_lookup(root->dict, unsafe_data_stack_pool, - DICT_QUOTA_CURRENT_BYTES_PATH, &value); - ctx->bytes_current = value == NULL ? 0 : - strtoull(value, NULL, 10); - } - if (ctx->count_limit != (uint64_t)-1) { - (void)dict_lookup(root->dict, unsafe_data_stack_pool, - DICT_QUOTA_CURRENT_COUNT_PATH, &value); - ctx->count_current = value == NULL ? 0 : - strtoull(value, NULL, 10); - } - t_pop(); - return ctx; -} - -static int -dict_quota_transaction_commit(struct quota_root_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) { - dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH, - ctx->bytes_diff); - } - if (ctx->count_limit != (uint64_t)-1) { - dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH, - ctx->count_diff); - } + dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH, + ctx->bytes_used); + dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH, + 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 } };
--- a/src/plugins/quota/quota-dirsize.c Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota-dirsize.c Sun Jul 30 20:58:38 2006 +0300 @@ -13,60 +13,22 @@ #include <dirent.h> #include <sys/stat.h> -struct dirsize_quota_root { - struct quota_root root; - - uint64_t storage_limit; +struct quota_count_path { + const char *path; + bool is_file; }; +ARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path); 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 * @@ -133,8 +95,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 +109,12 @@ } *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; -}; -ARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path); - static void quota_count_path_add(ARRAY_TYPE(quota_count_path) *paths, const char *path, bool is_file) { @@ -173,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) { @@ -191,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_TYPE(quota_count_path) paths; @@ -203,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); @@ -217,7 +169,7 @@ /* now sum up the found paths */ 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; @@ -225,97 +177,40 @@ } 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) 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) +static int +dirsize_quota_update(struct quota_root *root __attr_unused__, + struct quota_transaction_context *ctx __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) -{ - 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 } };
--- a/src/plugins/quota/quota-fs.c Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota-fs.c Sun Jul 30 20:58:38 2006 +0300 @@ -46,22 +46,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; @@ -89,7 +80,6 @@ if (root->mount != NULL) fs_quota_mountpoint_free(root->mount); - i_free(root->root.name); i_free(root); } @@ -110,43 +100,67 @@ 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 fs_quota_root *root = (struct fs_quota_root *)_root; + 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 == "a_backend_fs) { + 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_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) + mount = fs_quota_mountpoint_get(dir); + 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; - - fs_quota_mountpoint_free(mount); - if (!match) { - /* different mountpoints, can't use this */ - return FALSE; - } - mount = root->mount; - } - - if (getenv("DEBUG") != NULL) { 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); + return; + } + + 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) { mount->path = i_strconcat(mount->mount_path, "/quotas", NULL); @@ -155,13 +169,6 @@ 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 * @@ -200,8 +207,6 @@ 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; } @@ -217,8 +222,6 @@ root->uid, (caddr_t)&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; } @@ -255,32 +258,10 @@ return 1; } -static int -fs_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 * -fs_quota_transaction_begin(struct quota_root *root, - struct quota_transaction_context *ctx) +static int +fs_quota_update(struct quota_root *root __attr_unused__, + struct quota_transaction_context *ctx __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; } @@ -288,26 +269,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 } };
--- a/src/plugins/quota/quota-maildir.c Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota-maildir.c Sun Jul 30 20:58:38 2006 +0300 @@ -22,6 +22,7 @@ struct maildir_quota_root { struct quota_root root; + const char *maildirsize_path; uint64_t message_bytes_limit; uint64_t message_count_limit; @@ -29,6 +30,7 @@ uint64_t total_count; int fd; + time_t recalc_last_stamp; unsigned int master_message_limits:1; }; @@ -56,8 +58,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; @@ -71,8 +73,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; } @@ -111,16 +112,14 @@ *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; @@ -170,8 +169,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; } } @@ -209,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; @@ -226,8 +223,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; } @@ -246,8 +242,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; } @@ -255,38 +250,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(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(); @@ -294,27 +285,58 @@ if (maildir_list_deinit(ctx) < 0) ret = -1; - if (ret == 0) - ret = maildirs_check_have_changed(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) { @@ -397,32 +419,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; 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; @@ -435,8 +451,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; } @@ -467,12 +482,11 @@ return ret; } -static int maildirquota_refresh(struct maildir_quota_root *root, - struct mail_storage *storage) +static int maildirquota_refresh(struct maildir_quota_root *root) { int ret; - ret = maildirsize_read(root, storage); + ret = maildirsize_read(root); if (ret == 0) { if (root->message_bytes_limit == (uint64_t)-1 && root->message_count_limit == (uint64_t)-1) { @@ -480,13 +494,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; @@ -508,47 +521,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 != '\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; - } - } - t_pop(); - return &root->root; } @@ -556,28 +544,45 @@ { struct maildir_quota_root *root = (struct maildir_quota_root *)_root; - i_free(root->root.name); i_free(root); } -static bool -maildir_quota_add_storage(struct quota_root *root __attr_unused__, - struct mail_storage *_storage) +static void +maildir_quota_root_storage_added(struct quota_root *_root, + struct mail_storage *storage) { - if (strcmp(_storage->name, "maildir") == 0) { - struct maildir_storage *storage = - (struct maildir_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_modifiable("a->roots, &count); + for (i = 0; i < count; i++) { + if (roots[i]->backend == "a_backend_maildir) + 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 * @@ -592,120 +597,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; + if (strcmp(name, QUOTA_NAME_STORAGE) == 0) *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; + 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 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 (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 } };
--- a/src/plugins/quota/quota-plugin.c Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota-plugin.c Sun Jul 30 20:58:38 2006 +0300 @@ -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; +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 = quota_init(); - /* Currently we support only one quota setup */ - (void)quota_setup_init(quota, env, TRUE); + if (env == NULL) + return; + + quota = quota_init(); + + root = quota_root_init(quota, 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; - quota_next_hook_mail_storage_created = - hook_mail_storage_created; - hook_mail_storage_created = quota_mail_storage_created; + root_name = t_strdup_printf("QUOTA%d", i); + env = getenv(root_name); + + if (env == NULL) + break; + + root = quota_root_init(quota, 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)
--- a/src/plugins/quota/quota-private.h Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota-private.h Sun Jul 30 20:58:38 2006 +0300 @@ -9,54 +9,27 @@ extern unsigned int quota_module_id; struct quota { - 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_DEFINE(roots, struct quota_root *); - - unsigned int user_root:1; + ARRAY_DEFINE(storages, struct mail_storage *); }; 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); + uint64_t *value_r, uint64_t *limit); - 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_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); + int (*update)(struct quota_root *root, + struct quota_transaction_context *ctx); }; struct quota_backend { @@ -64,72 +37,46 @@ 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; - struct quota_backend_vfuncs v; + /* pointer to the quota that owns this root */ + struct quota *quota; - /* Mail storages using this quota root. */ - ARRAY_DEFINE(storages, struct mail_storage *); + struct quota_backend *backend; + struct quota_rule default_rule; + ARRAY_DEFINE(rules, struct quota_rule); + /* Module-specific contexts. See quota_module_id. */ 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 { struct quota *quota; - - ARRAY_DEFINE(root_transactions, - struct quota_root_transaction_context *); - struct mail *mail; -}; + struct mailbox *box; -struct quota_root_transaction_context { - struct quota_root *root; - struct quota_transaction_context *ctx; + int64_t bytes_used, count_used; + uint64_t bytes_left, count_left; - int count_diff; - int64_t bytes_diff; + struct mail *tmp_mail; - uint64_t bytes_limit, count_limit; - uint64_t bytes_current, count_current; - - 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); - -/* 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_remove_user_storage(struct quota *quota, + struct mail_storage *storage); 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); - #endif
--- a/src/plugins/quota/quota-storage.c Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota-storage.c Sun Jul 30 20:58:38 2006 +0300 @@ -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_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, box); array_idx_set(&t->module_contexts, quota_storage_module_id, &qt); return t; @@ -76,8 +72,8 @@ return -1; } else { (void)quota_transaction_commit(qt); - if (qt->mail != NULL) - mail_free(&qt->mail); + if (qt->tmp_mail != NULL) + mail_free(&qt->tmp_mail); return 0; } } @@ -90,8 +86,8 @@ qbox->super.transaction_rollback(ctx); - if (qt->mail != NULL) - mail_free(&qt->mail); + if (qt->tmp_mail != NULL) + mail_free(&qt->tmp_mail); quota_transaction_rollback(qt); } @@ -129,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)); + mail_storage_set_error(t->box->storage, + "Internal quota calculation error"); return -1; } } @@ -145,11 +141,11 @@ if (dest_mail == NULL) { /* we always want to know the mail size */ - if (qt->mail == NULL) { - qt->mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, - NULL); + if (qt->tmp_mail == NULL) { + qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, + NULL); } - dest_mail = qt->mail; + dest_mail = qt->tmp_mail; } qbox->save_hack = FALSE; @@ -186,25 +182,25 @@ 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)); + mail_storage_set_error(t->box->storage, + "Internal quota calculation error"); return -1; } } if (dest_mail == NULL) { /* we always want to know the mail size */ - if (qt->mail == NULL) { - qt->mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, - NULL); + if (qt->tmp_mail == NULL) { + qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, + NULL); } - dest_mail = qt->mail; + dest_mail = qt->tmp_mail; } return qbox->super.save_init(t, flags, keywords, received_date, @@ -214,13 +210,15 @@ static int quota_save_finish(struct mail_save_context *ctx) { + struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx->transaction); struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box); if (qbox->super.save_finish(ctx) < 0) return -1; qbox->save_hack = TRUE; - return quota_check(ctx->transaction, ctx->dest_mail); + return quota_check(ctx->transaction, ctx->dest_mail != NULL ? + ctx->dest_mail : qt->tmp_mail); } static struct mailbox * @@ -295,22 +293,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, storage); qstorage->super.destroy(storage); } @@ -328,8 +312,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; @@ -343,74 +325,3 @@ quota_add_user_storage(quota, storage); } } - -bool quota_mail_storage_add_root(struct mail_storage *storage, - struct quota_root *root) -{ - struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage); - - if (!root->v.add_storage(root, storage)) - return FALSE; - - array_append(&root->storages, &storage, 1); - array_append(&qstorage->roots, &root, 1); - return TRUE; -} - -void quota_mail_storage_remove_root(struct mail_storage *storage, - struct quota_root *root) -{ - struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage); - struct mail_storage *const *storages; - struct quota_root *const *roots; - unsigned int i, count; - - storages = array_get(&root->storages, &count); - for (i = 0; i < count; i++) { - if (storages[i] == storage) { - array_delete(&root->storages, i, 1); - break; - } - } - i_assert(i != count); - - roots = array_get(&qstorage->roots, &count); - for (i = 0; i < count; i++) { - if (roots[i] == root) { - array_delete(&qstorage->roots, i, 1); - break; - } - } - i_assert(i != count); - - root->v.remove_storage(root, storage); -} - -struct quota_root_iter *quota_root_iter_init(struct mailbox *box) -{ - struct quota_mail_storage *qstorage = QUOTA_CONTEXT(box->storage); - struct quota_root_iter *iter; - - iter = i_new(struct quota_root_iter, 1); - iter->qstorage = qstorage; - return iter; -} - -struct quota_root *quota_root_iter_next(struct quota_root_iter *iter) -{ - struct quota_root *const *roots; - unsigned int count; - - roots = array_get(&iter->qstorage->roots, &count); - i_assert(iter->idx <= count); - - if (iter->idx >= count) - return NULL; - - return roots[iter->idx++]; -} - -void quota_root_iter_deinit(struct quota_root_iter *iter) -{ - i_free(iter); -}
--- a/src/plugins/quota/quota.c Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota.c Sun Jul 30 20:58:38 2006 +0300 @@ -5,6 +5,16 @@ #include "hash.h" #include "quota-private.h" #include "quota-fs.h" +#include <stdlib.h> + +#define RULE_NAME_ALL_MAILBOXES "*" + +struct quota_root_iter { + struct quota *quota; + struct mailbox *box; + + unsigned int i; +}; unsigned int quota_module_id = 0; @@ -30,98 +40,92 @@ struct quota *quota; quota = i_new(struct quota, 1); - ARRAY_CREATE("a->setups, default_pool, struct quota_setup *, 4); + ARRAY_CREATE("a->roots, default_pool, struct quota_root *, 4); + ARRAY_CREATE("a->storages, default_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_modifiable("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 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); - - t_push(); - p = strchr(setup->data, ':'); - if (p == NULL) { - backend_name = setup->data; - data = ""; - } else { - backend_name = t_strdup_until(setup->data, p); - data = p+1; - } for (i = 0; i < QUOTA_CLASS_COUNT; i++) { - if (strcmp(quota_backends[i]->name, backend_name) == 0) { - setup->backend = quota_backends[i]; - break; - } + if (strcmp(quota_backends[i]->name, name) == 0) + return quota_backends[i]; } - if (setup->backend == NULL) - i_fatal("Unknown quota backend: %s", backend_name); - - t_pop(); - - array_append("a->setups, &setup, 1); - return setup; + return NULL; } -void quota_setup_deinit(struct quota_setup *setup) +struct quota_root *quota_root_init(struct quota *quota, const char *root_def) { - struct quota_setup *const *setups; - unsigned int i, count; + struct quota_root *root; + struct quota_backend *backend; + const char *p, *args, *backend_name; + + t_push(); + + /* <backend>[:<quota root name>[:<backend args>]] */ + p = strchr(root_def, ':'); + if (p == NULL) { + backend_name = root_def; + args = NULL; + } else { + backend_name = t_strdup_until(root_def, p); + args = p + 1; + } + + backend = quota_backend_find(backend_name); + if (backend == NULL) + i_fatal("Unknown quota backend: %s", backend_name); + + t_pop(); - 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; + 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 = ""; + } + + ARRAY_CREATE(&root->rules, default_pool, struct quota_rule, 4); + ARRAY_CREATE(&root->quota_module_contexts, default_pool, void *, 5); + + array_append("a->roots, &root, 1); + + if (backend->v.init != NULL) { + if (backend->v.init(root, args) < 0) { + quota_root_deinit(root); + return NULL; } } - i_assert(i != count); - - while (array_count(&setup->roots) > 0) { - struct quota_root *const *root; - - root = array_idx(&setup->roots, 0); - quota_root_deinit(*root); - } - - array_free(&setup->roots); - i_free(setup->data); - i_free(setup); -} - -struct quota_root * -quota_root_init(struct quota_setup *setup, const char *name) -{ - struct quota_root *root; - - root = setup->backend->v.init(setup, name); - root->setup = setup; - ARRAY_CREATE(&root->storages, default_pool, struct mail_storage *, 8); - array_create(&root->quota_module_contexts, - default_pool, sizeof(void *), 5); - array_append(&setup->roots, &root, 1); if (hook_quota_root_created != NULL) hook_quota_root_created(root); @@ -130,75 +134,202 @@ void quota_root_deinit(struct quota_root *root) { - /* make a copy, since root is freed */ - struct array module_contexts = root->quota_module_contexts.arr; - struct mail_storage *const *storage_p; + pool_t pool = root->pool; 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); + roots = array_get(&root->quota->roots, &count); + for (i = 0; i < count; i++) { + if (roots[i] == root) + array_delete(&root->quota->roots, i, 1); } - /* remove from setup */ - roots = array_get(&root->setup->roots, &count); + array_free(&root->rules); + array_free(&root->quota_module_contexts); + + root->backend->v.deinit(root); + pool_unref(pool); +} + +static struct quota_rule * +quota_root_rule_find(struct quota_root *root, const char *name) +{ + struct quota_rule *rules; + unsigned int i, count; + + rules = array_get_modifiable(&root->rules, &count); for (i = 0; i < count; i++) { - if (roots[i] == root) { - array_delete(&root->setup->roots, i, 1); + if (strcmp(rules[i].mailbox_name, name) == 0) + return &rules[i]; + } + return NULL; +} + +int quota_root_add_rule(struct quota_root *root, const char *rule_def, + const char **error_r) +{ + struct quota_rule *rule; + const char **args; + int ret = 0; + + if (*rule_def == '\0') { + *error_r = "Empty rule"; + return -1; + } + + /* <mailbox name>:<quota limits> */ + t_push(); + args = t_strsplit(rule_def, ":"); + + 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); + } + } + + 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); + 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; - array_free(&root->storages); - root->v.deinit(root); - _array_free(&module_contexts); + 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 quota_backend **backends; + unsigned int i, j, count; + + array_append("a->storages, &storage, 1); + + 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] == roots[i]->backend) + break; + } + if (backends[j] == NULL) + backends[j] = roots[i]->backend; + } + + for (i = 0; backends[i] != NULL; i++) { + if (backends[i]->v.storage_added != NULL) + backends[i]->v.storage_added(quota, storage); + } +} - 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; +void quota_remove_user_storage(struct quota *quota, + struct mail_storage *storage) +{ + 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; + } + } +} + +struct quota_root_iter * +quota_root_iter_init(struct quota *quota, struct mailbox *box) +{ + struct quota_root_iter *iter; - if (quota_mail_storage_add_root(storage, roots[j])) - found = TRUE; + iter = i_new(struct quota_root_iter, 1); + iter->quota = quota; + iter->box = box; + return iter; +} + +struct quota_root *quota_root_iter_next(struct quota_root_iter *iter) +{ + struct quota_root *const *roots, *root = NULL; + unsigned int count; + uint64_t value, limit; + int ret; + + 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, &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; } } - if (!found && setup_count > 0) { - /* create a new quota root for the storage */ - struct quota_root *root; + iter->i++; + return root; +} - root = quota_root_init(setups[0], ""); // FIXME: name? - found = quota_mail_storage_add_root(storage, root); - i_assert(found); - } +void quota_root_iter_deinit(struct quota_root_iter *iter) +{ + i_free(iter); } struct quota_root *quota_root_lookup(struct quota *quota, const char *name) { - struct quota_setup *const *setups; struct quota_root *const *roots; - unsigned int i, j, setup_count, root_count; + unsigned int i, 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]; - } + roots = array_get("a->roots, &count); + for (i = 0; i < count; i++) { + if (strcmp(roots[i]->name, name) == 0) + return roots[i]; } return NULL; } @@ -210,261 +341,175 @@ const char *const *quota_root_get_resources(struct quota_root *root) { - return root->v.get_resources(root); + return root->backend->v.get_resources(root); } -int quota_get_resource(struct quota_root *root, const char *name, - uint64_t *value_r, uint64_t *limit_r) +int quota_get_resource(struct quota_root *root, const char *mailbox_name, + const char *name, uint64_t *value_r, uint64_t *limit_r) { - return root->v.get_resource(root, name, value_r, limit_r); + uint64_t bytes_limit, count_limit; + int ret; + + (void)quota_root_get_rule_limits(root, mailbox_name, + &bytes_limit, &count_limit); + if (strcmp(name, QUOTA_NAME_STORAGE) == 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); + return ret <= 0 ? ret : + (*limit_r == 0 ? 0 : 1); } -int quota_set_resource(struct quota_root *root, - const char *name, uint64_t value) +int quota_set_resource(struct quota_root *root __attr_unused__, + const char *name __attr_unused__, + uint64_t value __attr_unused__, const char **error_r) { - return root->v.set_resource(root, name, value); + /* 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; } -struct quota_transaction_context *quota_transaction_begin(struct mailbox *box) +struct quota_transaction_context *quota_transaction_begin(struct quota *quota, + struct mailbox *box) { struct quota_transaction_context *ctx; - struct quota_root_transaction_context *root_ctx; - struct quota_root_iter *iter; - struct quota_root *root; + struct quota_root *const *roots; + const char *mailbox_name; + unsigned int i, count; + uint64_t current, limit, left; + int ret; + mailbox_name = mailbox_get_name(box); + ctx = i_new(struct quota_transaction_context, 1); - ARRAY_CREATE(&ctx->root_transactions, default_pool, - struct quota_root_transaction_context *, 4); + ctx->quota = quota; + ctx->box = box; + ctx->bytes_left = (uint64_t)-1; + ctx->count_left = (uint64_t) -1; - 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); + /* find the lowest quota limits from all roots and use them */ + roots = array_get("a->roots, &count); + for (i = 0; i < count; i++) { + ret = quota_get_resource(roots[i], mailbox_name, + QUOTA_NAME_STORAGE, ¤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; + } } - 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) { - 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]; - - if (t->root->v.transaction_commit(t) < 0) - ret = -1; + 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; + } } - quota_transaction_free(ctx); + i_free(ctx); return ret; } void quota_transaction_rollback(struct quota_transaction_context *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); + i_free(ctx); } int quota_try_alloc(struct quota_transaction_context *ctx, struct mail *mail, bool *too_large_r) { - struct quota_root_transaction_context *const *root_transactions; - unsigned int i, count; - int ret = 1; + int ret; - root_transactions = array_get(&ctx->root_transactions, &count); - for (i = 0; i < count; i++) { - struct quota_root_transaction_context *t = - root_transactions[i]; + ret = quota_test_alloc(ctx, mail_get_physical_size(mail), too_large_r); + if (ret <= 0) + return ret; - ret = t->root->v.try_alloc(t, mail, too_large_r); - if (ret <= 0) - break; - } - return ret; + quota_alloc(ctx, mail); + return 1; } -int quota_try_alloc_bytes(struct quota_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) { - struct quota_root_transaction_context *const *root_transactions; + struct quota_root *const *roots; unsigned int i, count; - int ret = 1; + + *too_large_r = FALSE; + + if (ctx->failed) + return -1; + if (ctx->count_left != 0 && ctx->bytes_left >= ctx->bytes_used + size) + return 1; - root_transactions = array_get(&ctx->root_transactions, &count); + roots = array_get(&ctx->quota->roots, &count); for (i = 0; i < count; i++) { - struct quota_root_transaction_context *t = - root_transactions[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; - ret = t->root->v.try_alloc_bytes(t, size, too_large_r); - if (ret <= 0) + /* 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 ret; -} -int quota_test_alloc_bytes(struct quota_transaction_context *ctx, - uoff_t size, bool *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.test_alloc_bytes(t, size, too_large_r); - if (ret <= 0) - break; - } - return ret; + return 0; } void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail) { - struct quota_root_transaction_context *const *root_transactions; - unsigned int i, count; + uoff_t size; - root_transactions = array_get(&ctx->root_transactions, &count); - for (i = 0; i < count; i++) { - struct quota_root_transaction_context *t = - root_transactions[i]; + size = mail_get_physical_size(mail); + if (size != (uoff_t)-1) + ctx->bytes_used += size; - t->root->v.alloc(t, mail); - } + ctx->count_used++; } void quota_free(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); - 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 != 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); -} - -void -quota_default_transaction_rollback(struct quota_root_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 ret; - - ret = quota_default_test_alloc_bytes(ctx, size, too_large_r); - if (ret <= 0 || ctx->disabled) - return ret; - - ctx->count_diff++; - ctx->bytes_diff += size; - return 1; -} - -int quota_default_test_alloc_bytes(struct quota_root_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; -} - -int quota_default_try_alloc(struct quota_root_transaction_context *ctx, - struct mail *mail, bool *too_large_r) -{ uoff_t size; - if (ctx->disabled) - return 1; - - 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"); - return -1; - } - - return quota_default_try_alloc_bytes(ctx, size, too_large_r); -} - -void quota_default_alloc(struct quota_root_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++; -} - -void quota_default_free(struct quota_root_transaction_context *ctx, - struct mail *mail) -{ - uoff_t size; + ctx->bytes_used -= size; - if (ctx->disabled) - return; - - size = mail_get_physical_size(mail); - if (size != (uoff_t)-1) - ctx->bytes_diff -= size; - ctx->count_diff--; + ctx->count_used--; }
--- a/src/plugins/quota/quota.h Mon Jul 24 04:57:15 2006 +0300 +++ b/src/plugins/quota/quota.h Sun Jul 30 20:58:38 2006 +0300 @@ -19,20 +19,17 @@ 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