]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
quota: Make quota_test_alloc() suitable for moving/replacing messages
authorStephan Bosch <stephan.bosch@dovecot.fi>
Tue, 13 Nov 2018 21:50:11 +0000 (22:50 +0100)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Wed, 26 Feb 2025 10:45:00 +0000 (10:45 +0000)
src/plugins/quota/quota-private.h
src/plugins/quota/quota-status.c
src/plugins/quota/quota-storage.c
src/plugins/quota/quota.c
src/plugins/quota/quota.h
src/plugins/trash/trash-plugin.c

index 40b57aafdc159bc662ac32c111524606399664a9..d3c91ab6c96d7bcefae490bd7ad896eee4d3a85e 100644 (file)
@@ -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;
index b663b3ac0c214e170a4361e0864eb0419576bc87..b5b25bdc7347dfc3cbefb7db17fdc550dd1defc1 100644 (file)
@@ -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);
index ea70a323102d719adc7b62a222bc42a620ddcf07..030937748c6adc4cf7f04efcb9064a494687a29b 100644 (file)
@@ -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. */
index 5034f583c553a21f64d5e0fa5cb27c4309a5881b..4ec91f0afe30dfea59530b28c0685b3e4bc6e578 100644 (file)
@@ -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);
index a42373427780f3634378a67f0933edd59b48d7bb..386196993478160c6f80cfef170e7f6f6e7807ec 100644 (file)
@@ -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. */
index e91dc56cd66cff2d32b2ed1cb9a613c48e170357..3c4cfb641b16db62c165eb60c915bbf9a165aa80 100644 (file)
@@ -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) {