]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
quota: Add error_r to quota_settings->test_alloc()
authorMartti Rannanjärvi <martti.rannanjarvi@dovecot.fi>
Fri, 6 Oct 2017 20:35:34 +0000 (23:35 +0300)
committerTimo Sirainen <tss@dovecot.fi>
Thu, 19 Oct 2017 13:43:44 +0000 (16:43 +0300)
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 6427b618869675142054390bf34a732555274362..da259bf785b9c8834c64841f96997a6d15a550f6 100644 (file)
@@ -23,7 +23,8 @@ struct quota_settings {
 
        ARRAY(struct quota_root_settings *) root_sets;
        enum quota_alloc_result (*test_alloc)(
-               struct quota_transaction_context *ctx, uoff_t size);
+               struct quota_transaction_context *ctx, uoff_t size,
+               const char **error_r);
 
        uoff_t max_mail_size;
        const char *quota_exceeded_msg;
index 8ade7036d749b0fe7ee20aa2e8f7c1bcaf5cb8d9..1b227339ff5f5f4f3804fd048abc02396af4d5f9 100644 (file)
@@ -69,7 +69,10 @@ quota_check(struct mail_user *user, uoff_t mail_size, const char **error_r)
        mailbox_set_reason(box, "quota status");
 
        ctx = quota_transaction_begin(box);
-       ret = quota_test_alloc(ctx, I_MAX(1, mail_size));
+       const char *internal_error;
+       ret = quota_test_alloc(ctx, I_MAX(1, mail_size), &internal_error);
+       if (ret == QUOTA_ALLOC_RESULT_TEMPFAIL)
+               i_error("quota check failed: %s", internal_error);
        *error_r = quota_alloc_result_errstr(ret, ctx);
        quota_transaction_rollback(&ctx);
 
