Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/plugins/quota/quota-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ struct quota_transaction_context {
bool auto_updating:1;
/* Quota doesn't need to be updated within this transaction. */
bool no_quota_updates:1;
/* TRUE if this transaction is for an IMAP MOVE operation. Used by
quota_try_alloc() to avoid double-counting the expunged source
mail: quota_mail_expunge() already accounts for it in the source
transaction via quota_free_bytes(). */
bool moving:1;
};

void quota_add_user_namespace(struct quota *quota, const char *root_name,
Expand Down
1 change: 1 addition & 0 deletions src/plugins/quota/quota-storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ static int quota_check(struct mail_save_context *ctx)
if (qt->failed)
return 0;

qt->moving = ctx->moving;
const char *error;
ret = quota_try_alloc(qt, ctx->dest_mail, ctx->expunged_mail,
NULL, &error);
Expand Down
9 changes: 8 additions & 1 deletion src/plugins/quota/quota.c
Original file line number Diff line number Diff line change
Expand Up @@ -1180,7 +1180,14 @@ quota_try_alloc(struct quota_transaction_context *ctx,
quota_alloc() or quota_free_bytes() was already used within the same
transaction, but that doesn't normally happen. */
ctx->auto_updating = FALSE;
quota_alloc_with_size(ctx, size, expunged_size);
/* For IMAP MOVE, do not subtract expunged_size here: the source mail
expunge is already accounted for by quota_mail_expunge() ->
quota_free_bytes() in the source transaction. Subtracting it here
too causes double-counting (destination records bytes_used=0, source
records bytes_used=-size, net result: quota drops by size).
expunged_size is still passed to quota_test_alloc() above so that
moves at the quota boundary are correctly permitted. */
quota_alloc_with_size(ctx, size, ctx->moving ? 0 : expunged_size);
return QUOTA_ALLOC_RESULT_OK;
}

Expand Down