quota.c revision 26a8b7deb3a5b6f26f9c4d71538e1248f680e4be
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (C) 2005 Timo Sirainen */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "lib.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "array.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "hash.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "quota-private.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "quota-fs.h"
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila#include <stdlib.h>
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#define RULE_NAME_ALL_MAILBOXES "*"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenstruct quota_root_iter {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct quota *quota;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct mailbox *box;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen unsigned int i;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen};
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
60ba197d17023594231d9805d889817782e41859Timo Sirainenunsigned int quota_module_id = 0;
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenextern struct quota_backend quota_backend_dict;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenextern struct quota_backend quota_backend_dirsize;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenextern struct quota_backend quota_backend_fs;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenextern struct quota_backend quota_backend_maildir;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainenstatic const struct quota_backend *quota_backends[] = {
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen#ifdef HAVE_FS_QUOTA
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen &quota_backend_fs,
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen#endif
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen &quota_backend_dict,
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen &quota_backend_dirsize,
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen &quota_backend_maildir
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen};
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen#define QUOTA_CLASS_COUNT (sizeof(quota_backends)/sizeof(quota_backends[0]))
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainenstatic int quota_default_test_alloc(struct quota_transaction_context *ctx,
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen uoff_t size, bool *too_large_r);
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainenstruct quota *quota_init(void)
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen{
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen struct quota *quota;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen quota = i_new(struct quota, 1);
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen quota->test_alloc = quota_default_test_alloc;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen ARRAY_CREATE(&quota->roots, default_pool, 4);
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen ARRAY_CREATE(&quota->storages, default_pool, 8);
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen return quota;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen}
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainenvoid quota_deinit(struct quota *quota)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen struct quota_root **root;
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen while (array_count(&quota->roots) > 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen root = array_idx_modifiable(&quota->roots, 0);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen quota_root_deinit(*root);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen array_free(&quota->roots);
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen array_free(&quota->storages);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_free(quota);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenstatic const struct quota_backend *quota_backend_find(const char *name)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen unsigned int i;
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (strcmp(quota_backends[i]->name, name) == 0)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return quota_backends[i];
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen }
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return NULL;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainenstruct quota_root *quota_root_init(struct quota *quota, const char *root_def)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct quota_root *root;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen const struct quota_backend *backend;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen const char *p, *args, *backend_name;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen t_push();
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* <backend>[:<quota root name>[:<backend args>]] */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen p = strchr(root_def, ':');
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (p == NULL) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen backend_name = root_def;
4c61d6b6ad6173f06563c2cee9bd813c59277dd2Timo Sirainen args = NULL;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen } else {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen backend_name = t_strdup_until(root_def, p);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen args = p + 1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
57dc9669d34de7d08a44be9d7d3c9f6a0c34cc87Timo Sirainen
57dc9669d34de7d08a44be9d7d3c9f6a0c34cc87Timo Sirainen backend = quota_backend_find(backend_name);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (backend == NULL)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_fatal("Unknown quota backend: %s", backend_name);
57dc9669d34de7d08a44be9d7d3c9f6a0c34cc87Timo Sirainen
57dc9669d34de7d08a44be9d7d3c9f6a0c34cc87Timo Sirainen t_pop();
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen root = backend->v.alloc();
5455a917515f774fcaad35558f149536b796b531Teemu Huovila root->quota = quota;
5455a917515f774fcaad35558f149536b796b531Teemu Huovila root->backend = *backend;
5455a917515f774fcaad35558f149536b796b531Teemu Huovila root->pool = pool_alloconly_create("quota root", 512);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (args != NULL) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* save root's name */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen p = strchr(args, ':');
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (p == NULL) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen root->name = p_strdup(root->pool, args);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen args = NULL;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen } else {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen root->name = p_strdup_until(root->pool, args, p);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen args = p + 1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen } else {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen root->name = "";
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
4c61d6b6ad6173f06563c2cee9bd813c59277dd2Timo Sirainen
fe0dff63d6b53d22ae16ac53ab183e9355a64a32Timo Sirainen ARRAY_CREATE(&root->rules, default_pool, 4);
4c61d6b6ad6173f06563c2cee9bd813c59277dd2Timo Sirainen array_create(&root->quota_module_contexts, default_pool,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen sizeof(void *), 5);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
fe0dff63d6b53d22ae16ac53ab183e9355a64a32Timo Sirainen array_append(&quota->roots, &root, 1);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (backend->v.init != NULL) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (backend->v.init(root, args) < 0) {
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen quota_root_deinit(root);
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen return NULL;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
fac865bad1ba10e85d80b63dedfd3493a65510d4Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return root;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenvoid quota_root_deinit(struct quota_root *root)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen pool_t pool = root->pool;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct quota_root *const *roots;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen unsigned int i, count;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen roots = array_get(&root->quota->roots, &count);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen for (i = 0; i < count; i++) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (roots[i] == root)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen array_delete(&root->quota->roots, i, 1);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen array_free(&root->rules);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila array_free(&root->quota_module_contexts);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen root->backend.v.deinit(root);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila pool_unref(pool);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila}
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovilastatic struct quota_rule *
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovilaquota_root_rule_find(struct quota_root *root, const char *name)
957b0b4c9aeff7153bb9ebf91d8aea550bd07865Teemu Huovila{
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen struct quota_rule *rules;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila unsigned int i, count;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila rules = array_get_modifiable(&root->rules, &count);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen for (i = 0; i < count; i++) {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila if (strcmp(rules[i].mailbox_name, name) == 0)
5455a917515f774fcaad35558f149536b796b531Teemu Huovila return &rules[i];
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen }
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen return NULL;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen}
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainenint quota_root_add_rule(struct quota_root *root, const char *rule_def,
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen const char **error_r)
5455a917515f774fcaad35558f149536b796b531Teemu Huovila{
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct quota_rule *rule;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila const char **args;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila int ret = 0;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila if (*rule_def == '\0') {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila *error_r = "Empty rule";
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila return -1;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila /* <mailbox name>:<quota limits> */
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila t_push();
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila args = t_strsplit(rule_def, ":");
957b0b4c9aeff7153bb9ebf91d8aea550bd07865Teemu Huovila
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen rule = quota_root_rule_find(root, *args);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila if (rule == NULL) {
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (strcmp(*args, RULE_NAME_ALL_MAILBOXES) == 0)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen rule = &root->default_rule;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen else {
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen rule = array_append_space(&root->rules);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila rule->mailbox_name = p_strdup(root->pool, *args);
0c827d2094e80ede4c089fc00260d7ffcc764636Timo Sirainen }
0c827d2094e80ede4c089fc00260d7ffcc764636Timo Sirainen }
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen for (args++; *args != NULL; args++) {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila if (strncmp(*args, "storage=", 8) == 0)
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen rule->bytes_limit = strtoll(*args + 8, NULL, 10) * 1024;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen else if (strncmp(*args, "messages=", 9) == 0)
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen rule->count_limit = strtoll(*args + 9, NULL, 10);
fac865bad1ba10e85d80b63dedfd3493a65510d4Timo Sirainen else {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila *error_r = p_strdup_printf(root->pool,
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila "Invalid rule limit: %s", *args);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila ret = -1;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila break;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila t_pop();
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila return ret;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila}
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovilastatic bool quota_root_get_rule_limits(struct quota_root *root,
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila const char *mailbox_name,
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila uint64_t *bytes_limit_r,
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila uint64_t *count_limit_r)
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila{
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct quota_rule *rule;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen int64_t bytes_limit, count_limit;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen bool found;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen bytes_limit = root->default_rule.bytes_limit;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila count_limit = root->default_rule.count_limit;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen /* if default rule limits are 0, this rule applies only to specific
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila mailboxes */
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila found = bytes_limit != 0 || count_limit != 0;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen rule = quota_root_rule_find(root, mailbox_name);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (rule != NULL) {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila bytes_limit += rule->bytes_limit;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila count_limit += rule->count_limit;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila found = TRUE;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila *bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit;
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila *count_limit_r = count_limit <= 0 ? 0 : count_limit;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return found;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovilavoid quota_add_user_storage(struct quota *quota, struct mail_storage *storage)
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct quota_root *const *roots;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct quota_backend **backends;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen unsigned int i, j, count;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen array_append(&quota->storages, &storage, 1);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen roots = array_get(&quota->roots, &count);
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila /* @UNSAFE: get different backends into one array */
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila backends = t_new(struct quota_backend *, count + 1);
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila for (i = 0; i < count; i++) {
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila for (j = 0; backends[j] != NULL; j++) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (backends[j]->name == roots[i]->backend.name)
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila break;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (backends[j] == NULL)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen backends[j] = &roots[i]->backend;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen for (i = 0; backends[i] != NULL; i++) {
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen if (backends[i]->v.storage_added != NULL)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen backends[i]->v.storage_added(quota, storage);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenvoid quota_remove_user_storage(struct quota *quota,
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila struct mail_storage *storage)
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila{
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila struct mail_storage *const *storages;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen unsigned int i, count;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen storages = array_get(&quota->storages, &count);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen for (i = 0; i < count; i++) {
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila if (storages[i] == storage) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen array_delete(&quota->storages, i, 1);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen break;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainenstruct quota_root_iter *
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainenquota_root_iter_init(struct quota *quota, struct mailbox *box)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen{
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen struct quota_root_iter *iter;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen iter = i_new(struct quota_root_iter, 1);
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen iter->quota = quota;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen iter->box = box;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen return iter;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen}
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainenstruct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen{
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen struct quota_root *const *roots, *root = NULL;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen unsigned int count;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen uint64_t value, limit;
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen int ret;
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen roots = array_get(&iter->quota->roots, &count);
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen if (iter->i >= count)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen return NULL;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen for (; iter->i < count; iter->i++) {
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen ret = quota_get_resource(roots[iter->i], "",
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen QUOTA_NAME_STORAGE, &value, &limit);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (ret == 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen ret = quota_get_resource(roots[iter->i], "",
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen QUOTA_NAME_MESSAGES,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen &value, &limit);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (ret > 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen root = roots[iter->i];
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen break;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen iter->i++;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return root;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainenvoid quota_root_iter_deinit(struct quota_root_iter *iter)
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen{
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen i_free(iter);
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen}
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainenstruct quota_root *quota_root_lookup(struct quota *quota, const char *name)
ebcd7cf40e53c2bbc98f7f686e206cda5c0e3111Timo Sirainen{
60ba197d17023594231d9805d889817782e41859Timo Sirainen struct quota_root *const *roots;
60ba197d17023594231d9805d889817782e41859Timo Sirainen unsigned int i, count;
60ba197d17023594231d9805d889817782e41859Timo Sirainen
60ba197d17023594231d9805d889817782e41859Timo Sirainen roots = array_get(&quota->roots, &count);
60ba197d17023594231d9805d889817782e41859Timo Sirainen for (i = 0; i < count; i++) {
60ba197d17023594231d9805d889817782e41859Timo Sirainen if (strcmp(roots[i]->name, name) == 0)
60ba197d17023594231d9805d889817782e41859Timo Sirainen return roots[i];
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen }
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen return NULL;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen}
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainenconst char *quota_root_get_name(struct quota_root *root)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen{
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen return root->name;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen}
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainenconst char *const *quota_root_get_resources(struct quota_root *root)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return root->backend.v.get_resources(root);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenint quota_get_resource(struct quota_root *root, const char *mailbox_name,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen const char *name, uint64_t *value_r, uint64_t *limit_r)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen uint64_t bytes_limit, count_limit;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen int ret;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen (void)quota_root_get_rule_limits(root, mailbox_name,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen &bytes_limit, &count_limit);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE) == 0)
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen *limit_r = bytes_limit;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen *limit_r = count_limit;
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen else
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen *limit_r = 0;
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen ret = root->backend.v.get_resource(root, name, value_r, limit_r);
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen return ret <= 0 ? ret :
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen (*limit_r == 0 ? 0 : 1);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainenint quota_set_resource(struct quota_root *root __attr_unused__,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen const char *name __attr_unused__,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen uint64_t value __attr_unused__, const char **error_r)
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen{
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen /* the quota information comes from userdb (or even config file),
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen so there's really no way to support this until some major changes
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen are done */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen *error_r = MAIL_STORAGE_ERR_NO_PERMISSION;
dbf26a3ea43cd79fe88f01ec99c7d9440679b996Timo Sirainen return -1;
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila}
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovilastruct quota_transaction_context *quota_transaction_begin(struct quota *quota,
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct mailbox *box)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen struct quota_transaction_context *ctx;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct quota_root *const *roots;
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen const char *mailbox_name;
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen unsigned int i, count;
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen uint64_t current, limit, left;
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen int ret;
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen mailbox_name = mailbox_get_name(box);
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen ctx = i_new(struct quota_transaction_context, 1);
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen ctx->quota = quota;
6634e45500e81cfa36932203ee69c81745efd3e6Timo Sirainen ctx->box = box;
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen ctx->bytes_left = (uint64_t)-1;
ctx->count_left = (uint64_t) -1;
/* find the lowest quota limits from all roots and use them */
roots = array_get(&quota->roots, &count);
for (i = 0; i < count; i++) {
ret = quota_get_resource(roots[i], mailbox_name,
QUOTA_NAME_STORAGE, &current, &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, &current, &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;
}
int quota_transaction_commit(struct quota_transaction_context *ctx)
{
struct quota_root *const *roots;
unsigned int i, count;
int ret = 0;
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;
}
}
i_free(ctx);
return ret;
}
void quota_transaction_rollback(struct quota_transaction_context *ctx)
{
i_free(ctx);
}
int quota_try_alloc(struct quota_transaction_context *ctx,
struct mail *mail, bool *too_large_r)
{
int ret;
ret = quota_test_alloc(ctx, mail_get_physical_size(mail), too_large_r);
if (ret <= 0)
return ret;
quota_alloc(ctx, mail);
return 1;
}
int quota_test_alloc(struct quota_transaction_context *ctx,
uoff_t size, bool *too_large_r)
{
return ctx->quota->test_alloc(ctx, size, too_large_r);
}
static int quota_default_test_alloc(struct quota_transaction_context *ctx,
uoff_t size, bool *too_large_r)
{
struct quota_root *const *roots;
unsigned int i, count;
*too_large_r = FALSE;
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 0;
}
void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
{
uoff_t size;
size = mail_get_physical_size(mail);
if (size != (uoff_t)-1)
ctx->bytes_used += size;
ctx->count_used++;
}
void quota_free(struct quota_transaction_context *ctx, struct mail *mail)
{
uoff_t size;
size = mail_get_physical_size(mail);
if (size != (uoff_t)-1)
ctx->bytes_used -= size;
ctx->count_used--;
}