Mercurial > dovecot > original-hg > dovecot-1.2
changeset 7294:9e4c26471b19 HEAD
Relative (%) limits in quota rules and warnings work now if the backend
specifies or changes the limits (e.g. Maildir++ maildirsize file)
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 26 Feb 2008 16:21:05 +0200 |
parents | f78b83bf16b7 |
children | a39b16b0a530 |
files | 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-private.h src/plugins/quota/quota.c |
diffstat | 6 files changed, 144 insertions(+), 94 deletions(-) [+] |
line wrap: on
line diff
--- a/src/plugins/quota/quota-dict.c Tue Feb 26 15:11:34 2008 +0200 +++ b/src/plugins/quota/quota-dict.c Tue Feb 26 16:21:05 2008 +0200 @@ -96,7 +96,7 @@ static int dict_quota_get_resource(struct quota_root *_root, const char *name, - uint64_t *value_r, uint64_t *limit ATTR_UNUSED) + uint64_t *value_r) { struct dict_quota_root *root = (struct dict_quota_root *)_root; bool want_bytes;
--- a/src/plugins/quota/quota-dirsize.c Tue Feb 26 15:11:34 2008 +0200 +++ b/src/plugins/quota/quota-dirsize.c Tue Feb 26 16:21:05 2008 +0200 @@ -180,7 +180,7 @@ static int dirsize_quota_get_resource(struct quota_root *_root, const char *name, - uint64_t *value_r, uint64_t *limit ATTR_UNUSED) + uint64_t *value_r) { int ret;
--- a/src/plugins/quota/quota-fs.c Tue Feb 26 15:11:34 2008 +0200 +++ b/src/plugins/quota/quota-fs.c Tue Feb 26 16:21:05 2008 +0200 @@ -549,14 +549,14 @@ static int fs_quota_get_resource(struct quota_root *_root, const char *name, - uint64_t *value_r, uint64_t *limit_r) + uint64_t *value_r) { struct fs_quota_root *root = (struct fs_quota_root *)_root; + uint64_t limit = 0; bool bytes; int ret; *value_r = 0; - *limit_r = 0; if (root->mount == NULL || (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0 && @@ -566,22 +566,29 @@ #ifdef HAVE_RQUOTA if (strcmp(root->mount->type, "nfs") == 0) { - int ret; - T_BEGIN { - ret = do_rquota(root, bytes, value_r, limit_r); + ret = do_rquota(root, bytes, value_r, &limit); } T_END; - return ret; - } + } else #endif - - ret = fs_quota_get_one_resource(root, FALSE, bytes, - value_r, limit_r); - if (ret != 0) + { + ret = fs_quota_get_one_resource(root, FALSE, bytes, + value_r, &limit); + if (ret == 0) { + /* fallback to group quota */ + ret = fs_quota_get_one_resource(root, TRUE, bytes, + value_r, &limit); + } + } + if (ret <= 0) return ret; - /* fallback to group quota */ - return fs_quota_get_one_resource(root, TRUE, bytes, value_r, limit_r); + /* update limit */ + if (bytes) + _root->default_rule.bytes_limit = limit; + else + _root->default_rule.count_limit = limit; + return 1; } static int
--- a/src/plugins/quota/quota-maildir.c Tue Feb 26 15:11:34 2008 +0200 +++ b/src/plugins/quota/quota-maildir.c Tue Feb 26 16:21:05 2008 +0200 @@ -23,8 +23,6 @@ struct quota_root root; const char *maildirsize_path; - uint64_t message_bytes_limit; - uint64_t message_count_limit; uint64_t total_bytes; uint64_t total_count; @@ -208,6 +206,7 @@ static int maildirsize_write(struct maildir_quota_root *root, const char *path) { + const struct quota_rule *rule = &root->root.default_rule; struct dotlock *dotlock; string_t *str; int fd; @@ -229,15 +228,15 @@ } str = t_str_new(128); - if (root->message_bytes_limit != (uint64_t)-1) { + if (rule->bytes_limit != 0) { str_printfa(str, "%lluS", - (unsigned long long)root->message_bytes_limit); + (unsigned long long)rule->bytes_limit); } - if (root->message_count_limit != (uint64_t)-1) { + if (rule->count_limit != 0) { if (str_len(str) > 0) str_append_c(str, ','); str_printfa(str, "%lluC", - (unsigned long long)root->message_count_limit); + (unsigned long long)rule->count_limit); } str_printfa(str, "\n%llu %llu\n", (unsigned long long)root->total_bytes, @@ -352,8 +351,8 @@ char *pos; bool ret = TRUE; - *bytes_r = (uint64_t)-1; - *count_r = (uint64_t)-1; + *bytes_r = 0; + *count_r = 0; /* 0 values mean unlimited */ for (limit = t_strsplit(str, ","); *limit != NULL; limit++) { @@ -382,6 +381,7 @@ static int maildirsize_parse(struct maildir_quota_root *root, int fd, const char *const *lines) { + struct quota_rule *rule = &root->root.default_rule; uint64_t message_bytes_limit, message_count_limit; long long bytes_diff, total_bytes; int count_diff, total_count; @@ -394,15 +394,24 @@ (void)maildir_parse_limit(lines[0], &message_bytes_limit, &message_count_limit); - if (!root->master_message_limits) { - /* we don't know the limits, use whatever the file says */ - root->message_bytes_limit = message_bytes_limit; - root->message_count_limit = message_count_limit; - } else if (root->message_bytes_limit != message_bytes_limit || - root->message_count_limit != message_count_limit) { + /* truncate too high limits to signed 64bit int range */ + if (message_bytes_limit >= (1ULL << 63)) + message_bytes_limit = (1ULL << 63) - 1; + if (message_count_limit >= (1ULL << 63)) + message_count_limit = (1ULL << 63) - 1; + + if (rule->bytes_limit == (int64_t)message_bytes_limit && + rule->count_limit == (int64_t)message_count_limit) { + /* limits haven't changed */ + } else if (root->master_message_limits) { /* we know the limits and they've changed. the file must be rewritten. */ return 0; + } else { + /* we're using limits from the file. */ + rule->bytes_limit = message_bytes_limit; + rule->count_limit = message_count_limit; + quota_root_recalculate_relative_rules(&root->root); } if (*lines == NULL) { @@ -425,8 +434,8 @@ return -1; } - if ((uint64_t)total_bytes > root->message_bytes_limit || - (uint64_t)total_count > root->message_count_limit) { + if (total_bytes > rule->bytes_limit || + total_count > rule->count_limit) { /* we're over quota. don't trust these values if the file contains more than the initial summary line, or if the file is older than 15 minutes. */ @@ -540,26 +549,23 @@ return ret; } -static void maildirquota_init_limits(struct maildir_quota_root *root) +static bool maildirquota_limits_init(struct maildir_quota_root *root) { + if (root->limits_initialized) + return root->maildirsize_path != NULL; root->limits_initialized = TRUE; + /* these limits must be checked before the maildirsize is read the + first time. if master limits aren't used, the default rule limits + will be zero initially, but they'll be updated after the file is + read. */ if (root->root.default_rule.bytes_limit != 0 || - root->root.default_rule.count_limit != 0) { + root->root.default_rule.count_limit != 0) root->master_message_limits = TRUE; - root->message_bytes_limit = root->root.default_rule.bytes_limit; - root->message_count_limit = root->root.default_rule.count_limit; - } -} -static bool maildirquota_limits_init(struct maildir_quota_root *root) -{ - if (!root->limits_initialized) { - maildirquota_init_limits(root); - if (root->maildirsize_path == NULL) { - i_warning("quota maildir: No maildir storages, " - "ignoring quota."); - } + if (root->maildirsize_path == NULL) { + i_warning("quota maildir: No maildir storages, " + "ignoring quota."); } return root->maildirsize_path != NULL; } @@ -575,8 +581,8 @@ ret = maildirsize_read(root); } T_END; if (ret == 0) { - if (root->message_bytes_limit == (uint64_t)-1 && - root->message_count_limit == (uint64_t)-1) { + if (root->root.default_rule.bytes_limit == 0 && + root->root.default_rule.count_limit == 0) { /* no quota */ return 0; } @@ -620,8 +626,6 @@ root = i_new(struct maildir_quota_root, 1); root->fd = -1; - root->message_bytes_limit = (uint64_t)-1; - root->message_count_limit = (uint64_t)-1; return &root->root; } @@ -703,7 +707,7 @@ 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) { struct maildir_quota_root *root = (struct maildir_quota_root *)_root; @@ -712,14 +716,8 @@ if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) { *value_r = root->total_bytes; - if (!root->master_message_limits && - root->message_bytes_limit != (uint64_t)-1) - *limit_r = root->message_bytes_limit; } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) { *value_r = root->total_count; - if (!root->master_message_limits && - root->message_count_limit != (uint64_t)-1) - *limit_r = root->message_count_limit; } else return 0; return 1;
--- a/src/plugins/quota/quota-private.h Tue Feb 26 15:11:34 2008 +0200 +++ b/src/plugins/quota/quota-private.h Tue Feb 26 16:21:05 2008 +0200 @@ -23,11 +23,12 @@ char *mailbox_name; int64_t bytes_limit, count_limit; + /* relative to default_rule */ + unsigned int bytes_percent, count_percent; }; struct quota_warning_rule { - uint64_t bytes_limit; - uint64_t count_limit; + struct quota_rule rule; char *command; }; @@ -45,10 +46,8 @@ 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); + uint64_t *value_r); int (*update)(struct quota_root *root, struct quota_transaction_context *ctx); @@ -102,6 +101,7 @@ void quota_remove_user_storage(struct quota *quota, struct mail_storage *storage); +void quota_root_recalculate_relative_rules(struct quota_root *root); int quota_count(struct quota *quota, uint64_t *bytes_r, uint64_t *count_r); #endif
--- a/src/plugins/quota/quota.c Tue Feb 26 15:11:34 2008 +0200 +++ b/src/plugins/quota/quota.c Tue Feb 26 16:21:05 2008 +0200 @@ -195,7 +195,7 @@ { int64_t percentage = *limit; - if (percentage < 0) { + if (percentage <= 0 || percentage >= -1U) { *error_r = p_strdup_printf(root->pool, "Invalid rule percentage: %lld", (long long)percentage); return -1; @@ -207,17 +207,51 @@ } if (limit == &rule->bytes_limit) - *limit = root->default_rule.bytes_limit * percentage / 100; + rule->bytes_percent = percentage; else if (limit == &rule->count_limit) - *limit = root->default_rule.count_limit * percentage / 100; + rule->count_percent = percentage; else i_unreached(); return 0; } +static void +quota_rule_recalculate_relative_rules(struct quota_rule *rule, + const struct quota_rule *default_rule) +{ + if (rule->bytes_percent > 0) { + rule->bytes_limit = default_rule->bytes_limit * + rule->bytes_percent / 100; + } + if (rule->count_percent > 0) { + rule->count_limit = default_rule->count_limit * + rule->count_percent / 100; + } +} + +void quota_root_recalculate_relative_rules(struct quota_root *root) +{ + struct quota_rule *rules; + struct quota_warning_rule *warning_rules; + unsigned int i, count; + + rules = array_get_modifiable(&root->rules, &count); + for (i = 0; i < count; i++) { + quota_rule_recalculate_relative_rules(&rules[i], + &root->default_rule); + } + + warning_rules = array_get_modifiable(&root->warning_rules, &count); + for (i = 0; i < count; i++) { + quota_rule_recalculate_relative_rules(&warning_rules[i].rule, + &root->default_rule); + } +} + static int quota_rule_parse_limits(struct quota_root *root, struct quota_rule *rule, - const char *limits, const char **error_r) + const char *limits, bool allow_negative, + const char **error_r) { const char **args; char *p; @@ -264,7 +298,7 @@ multiply = 1024ULL*1024*1024*1024; break; case '%': - multiply = 1; + multiply = 0; if (quota_rule_parse_percentage(root, rule, limit, error_r) < 0) return -1; @@ -276,6 +310,16 @@ } *limit *= multiply; } + if (!allow_negative) { + if (rule->bytes_limit < 0) { + *error_r = "Bytes limit can't be negative"; + return -1; + } + if (rule->count_limit < 0) { + *error_r = "Count limit can't be negative"; + return -1; + } + } return 0; } @@ -309,16 +353,20 @@ if (!root->backend.v.parse_rule(root, rule, p, error_r)) ret = -1; } else { - if (quota_rule_parse_limits(root, rule, p, error_r) < 0) + bool allow_negative = rule != &root->default_rule; + + if (quota_rule_parse_limits(root, rule, p, + allow_negative, error_r) < 0) ret = -1; } + quota_root_recalculate_relative_rules(root); if (root->quota->debug) { i_info("Quota rule: root=%s mailbox=%s " - "bytes=%lld messages=%lld", root->name, + "bytes=%lld (%u%%) messages=%lld (%u%%)", root->name, rule->mailbox_name != NULL ? rule->mailbox_name : "", - (long long)rule->bytes_limit, - (long long)rule->count_limit); + (long long)rule->bytes_limit, rule->bytes_percent, + (long long)rule->count_limit, rule->count_percent); } return ret; } @@ -426,29 +474,22 @@ memset(&rule, 0, sizeof(rule)); ret = quota_rule_parse_limits(root, &rule, t_strdup_until(rule_def, p), - error_r); + TRUE, error_r); if (ret < 0) return -1; - if (rule.bytes_limit < 0) { - *error_r = "Bytes limit can't be negative"; - return -1; - } - if (rule.count_limit < 0) { - *error_r = "Count limit can't be negative"; - return -1; - } - warning = array_append_space(&root->warning_rules); warning->command = i_strdup(p+1); - warning->bytes_limit = rule.bytes_limit; - warning->count_limit = rule.count_limit; + warning->rule = rule; + quota_root_recalculate_relative_rules(root); if (root->quota->debug) { - i_info("Quota warning: bytes=%llu messages=%llu command=%s", - (unsigned long long)warning->bytes_limit, - (unsigned long long)warning->count_limit, - warning->command); + i_info("Quota warning: bytes=%llu (%u%%) " + "messages=%llu (%u%%) command=%s", + (unsigned long long)warning->rule.bytes_limit, + warning->rule.bytes_percent, + (unsigned long long)warning->rule.count_limit, + warning->rule.count_percent, warning->command); } return 0; } @@ -537,6 +578,12 @@ kilobytes = TRUE; } + /* Get the value first. This call may also update quota limits if + they're defined externally. */ + ret = root->backend.v.get_resource(root, name, value_r); + if (ret <= 0) + return ret; + (void)quota_root_get_rule_limits(root, mailbox_name, &bytes_limit, &count_limit); if (strcmp(name, QUOTA_NAME_STORAGE_BYTES) == 0) @@ -546,13 +593,11 @@ else *limit_r = 0; - ret = root->backend.v.get_resource(root, name, value_r, limit_r); - if (kilobytes && ret > 0) { + if (kilobytes) { *value_r /= 1024; *limit_r /= 1024; } - return ret <= 0 ? ret : - (*limit_r == 0 ? 0 : 1); + return *limit_r == 0 ? 0 : 1; } int quota_set_resource(struct quota_root *root ATTR_UNUSED, @@ -662,10 +707,10 @@ bytes_before = bytes_current - ctx->bytes_used; count_before = count_current - ctx->count_used; for (i = 0; i < count; i++) { - if ((bytes_before < warnings[i].bytes_limit && - bytes_current >= warnings[i].bytes_limit) || - (count_before < warnings[i].count_limit && - count_current >= warnings[i].count_limit)) { + if ((bytes_before < (uint64_t)warnings[i].rule.bytes_limit && + bytes_current >= (uint64_t)warnings[i].rule.bytes_limit) || + (count_before < (uint64_t)warnings[i].rule.count_limit && + count_current >= (uint64_t)warnings[i].rule.count_limit)) { quota_warning_execute(warnings[i].command); break; }