]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
quota-clone: Move state tracking to user context
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 19 Jun 2018 12:00:02 +0000 (15:00 +0300)
committerAki Tuomi <aki.tuomi@dovecot.fi>
Tue, 7 Aug 2018 09:27:45 +0000 (12:27 +0300)
This way there's no need to do multiple flushes when e.g. multiple
mailboxes change.

The flush had to be removed from close(), because the code path recurses:

 * mailbox_transaction_commit() when mail is saved
 * quota_count() starts counting the quota, which opens and closes mailbox
 * quota_clone_mailbox_close() attempts to flush quota, which again recurses
into quota_count(), which returns 0 for the quota value.

Based on patch by Michael Slusarz

src/plugins/quota-clone/quota-clone-plugin.c

index d99ed90a1d18df0df9258d78d5412f69ab6c5a34..8cddd055754961569db8857a555e77d1e9edd09e 100644 (file)
@@ -31,20 +31,15 @@ static MODULE_CONTEXT_DEFINE_INIT(quota_clone_storage_module,
 struct quota_clone_user {
        union mail_user_module_context module_ctx;
        struct dict *dict;
-       bool quota_flushing;
-};
-
-struct quota_clone_mailbox {
-       union mailbox_module_context module_ctx;
        struct timeout *to_quota_flush;
        bool quota_changed;
+       bool quota_flushing;
 };
 
-static void quota_clone_flush_real(struct mailbox *box)
+static void quota_clone_flush_real(struct mail_user *user)
 {
-       struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box);
        struct quota_clone_user *quser =
-               QUOTA_CLONE_USER_CONTEXT_REQUIRE(box->storage->user);
+               QUOTA_CLONE_USER_CONTEXT_REQUIRE(user);
        struct dict_transaction_context *trans;
        struct quota_root_iter *iter;
        struct quota_root *root;
@@ -53,12 +48,12 @@ static void quota_clone_flush_real(struct mailbox *box)
        enum quota_get_result bytes_res, count_res;
 
        /* we'll clone the first quota root */
-       iter = quota_root_iter_init(box);
+       iter = quota_root_iter_init_user(user);
        root = quota_root_iter_next(iter);
        quota_root_iter_deinit(&iter);
        if (root == NULL) {
-               /* no quota roots defined for this mailbox - ignore */
-               qbox->quota_changed = FALSE;
+               /* no quota roots defined - ignore */
+               quser->quota_changed = FALSE;
                return;
        }
 
@@ -109,103 +104,121 @@ static void quota_clone_flush_real(struct mailbox *box)
        if (dict_transaction_commit(&trans, &error) < 0)
                i_error("quota_clone_plugin: Failed to commit dict update: %s", error);
        else
-               qbox->quota_changed = FALSE;
+               quser->quota_changed = FALSE;
 }
 
-static void quota_clone_flush(struct mailbox *box)
+static void quota_clone_flush(struct mail_user *user)
 {
-       struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box);
        struct quota_clone_user *quser =
-               QUOTA_CLONE_USER_CONTEXT_REQUIRE(box->storage->user);
+               QUOTA_CLONE_USER_CONTEXT_REQUIRE(user);
 
-       timeout_remove(&qbox->to_quota_flush);
+       timeout_remove(&quser->to_quota_flush);
 
        if (quser->quota_flushing) {
                /* recursing back from quota recalculation */
-       } else if (qbox->quota_changed) {
+       } else if (quser->quota_changed) {
                quser->quota_flushing = TRUE;
-               quota_clone_flush_real(box);
+               quota_clone_flush_real(user);
                quser->quota_flushing = FALSE;
        }
 }
 
+static struct mail_user *quota_mailbox_get_user(struct mailbox *box)
+{
+       struct mail_namespace *ns = mailbox_list_get_namespace(box->list);
+       return ns->owner != NULL ? ns->owner : ns->user;
+}
+
 static void quota_clone_changed(struct mailbox *box)
 {
-       struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box);
+       struct mail_user *user = quota_mailbox_get_user(box);
+       struct quota_clone_user *quser =
+               QUOTA_CLONE_USER_CONTEXT_REQUIRE(user);
 