index fff294af21a66c8553b6766cdb42dde0b2c7e97f..3772028f6e09b2eb5e664e870c38d7221a38702f 100644 (file)
@@ -120,8 +120,11 @@ quota_get_status(struct mailbox *box, enum mailbox_status_items items,
 
        if ((items & STATUS_CHECK_OVER_QUOTA) != 0) {
                qt = quota_transaction_begin(box);
-               enum quota_alloc_result qret = quota_test_alloc(qt, 0);
+               const char *error;
+               enum quota_alloc_result qret = quota_test_alloc(qt, 0, &error);
                if (qret != QUOTA_ALLOC_RESULT_OK) {
+                       if (qret == QUOTA_ALLOC_RESULT_TEMPFAIL)
+                               i_error("quota check failed: %s", error);
                        quota_set_storage_error(qt, box->storage, qret);
                        ret = -1;
                }
@@ -250,16 +253,14 @@ static int quota_check(struct mail_save_context *ctx, struct mailbox *src_box)
                return 0;
        }
 
-       ret = quota_try_alloc(qt, ctx->dest_mail);
+       const char *error;
+       ret = quota_try_alloc(qt, ctx->dest_mail, &error);
        switch (ret) {
        case QUOTA_ALLOC_RESULT_OK:
                return 0;
        case QUOTA_ALLOC_RESULT_TEMPFAIL:
-               /* allow saving anyway. don't log an error, because at this
-                  point we can't give very informative error without API
-                  changes. the real error should have been logged already
-                  (except if this was due to quota calculation on background,
-                  then we intentionally don't want to log anything) */
+               /* Log the error, but allow saving anyway. */
+               i_error("quota check failed: %s", error);
                return 0;
        default:
                quota_set_storage_error(qt, t->box->storage, ret);
@@ -298,6 +299,7 @@ quota_save_begin(struct mail_save_context *ctx, struct istream *input)
        struct mailbox_transaction_context *t = ctx->transaction;
        struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
        struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
+       const char *error;
        uoff_t size;
 
        if (!ctx->moving && i_stream_get_size(input, TRUE, &size) > 0) {
@@ -311,14 +313,14 @@ quota_save_begin(struct mail_save_context *ctx, struct istream *input)
                   benefit of giving "out of quota" error before sending the
                   full mail. */
 
-               enum quota_alloc_result qret = quota_test_alloc(qt, size);
+               enum quota_alloc_result qret = quota_test_alloc(qt, size, &error);
                switch (qret) {
                case QUOTA_ALLOC_RESULT_OK:
                        /* Great, there is space. */
                        break;
                case QUOTA_ALLOC_RESULT_TEMPFAIL:
-                       /* allow saving anyway. don't log an error - see
-                          quota_check() for reasons. */
+                       /* Log the error, but allow saving anyway. */
+                       i_error("quota allocation failed: %s", error);
                        break;
                default:
                        quota_set_storage_error(qt, t->box->storage, qret);
index 316a86aab69022bf977c770708eafa020eb33511..0b07c56e4235b8994c93446614927163a9bf793a 100644 (file)
@@ -68,7 +68,8 @@ struct quota_param_parser quota_param_noenforcing = {.param_name = "noenforcing"
 struct quota_param_parser quota_param_ns = {.param_name = "ns=", .param_handler = ns_param_handler};
 
 static enum quota_alloc_result quota_default_test_alloc(
-               struct quota_transaction_context *ctx, uoff_t size);
+               struct quota_transaction_context *ctx, uoff_t size,
+               const char **error_r);
 static void quota_over_flag_check_root(struct quota_root *root);
 
 static const struct quota_backend *quota_backend_find(const char *name)
@@ -1257,12 +1258,14 @@ static int quota_get_mail_size(struct quota_transaction_context *ctx,
 }
 
 enum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx,
-                                       struct mail *mail)
+                                       struct mail *mail, const char **error_r)
 {
        uoff_t size;
 
-       if (quota_transaction_set_limits(ctx) < 0)
+       if (quota_transaction_set_limits(ctx) < 0) {
+               *error_r = "Failed to set quota transaction limits";
                return QUOTA_ALLOC_RESULT_TEMPFAIL;
+       }
 
        if (ctx->no_quota_updates)
                return QUOTA_ALLOC_RESULT_OK;
@@ -1276,12 +1279,13 @@ enum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx,
                           so just return success for the quota allocated. */
                        return QUOTA_ALLOC_RESULT_OK;
                }
-               i_error("quota: Failed to get mail size (box=%s, uid=%u): %s",
+               *error_r = t_strdup_printf(
+                       "Failed to get mail size (box=%s, uid=%u): %s",
                        mail->box->vname, mail->uid, errstr);
                return QUOTA_ALLOC_RESULT_TEMPFAIL;
        }
 
-       enum quota_alloc_result ret = quota_test_alloc(ctx, size);
+       enum quota_alloc_result ret = quota_test_alloc(ctx, size, error_r);
        if (ret != QUOTA_ALLOC_RESULT_OK)
                return ret;
        /* with quota_try_alloc() we want to keep track of how many bytes
@@ -1295,27 +1299,36 @@ enum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx,
 }
 
 enum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx,
-                                        uoff_t size)
+                                        uoff_t size, const char **error_r)
 {
-       if (ctx->failed)
+       if (ctx->failed) {
+               *error_r = "Quota transaction has failed earlier";
                return QUOTA_ALLOC_RESULT_TEMPFAIL;
+       }
 
-       if (quota_transaction_set_limits(ctx) < 0)
+       if (quota_transaction_set_limits(ctx) < 0) {
+               *error_r = "Failed to set quota transaction limits";
                return QUOTA_ALLOC_RESULT_TEMPFAIL;
+       }
 
        uoff_t max_size = ctx->quota->set->max_mail_size;
-       if (max_size > 0 && size > max_size)
+       if (max_size > 0 && size > max_size) {
+               *error_r = t_strdup_printf(
+                       "Requested allocation size %"PRIuUOFF_T" exceeds max "
+                       "mail size %"PRIuUOFF_T, size, max_size);
                return QUOTA_ALLOC_RESULT_OVER_MAXSIZE;
+       }
 
        if (ctx->no_quota_updates)
                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->set->test_alloc(ctx, size);
+       return ctx->quota->set->test_alloc(ctx, size, error_r);
 }
 
 static enum quota_alloc_result quota_default_test_alloc(
-                       struct quota_transaction_context *ctx, uoff_t size)
+                       struct quota_transaction_context *ctx, uoff_t size,
+                       const char **error_r)
 {
        struct quota_root *const *roots;
        unsigned int i, count;
@@ -1337,14 +1350,22 @@ static enum quota_alloc_result quota_default_test_alloc(
                                                 mailbox_get_vname(ctx->box),
                                                 &bytes_limit, &count_limit,
                                                 &ignore);
-               if (ret < 0)
+               if (ret < 0) {
+                       *error_r = "Failed to get quota root rule limits";
                        return QUOTA_ALLOC_RESULT_TEMPFAIL;
+               }
 
                /* if size is bigger than any limit, then
                   it is bigger than the lowest limit */
-               if (bytes_limit > 0 && size > bytes_limit)
+               if (bytes_limit > 0 && size > bytes_limit) {
+                       *error_r = t_strdup_printf(
+                               "Allocating %"PRIuUOFF_T" bytes would exceed quota limit",
+                               size);
                        return QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT;
+               }
        }
+       *error_r = t_strdup_printf(
+               "Allocating %"PRIuUOFF_T" bytes would exceed quota", size);
        return QUOTA_ALLOC_RESULT_OVER_QUOTA;
 }
 
