From b62139aae0a0099d0ede35b164b2870479f9b027 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Martti=20Rannanj=C3=A4rvi?= Date: Fri, 6 Oct 2017 23:35:34 +0300 Subject: [PATCH] quota: Add error_r to quota_settings->test_alloc() --- src/plugins/quota/quota-private.h | 3 +- src/plugins/quota/quota-status.c | 5 +++- src/plugins/quota/quota-storage.c | 22 ++++++++------- src/plugins/quota/quota.c | 47 ++++++++++++++++++++++--------- src/plugins/quota/quota.h | 7 +++-- src/plugins/trash/trash-plugin.c | 14 ++++++--- 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/src/plugins/quota/quota-private.h b/src/plugins/quota/quota-private.h index 6427b61886..da259bf785 100644 --- a/src/plugins/quota/quota-private.h +++ b/src/plugins/quota/quota-private.h @@ -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; diff --git a/src/plugins/quota/quota-status.c b/src/plugins/quota/quota-status.c index 8ade7036d7..1b227339ff 100644 --- a/src/plugins/quota/quota-status.c +++ b/src/plugins/quota/quota-status.c @@ -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); diff --git a/src/plugins/quota/quota-storage.c b/src/plugins/quota/quota-storage.c index fff294af21..3772028f6e 100644 --- a/src/plugins/quota/quota-storage.c +++ b/src/plugins/quota/quota-storage.c @@ -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); diff --git a/src/plugins/quota/quota.c b/src/plugins/quota/quota.c index 316a86aab6..0b07c56e42 100644 --- a/src/plugins/quota/quota.c +++ b/src/plugins/quota/quota.c @@ -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; } diff --git a/src/plugins/quota/quota.h b/src/plugins/quota/quota.h index 206f8b7a36..e979388ffb 100644 --- a/src/plugins/quota/quota.h +++ b/src/plugins/quota/quota.h @@ -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, diff --git a/src/plugins/trash/trash-plugin.c b/src/plugins/trash/trash-plugin.c index 699f7553d4..0b5c8c0131 100644 --- a/src/plugins/trash/trash-plugin.c +++ b/src/plugins/trash/trash-plugin.c @@ -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; } -- 2.47.3