-       qbox->quota_changed = TRUE;
-       if (qbox->to_quota_flush == NULL) {
-               qbox->to_quota_flush = timeout_add(QUOTA_CLONE_FLUSH_DELAY_MSECS,
-                                                  quota_clone_flush, box);
+       quser->quota_changed = TRUE;
+       if (quser->to_quota_flush == NULL) {
+               quser->to_quota_flush = timeout_add(QUOTA_CLONE_FLUSH_DELAY_MSECS,
+                                                   quota_clone_flush, user);
        }
 }
 
 static int quota_clone_save_finish(struct mail_save_context *ctx)
 {
-       struct quota_clone_mailbox *qbox =
+       union mailbox_module_context *qbox =
                QUOTA_CLONE_CONTEXT(ctx->transaction->box);
 
        quota_clone_changed(ctx->transaction->box);
-       return qbox->module_ctx.super.save_finish(ctx);
+       return qbox->super.save_finish(ctx);
 }
 
 static int
 quota_clone_copy(struct mail_save_context *ctx, struct mail *mail)
 {
-       struct quota_clone_mailbox *qbox =
+       union mailbox_module_context *qbox =
                QUOTA_CLONE_CONTEXT(ctx->transaction->box);
 
        quota_clone_changed(ctx->transaction->box);
-       return qbox->module_ctx.super.copy(ctx, mail);
+       return qbox->super.copy(ctx, mail);
 }
 
 static void
 quota_clone_mailbox_sync_notify(struct mailbox *box, uint32_t uid,
                                enum mailbox_sync_type sync_type)
 {
-       struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box);
+       union mailbox_module_context *qbox = QUOTA_CLONE_CONTEXT(box);
 
-       if (qbox->module_ctx.super.sync_notify != NULL)
-               qbox->module_ctx.super.sync_notify(box, uid, sync_type);
+       if (qbox->super.sync_notify != NULL)
+               qbox->super.sync_notify(box, uid, sync_type);
 
        if (sync_type == MAILBOX_SYNC_TYPE_EXPUNGE)
                quota_clone_changed(box);
 }
 
-static void quota_clone_mailbox_close(struct mailbox *box)
-{
-       struct quota_clone_mailbox *qbox = QUOTA_CLONE_CONTEXT(box);
-
-       qbox->module_ctx.super.close(box);
-
-       quota_clone_flush(box);
-}
-
 static void quota_clone_mailbox_allocated(struct mailbox *box)
 {
        struct quota_clone_user *quser =
                QUOTA_CLONE_USER_CONTEXT(box->storage->user);
        struct mailbox_vfuncs *v = box->vlast;
-       struct quota_clone_mailbox *qbox;
+       union mailbox_module_context *qbox;
 
        if (quser == NULL)
                return;
 
-       qbox = p_new(box->pool, struct quota_clone_mailbox, 1);
-       qbox->module_ctx.super = *v;
-       box->vlast = &qbox->module_ctx.super;
+       qbox = p_new(box->pool, union mailbox_module_context, 1);
+       qbox->super = *v;
+       box->vlast = &qbox->super;
 
        v->save_finish = quota_clone_save_finish;
        v->copy = quota_clone_copy;
        v->sync_notify = quota_clone_mailbox_sync_notify;
-       v->close = quota_clone_mailbox_close;
-       MODULE_CONTEXT_SET(box, quota_clone_storage_module, qbox);
+       MODULE_CONTEXT_SET_SELF(box, quota_clone_storage_module, qbox);
+}
+
+static void quota_clone_mail_user_deinit_pre(struct mail_user *user)
+{
+       struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT_REQUIRE(user);
+
+       dict_wait(quser->dict);
+       /* Check once more if quota needs to be updated. This needs to be done
+          in deinit_pre(), because at deinit() the quota is already
+          deinitialized. */
+       if (quser->to_quota_flush != NULL) {
+               i_assert(!quser->quota_flushing);
+               quota_clone_flush(user);
+               dict_wait(quser->dict);
+               i_assert(quser->to_quota_flush == NULL);
+       }
+       quser->module_ctx.super.deinit_pre(user);
 }
 
 static void quota_clone_mail_user_deinit(struct mail_user *user)
 {
        struct quota_clone_user *quser = QUOTA_CLONE_USER_CONTEXT_REQUIRE(user);
 
+       /* wait once more, just in case something changed quota during
+          deinit_pre() */
+       dict_wait(quser->dict);
+       i_assert(quser->to_quota_flush == NULL);
        dict_deinit(&quser->dict);
        quser->module_ctx.super.deinit(user);
 }
@@ -239,6 +252,7 @@ static void quota_clone_mail_user_created(struct mail_user *user)
        quser = p_new(user->pool, struct quota_clone_user, 1);
        quser->module_ctx.super = *v;
        user->vlast = &quser->module_ctx.super;
+       v->deinit_pre = quota_clone_mail_user_deinit_pre;
        v->deinit = quota_clone_mail_user_deinit;
        quser->dict = dict;
        MODULE_CONTEXT_SET(user, quota_clone_user_module, quser);