From: Stephan Bosch Date: Tue, 13 Nov 2018 21:50:11 +0000 (+0100) Subject: quota: Make quota_test_alloc() suitable for moving/replacing messages X-Git-Tag: 2.4.1~130 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=667d0da61e9c5916c4692e2f21a8c32959ee65b0;p=thirdparty%2Fdovecot%2Fcore.git quota: Make quota_test_alloc() suitable for moving/replacing messages --- diff --git a/src/plugins/quota/quota-private.h b/src/plugins/quota/quota-private.h index 40b57aafdc..d3c91ab6c9 100644 --- a/src/plugins/quota/quota-private.h +++ b/src/plugins/quota/quota-private.h @@ -25,6 +25,7 @@ struct quota { enum quota_alloc_result (*test_alloc)( struct quota_transaction_context *ctx, uoff_t size, + struct mailbox *expunged_box, uoff_t expunged_size, const struct quota_overrun **overruns_r, const char **error_r); bool vsizes:1; diff --git a/src/plugins/quota/quota-status.c b/src/plugins/quota/quota-status.c index b663b3ac0c..b5b25bdc73 100644 --- a/src/plugins/quota/quota-status.c +++ b/src/plugins/quota/quota-status.c @@ -119,7 +119,8 @@ quota_check(struct mail_user *user, uoff_t mail_size, const char **error_r) ctx = quota_transaction_begin(box); const char *internal_error; - ret = quota_test_alloc(ctx, I_MAX(1, mail_size), NULL, &internal_error); + ret = quota_test_alloc(ctx, I_MAX(1, mail_size), NULL, 0, NULL, + &internal_error); if (ret == QUOTA_ALLOC_RESULT_TEMPFAIL) e_error(user->event, "quota check failed: %s", internal_error); *error_r = quota_alloc_result_errstr(ret, ctx); diff --git a/src/plugins/quota/quota-storage.c b/src/plugins/quota/quota-storage.c index ea70a32310..030937748c 100644 --- a/src/plugins/quota/quota-storage.c +++ b/src/plugins/quota/quota-storage.c @@ -162,7 +162,7 @@ quota_get_status(struct mailbox *box, enum mailbox_status_items items, qt = quota_transaction_begin(box); const char *error; enum quota_alloc_result qret = - quota_test_alloc(qt, 0, NULL, &error); + quota_test_alloc(qt, 0, NULL, 0, NULL, &error); if (qret != QUOTA_ALLOC_RESULT_OK) { quota_set_storage_error(qt, box, qret, error); ret = -1; @@ -375,7 +375,7 @@ quota_save_begin(struct mail_save_context *ctx, struct istream *input) full mail. */ enum quota_alloc_result qret = - quota_test_alloc(qt, size, NULL, &error); + quota_test_alloc(qt, size, NULL, 0, NULL, &error); switch (qret) { case QUOTA_ALLOC_RESULT_OK: /* Great, there is space. */ diff --git a/src/plugins/quota/quota.c b/src/plugins/quota/quota.c index 5034f583c5..4ec91f0afe 100644 --- a/src/plugins/quota/quota.c +++ b/src/plugins/quota/quota.c @@ -51,6 +51,7 @@ static ARRAY(const struct quota_backend*) quota_backends; static enum quota_alloc_result quota_default_test_alloc(struct quota_transaction_context *ctx, uoff_t size, + struct mailbox *expunged_box, uoff_t expunged_size, const struct quota_overrun **overruns_r, const char **error_r); static void quota_over_status_check_root(struct quota_root *root); @@ -1151,7 +1152,7 @@ quota_try_alloc(struct quota_transaction_context *ctx, struct mail *mail, } enum quota_alloc_result ret = - quota_test_alloc(ctx, size, overruns_r, error_r); + quota_test_alloc(ctx, size, NULL, 0, overruns_r, error_r); if (ret != QUOTA_ALLOC_RESULT_OK) return ret; /* with quota_try_alloc() we want to keep track of how many bytes @@ -1166,6 +1167,7 @@ quota_try_alloc(struct quota_transaction_context *ctx, struct mail *mail, enum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx, uoff_t size, + struct mailbox *expunged_box, uoff_t expunged_size, const struct quota_overrun **overruns_r, const char **error_r) { if (overruns_r != NULL) @@ -1195,11 +1197,13 @@ quota_test_alloc(struct quota_transaction_context *ctx, uoff_t size, return QUOTA_ALLOC_RESULT_OK; /* this is a virtual function mainly for trash plugin and similar, which may automatically delete mails to stay under quota. */ - return ctx->quota->test_alloc(ctx, size, overruns_r, error_r); + return ctx->quota->test_alloc(ctx, size, expunged_box, + expunged_size, overruns_r, error_r); } static enum quota_alloc_result quota_default_test_alloc(struct quota_transaction_context *ctx, uoff_t size, + struct mailbox *expunged_box, uoff_t expunged_size, const struct quota_overrun **overruns_r, const char **error_r) { @@ -1211,8 +1215,11 @@ quota_default_test_alloc(struct quota_transaction_context *ctx, uoff_t size, if (overruns_r != NULL) *overruns_r = NULL; - if (!quota_transaction_is_over(ctx, size)) + if (!quota_transaction_is_over(ctx, size)) { + /* Doesn't exceed quota for any root, even without expunging + the (replaced or moved) mail. */ return QUOTA_ALLOC_RESULT_OK; + } if (ctx->set->quota_mailbox_message_count != SET_UINT_UNLIMITED) { struct mailbox_status status; @@ -1222,18 +1229,23 @@ quota_default_test_alloc(struct quota_transaction_context *ctx, uoff_t size, return QUOTA_ALLOC_RESULT_OVER_QUOTA_MAILBOX_LIMIT; } - /* limit reached. */ + /* Some limit is (almost) reached. Find out which one and check whether + the expunged message prevents it from truly crossing the limit. */ t_array_init(&overruns, 4); roots = array_get(&ctx->quota->all_roots, &count); for (i = 0; i < count; i++) { + bool expunged = FALSE; uint64_t bytes_limit, count_limit; struct quota_overrun overrun; if (!quota_root_is_visible(roots[i], ctx->box) || !roots[i]->set->quota_enforce) continue; + if (expunged_box != NULL && + quota_root_is_visible(roots[i], expunged_box)) + expunged = TRUE; if (quota_root_get_rule_limits(roots[i], ctx->box->event, &bytes_limit, &count_limit, @@ -1251,7 +1263,9 @@ quota_default_test_alloc(struct quota_transaction_context *ctx, uoff_t size, } i_zero(&overrun); - if (quota_root_is_over(ctx, &ctx->roots[i], 1, size, 0, 0, + if (quota_root_is_over(ctx, &ctx->roots[i], 1, size, + (expunged ? 1 : 0), + (expunged ? expunged_size : 0), &overrun.resource.count, &overrun.resource.bytes)) { overrun.root = roots[i]; @@ -1259,7 +1273,9 @@ quota_default_test_alloc(struct quota_transaction_context *ctx, uoff_t size, } } - i_assert(array_count(&overruns) > 0); + if (array_count(&overruns) == 0) + return QUOTA_ALLOC_RESULT_OK; + if (overruns_r != NULL) { array_append_zero(&overruns); *overruns_r = array_front(&overruns); diff --git a/src/plugins/quota/quota.h b/src/plugins/quota/quota.h index a423734277..3861969934 100644 --- a/src/plugins/quota/quota.h +++ b/src/plugins/quota/quota.h @@ -126,6 +126,7 @@ quota_try_alloc(struct quota_transaction_context *ctx, struct mail *mail, /* Like quota_try_alloc(), but don't actually allocate anything. */ enum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx, uoff_t size, + struct mailbox *expunged_box, uoff_t expunged_size, const struct quota_overrun **overruns_r, const char **error_r) ATTR_NULL(3); /* Update quota by allocating/freeing space used by mail. */ diff --git a/src/plugins/trash/trash-plugin.c b/src/plugins/trash/trash-plugin.c index e91dc56cd6..3c4cfb641b 100644 --- a/src/plugins/trash/trash-plugin.c +++ b/src/plugins/trash/trash-plugin.c @@ -99,11 +99,10 @@ const char *trash_plugin_version = DOVECOT_ABI_VERSION; static MODULE_CONTEXT_DEFINE_INIT(trash_user_module, &mail_user_module_register); -static enum quota_alloc_result -(*trash_next_quota_test_alloc)(struct quota_transaction_context *ctx, - uoff_t size, - const struct quota_overrun **overruns_r, - const char **error_r); +static enum quota_alloc_result(*trash_next_quota_test_alloc)( + struct quota_transaction_context *ctx, uoff_t size, + struct mailbox *expunged_box, uoff_t expunged_size, + const struct quota_overrun **overruns_r, const char **error_r); static void trash_clean_init(struct trash_clean *tclean, @@ -514,6 +513,7 @@ trash_try_clean_mails(struct quota_transaction_context *ctx, static enum quota_alloc_result trash_quota_test_alloc(struct quota_transaction_context *ctx, uoff_t size, + struct mailbox *expunged_box, uoff_t expunged_size, const struct quota_overrun **overruns_r, const char **error_r) { @@ -522,8 +522,10 @@ trash_quota_test_alloc(struct quota_transaction_context *ctx, uoff_t size, for (i = 0; ; i++) { const struct quota_overrun *overruns = NULL; enum quota_alloc_result ret; - ret = trash_next_quota_test_alloc(ctx, size, - &overruns, error_r); + + ret = trash_next_quota_test_alloc( + ctx, size, expunged_box, expunged_size, + &overruns, error_r); if (overruns_r != NULL) *overruns_r = overruns; if (ret != QUOTA_ALLOC_RESULT_OVER_QUOTA) {