index 206f8b7a36c4c00a81a50614933568fe5e3d0477..e979388ffb6286daf9b6bf2fd2aa7a6103d9c2f6 100644 (file)
@@ -114,12 +114,13 @@ int quota_transaction_commit(struct quota_transaction_context **ctx);
 /* Rollback quota transaction changes. */
 void quota_transaction_rollback(struct quota_transaction_context **ctx);
 
-/* Allocate from quota if there's space. */
+/* Allocate from quota if there's space. error_r is set when result is not
+ * QUOTA_ALLOC_RESULT_OK. */
 enum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx,
-                                       struct mail *mail);
+                                       struct mail *mail, const char **error_r);
 /* 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);
+                                        uoff_t size, const char **error_r);
 /* Update quota by allocating/freeing space used by mail. */
 void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail);
 void quota_free_bytes(struct quota_transaction_context *ctx,
index 699f7553d43f56b8cc445e0468655d2c3c0e3203..0b5c8c01316aeddd04bf1377e1dc14e14286252a 100644 (file)
@@ -45,7 +45,8 @@ 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 *, uoff_t);
+               struct quota_transaction_context *, uoff_t,
+               const char **error_r);
 
 static int trash_clean_mailbox_open(struct trash_mailbox *trash)
 {
@@ -219,7 +220,7 @@ err:
 
 static enum quota_alloc_result
 trash_quota_test_alloc(struct quota_transaction_context *ctx,
-                      uoff_t size)
+                      uoff_t size, const char **error_r)
 {
        int i;
        uint64_t size_needed = 0;
@@ -227,7 +228,7 @@ trash_quota_test_alloc(struct quota_transaction_context *ctx,
 
        for (i = 0; ; i++) {
                enum quota_alloc_result ret;
-               ret = trash_next_quota_test_alloc(ctx, size);
+               ret = trash_next_quota_test_alloc(ctx, size, error_r);
                if (ret != QUOTA_ALLOC_RESULT_OVER_QUOTA) {
                        if (ret == QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT &&
                            ctx->quota->user->mail_debug)
@@ -252,9 +253,14 @@ trash_quota_test_alloc(struct quota_transaction_context *ctx,
                        count_needed = 1 + ctx->count_over - ctx->count_ceil;
 
                /* not enough space. try deleting some from mailbox. */
-               if (trash_try_clean_mails(ctx, size_needed, count_needed) <= 0)
+               if (trash_try_clean_mails(ctx, size_needed, count_needed) <= 0) {
+                       *error_r = t_strdup_printf(
+                               "Allocating %"PRIuUOFF_T" bytes would exceed quota", size);
                        return QUOTA_ALLOC_RESULT_OVER_QUOTA;
+               }
        }
+       *error_r = t_strdup_printf(
+               "Allocating %"PRIuUOFF_T" bytes would exceed quota", size);
        return QUOTA_ALLOC_RESULT_OVER_QUOTA;
 }