quota.c revision de5c7c99783cd86f3bdbc057345cbee923b51a20
7cb128dc4cae2a03a742f63ba7afee23c78e3af0Phil Carmody/* Copyright (C) 2005 Timo Sirainen */
c5d6b453eccc0962eae967abc10e028a740e1256Timo Sirainen unsigned int i;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenunsigned int quota_module_id = 0;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenextern struct quota_backend quota_backend_dict;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenextern struct quota_backend quota_backend_dirsize;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenextern struct quota_backend quota_backend_maildir;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic const struct quota_backend *quota_backends[] = {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen#define QUOTA_CLASS_COUNT (sizeof(quota_backends)/sizeof(quota_backends[0]))
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic int quota_default_test_alloc(struct quota_transaction_context *ctx,
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen root = array_idx_modifiable("a->roots, 0);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstatic const struct quota_backend *quota_backend_find(const char *name)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen unsigned int i;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen if (strcmp(quota_backends[i]->name, name) == 0)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenstruct quota_root *quota_root_init(struct quota *quota, const char *root_def)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen /* <backend>[:<quota root name>[:<backend args>]] */
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen i_fatal("Unknown quota backend: %s", backend_name);
29371e68adc180501454783b44ec8e43b4e6ddc1Timo Sirainen root->pool = pool_alloconly_create("quota root", 512);
29371e68adc180501454783b44ec8e43b4e6ddc1Timo Sirainen /* save root's name */
ce7c2091ca9f19a13c835d1d522832a73f7cfaa0Timo Sirainen root->name = p_strdup_until(root->pool, args, p);
951c92a29c36d22a60e56cae4b47d5d0fa5dd6b5Timo Sirainen array_create(&root->quota_module_contexts, default_pool,
1a878b9d2a823abc6b1c8b1631e50a15d534665fTimo Sirainen sizeof(void *), 5);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenvoid quota_root_deinit(struct quota_root *root)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen unsigned int i, count;
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen roots = array_get(&root->quota->roots, &count);
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen for (i = 0; i < count; i++) {
b50e80d237435686c4ea525643f266731a600981Timo Sirainenstatic struct quota_rule *
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenquota_root_rule_find(struct quota_root *root, const char *name)
8c98b8adba0e70743d5d8c35ae922038881b1f47Timo Sirainen unsigned int i, count;
ad9403d54b5a0f312de6fa22abda6c120988d3deTimo Sirainen rules = array_get_modifiable(&root->rules, &count);
ad9403d54b5a0f312de6fa22abda6c120988d3deTimo Sirainen for (i = 0; i < count; i++) {
b50e80d237435686c4ea525643f266731a600981Timo Sirainenint quota_root_add_rule(struct quota_root *root, const char *rule_def,
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen const char **error_r)
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen const char **args;
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen /* <mailbox name>:<quota limits> */
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen if (strcmp(*args, RULE_NAME_ALL_MAILBOXES) == 0)
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen rule->mailbox_name = p_strdup(root->pool, *args);
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen rule->bytes_limit = strtoll(*args + 8, NULL, 10) * 1024;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen rule->count_limit = strtoll(*args + 9, NULL, 10);
ce7c2091ca9f19a13c835d1d522832a73f7cfaa0Timo Sirainen rule->mailbox_name != NULL ? rule->mailbox_name : "",
ce7c2091ca9f19a13c835d1d522832a73f7cfaa0Timo Sirainenstatic bool quota_root_get_rule_limits(struct quota_root *root,
8a26102b8b1e08a774398980a8f92ae8f8575da8Timo Sirainen /* if default rule limits are 0, this rule applies only to specific
c5d6b453eccc0962eae967abc10e028a740e1256Timo Sirainen rule = quota_root_rule_find(root, mailbox_name);
0bf0c44fce1df29e60f4f5b312ebe862d44aa237Timo Sirainen *bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit;
0bf0c44fce1df29e60f4f5b312ebe862d44aa237Timo Sirainen *count_limit_r = count_limit <= 0 ? 0 : count_limit;
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainenvoid quota_add_user_storage(struct quota *quota, struct mail_storage *storage)
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainen unsigned int i, j, count;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen /* first check if there already exists a storage with the exact same
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainen path. we don't want to count them twice. */
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainen path = mail_storage_get_mailbox_path(storage, "", &is_file);
97dc3902e9bcf2e17b9c249999bffba908231b62Timo Sirainen storages = array_get("a->storages, &count);
8a26102b8b1e08a774398980a8f92ae8f8575da8Timo Sirainen for (i = 0; i < count; i++) {
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainen path2 = mail_storage_get_mailbox_path(storages[i], "",
8a26102b8b1e08a774398980a8f92ae8f8575da8Timo Sirainen if (path2 != NULL && strcmp(path, path2) == 0) {
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainen /* duplicate */
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainen /* @UNSAFE: get different backends into one array */
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainen backends = t_new(struct quota_backend *, count + 1);
de26c21cfadf24c1fa59f06414854d58b3d8baadTimo Sirainen for (i = 0; i < count; i++) {
de26c21cfadf24c1fa59f06414854d58b3d8baadTimo Sirainen if (backends[j]->name == roots[i]->backend.name)
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainenvoid quota_remove_user_storage(struct quota *quota,
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen unsigned int i, count;
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen storages = array_get("a->storages, &count);
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen for (i = 0; i < count; i++) {
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainenquota_root_iter_init(struct quota *quota, struct mailbox *box)
4ee385fb4e4b007d3fa907b7616988006a21e85fTimo Sirainenstruct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
af466fd3ee9d17f2e7b264079d25306c5598b200Timo Sirainen struct quota_root *const *roots, *root = NULL;
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen unsigned int count;
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainen roots = array_get(&iter->quota->roots, &count);
c6afd726060aae56b6622c6c52aec10231c4bf1cTimo Sirainenvoid quota_root_iter_deinit(struct quota_root_iter *iter)
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainenstruct quota_root *quota_root_lookup(struct quota *quota, const char *name)
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen unsigned int i, count;
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen for (i = 0; i < count; i++) {
90de49eb151c2be7655ce6aef5aa3b58295d5c84Timo Sirainenconst char *quota_root_get_name(struct quota_root *root)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenconst char *const *quota_root_get_resources(struct quota_root *root)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenint quota_get_resource(struct quota_root *root, const char *mailbox_name,
feccf3f8679807f25d105521d5f6ddce6df7cdceTimo Sirainen const char *name, uint64_t *value_r, uint64_t *limit_r)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_KILOBYTES) == 0) {
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen (void)quota_root_get_rule_limits(root, mailbox_name,
c5c71245fec4331d6598376f0ff2f3b9d4372cc8Timo Sirainen if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
d30c35e25ea6d935393e031509e6e22422b1e006Timo Sirainen else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen ret = root->backend.v.get_resource(root, name, value_r, limit_r);
1228c0604b8e21e170bba3e2060331599a378110Timo Sirainenint quota_set_resource(struct quota_root *root __attr_unused__,
1228c0604b8e21e170bba3e2060331599a378110Timo Sirainen uint64_t value __attr_unused__, const char **error_r)
1228c0604b8e21e170bba3e2060331599a378110Timo Sirainen /* the quota information comes from userdb (or even config file),
1228c0604b8e21e170bba3e2060331599a378110Timo Sirainen so there's really no way to support this until some major changes
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainenstruct quota_transaction_context *quota_transaction_begin(struct quota *quota,
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainen unsigned int i, count;
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainen ctx = i_new(struct quota_transaction_context, 1);
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainen /* we got here through quota_count_storage() */
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainen /* find the lowest quota limits from all roots and use them */
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainen for (i = 0; i < count; i++) {
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainen ret = quota_get_resource(roots[i], mailbox_name,
b2048c45f6c6ace7d90a2c05f0f3fc6e03e30f38Timo Sirainen } else if (ret < 0) {
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainen ret = quota_get_resource(roots[i], mailbox_name,
f87844c400cf9741abad57d9815121d0738a738fTimo Sirainen } else if (ret < 0) {
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenint quota_transaction_commit(struct quota_transaction_context *ctx)
edcd6f7223568e080d5a6767c5038e3bc891e963Timo Sirainen unsigned int i, count;
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen roots = array_get(&ctx->quota->roots, &count);
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainen for (i = 0; i < count; i++) {
ce7c2091ca9f19a13c835d1d522832a73f7cfaa0Timo Sirainen if (roots[i]->backend.v.update(roots[i], ctx) < 0)
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenvoid quota_transaction_rollback(struct quota_transaction_context *ctx)
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainenint quota_try_alloc(struct quota_transaction_context *ctx,
df452e9628fe8d3356c42dd644b020ea9733c0c1Timo Sirainen ret = quota_test_alloc(ctx, mail_get_physical_size(mail), too_large_r);
fb37a9b7bb71807a394e3ecdb74511f32a79c39bTimo Sirainenint quota_test_alloc(struct quota_transaction_context *ctx,
fb37a9b7bb71807a394e3ecdb74511f32a79c39bTimo Sirainen return ctx->quota->test_alloc(ctx, size, too_large_r);
8e1dbcb9b249c37d00b420705777b103ffa6145dTimo Sirainenstatic int quota_default_test_alloc(struct quota_transaction_context *ctx,
df452e9628fe8d3356c42dd644b020ea9733c0c1Timo Sirainen unsigned int i, count;
fb37a9b7bb71807a394e3ecdb74511f32a79c39bTimo Sirainen if (ctx->count_left != 0 && ctx->bytes_left >= ctx->bytes_used + size)
fb37a9b7bb71807a394e3ecdb74511f32a79c39bTimo Sirainen roots = array_get(&ctx->quota->roots, &count);
fb37a9b7bb71807a394e3ecdb74511f32a79c39bTimo Sirainen for (i = 0; i < count; i++) {
afb49e8adab954708e3f192386a3c7faa07e5ae5Timo Sirainen /* if size is bigger than any limit, then
afb49e8adab954708e3f192386a3c7faa07e5ae5Timo Sirainen it is bigger than the lowest limit */
9fb018dea4e2073639249ea8a14ae27cab2c0aacTimo Sirainenvoid quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
afb49e8adab954708e3f192386a3c7faa07e5ae5Timo Sirainenvoid quota_free(struct quota_transaction_context *ctx, struct mail *mail)