]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: mailbox_delete() now always first expunges all mails before trying deletion.
authorTimo Sirainen <tss@iki.fi>
Sat, 30 Apr 2011 12:00:42 +0000 (15:00 +0300)
committerTimo Sirainen <tss@iki.fi>
Sat, 30 Apr 2011 12:00:42 +0000 (15:00 +0300)
If any new mails are added during expunging, the mailbox deletion fails.
This behavior allows removing basically duplicated code where this is done
for mdbox, sdbox with external attachments, quota updates and lazy-expunge.

src/lib-storage/index/dbox-multi/mdbox-storage.c
src/lib-storage/index/dbox-single/sdbox-storage.c
src/lib-storage/index/index-storage.c
src/plugins/lazy-expunge/lazy-expunge-plugin.c
src/plugins/quota/quota-storage.c

index 9fc55bad01c919c1097cd0cd91d2cce161c8b11e..4e667196bf29e0978711acae8005944867911b7a 100644 (file)
@@ -350,49 +350,6 @@ mdbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update)
        return mdbox_write_index_header(box, update, NULL);
 }
 
-static int mdbox_mailbox_unref_mails(struct mdbox_mailbox *mbox)
-{
-       struct mdbox_map_atomic_context *atomic;
-       struct mdbox_map_transaction_context *map_trans;
-       const struct mail_index_header *hdr;
-       uint32_t seq, map_uid;
-       int ret = 0;
-
-       atomic = mdbox_map_atomic_begin(mbox->storage->map);
-       map_trans = mdbox_map_transaction_begin(atomic, FALSE);
-       hdr = mail_index_get_header(mbox->box.view);
-       for (seq = 1; seq <= hdr->messages_count; seq++) {
-               if (mdbox_mail_lookup(mbox, mbox->box.view, seq,
-                                     &map_uid) < 0) {
-                       ret = -1;
-                       break;
-               }
-
-               if (mdbox_map_update_refcount(map_trans, map_uid, -1) < 0) {
-                       ret = -1;
-                       break;
-               }
-       }
-
-       if (ret == 0)
-               ret = mdbox_map_transaction_commit(map_trans);
-       mdbox_map_transaction_free(&map_trans);
-       if (mdbox_map_atomic_finish(&atomic) < 0)
-               ret = -1;
-       return ret;
-}
-
-static int mdbox_mailbox_delete(struct mailbox *box)
-{
-       struct mdbox_mailbox *mbox = (struct mdbox_mailbox *)box;
-
-       if (box->opened) {
-               if (mdbox_mailbox_unref_mails(mbox) < 0)
-                       return -1;
-       }
-       return index_storage_mailbox_delete(box);
-}
-
 struct mail_storage mdbox_storage = {
        .name = MDBOX_STORAGE_NAME,
        .class_flags = MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT,
@@ -421,7 +378,7 @@ struct mailbox mdbox_mailbox = {
                index_storage_mailbox_free,
                dbox_mailbox_create,
                mdbox_mailbox_update,
-               mdbox_mailbox_delete,
+               index_storage_mailbox_delete,
                index_storage_mailbox_rename,
                index_storage_get_status,
                mdbox_mailbox_get_metadata,
index 6e8a2f8b677d17b30ebaaf04dfb0d2f7fc26e554..6f6fffac5370612d3945c39607cee534c44acc5d 100644 (file)
@@ -263,46 +263,6 @@ static void sdbox_mailbox_close(struct mailbox *box)
        index_storage_mailbox_close(box);
 }
 
-static int sdbox_mailbox_delete(struct mailbox *box)
-{
-       struct sdbox_mailbox *mbox = (struct sdbox_mailbox *)box;
-       struct mail_search_context *ctx;
-        struct mailbox_transaction_context *t;
-       struct mail *mail;
-       struct mail_search_args *search_args;
-       struct dbox_file *file;
-       struct sdbox_file *sfile;
-
-       if (!box->opened || mbox->storage->storage.attachment_dir == NULL)
-               return index_storage_mailbox_delete(box);
-
-       /* mark the mailbox deleted to avoid race conditions */
-       if (mailbox_mark_index_deleted(box, TRUE) < 0)
-               return -1;
-
-       /* ulink all dbox mails and their attachements in the mailbox. */
-       t = mailbox_transaction_begin(box, 0);
-
-       search_args = mail_search_build_init();
-       mail_search_build_add_all(search_args);
-       ctx = mailbox_search_init(t, search_args, NULL, 0, NULL);
-       mail_search_args_unref(&search_args);
-
-       while (mailbox_search_next(ctx, &mail)) {
-               file = sdbox_file_init(mbox, mail->uid);
-               sfile = (struct sdbox_file *)file;
-               (void)sdbox_file_unlink_with_attachments(sfile);
-               dbox_file_unref(&file);
-       }
-
-       if (mailbox_search_deinit(&ctx) < 0) {
-               /* maybe we missed some mails. oh well, can't help it. */
-       }
-       mailbox_transaction_rollback(&t);
-
-       return index_storage_mailbox_delete(box);
-}
-
 static int
 sdbox_mailbox_get_metadata(struct mailbox *box,
                           enum mailbox_metadata_items items,
@@ -374,7 +334,7 @@ struct mailbox sdbox_mailbox = {
                index_storage_mailbox_free,
                dbox_mailbox_create,
                dbox_mailbox_update,
-               sdbox_mailbox_delete,
+               index_storage_mailbox_delete,
                index_storage_mailbox_rename,
                index_storage_get_status,
                sdbox_mailbox_get_metadata,
index 379d68454e848248e9ee5247f70704ece7a226dd..1a2494259eec2311448ef5405da1fb058c98fba5 100644 (file)
@@ -11,6 +11,7 @@
 #include "mail-index-modseq.h"
 #include "mailbox-log.h"
 #include "mailbox-list-private.h"
+#include "mail-search-build.h"
 #include "index-storage.h"
 #include "index-mail.h"
 #include "index-attachment.h"
@@ -196,17 +197,10 @@ int index_storage_mailbox_exists_full(struct mailbox *box, const char *subdir,
        return 0;
 }
 
-int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory)
+static int index_storage_mailbox_alloc_index(struct mailbox *box)
 {
-       struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
-       enum mail_index_open_flags index_flags;
-       int ret;
-
-       i_assert(!box->opened);
-
-       index_flags = ibox->index_flags;
-       if (move_to_memory)
-               ibox->index_flags &= ~MAIL_INDEX_OPEN_FLAG_CREATE;
+       if (box->index != NULL)
+               return 0;
 
        if (mailbox_list_create_missing_index_dir(box->list, box->name) < 0) {
                mail_storage_set_internal_error(box->storage);
@@ -219,6 +213,23 @@ int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory)
        mail_index_set_lock_method(box->index,
                box->storage->set->parsed_lock_method,
                mail_storage_get_lock_timeout(box->storage, -1U));
+       return 0;
+}
+
+int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory)
+{
+       struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
+       enum mail_index_open_flags index_flags;
+       int ret;
+
+       i_assert(!box->opened);
+
+       index_flags = ibox->index_flags;
+       if (move_to_memory)
+               ibox->index_flags &= ~MAIL_INDEX_OPEN_FLAG_CREATE;
+
+       if (index_storage_mailbox_alloc_index(box) < 0)
+               return -1;
 
        /* make sure mail_index_set_permissions() has been called */
        (void)mailbox_get_permissions(box);
@@ -243,6 +254,15 @@ int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory)
                if (mail_index_is_in_memory(box->index)) {
                        mail_storage_set_critical(box->storage,
                                "Couldn't create index file");
+                       mail_index_close(box->index);
+                       return -1;
+               }
+       }
+
+       if ((box->flags & MAILBOX_FLAG_OPEN_DELETED) == 0) {
+               if (mail_index_is_deleted(box->index)) {
+                       mailbox_set_deleted(box);
+                       mail_index_close(box->index);
                        return -1;
                }
        }
@@ -261,13 +281,6 @@ int index_storage_mailbox_open(struct mailbox *box, bool move_to_memory)
        index_thread_mailbox_opened(box);
        if (hook_mailbox_opened != NULL)
                hook_mailbox_opened(box);
-
-       if ((box->flags & MAILBOX_FLAG_OPEN_DELETED) == 0) {
-               if (mail_index_is_deleted(box->index)) {
-                       mailbox_set_deleted(box);
-                       return -1;
-               }
-       }
        return 0;
 }
 
@@ -472,9 +485,36 @@ int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted)
        return 0;
 }
 
+static int mailbox_expunge_all_mails(struct mailbox *box)
+{
+       struct mail_search_context *ctx;
+        struct mailbox_transaction_context *t;
+       struct mail *mail;
+       struct mail_search_args *search_args;
+
+       (void)mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ);
+
+       t = mailbox_transaction_begin(box, 0);
+
+       search_args = mail_search_build_init();
+       mail_search_build_add_all(search_args);
+       ctx = mailbox_search_init(t, search_args, NULL, 0, NULL);
+       mail_search_args_unref(&search_args);
+
+       while (mailbox_search_next(ctx, &mail))
+               mail_expunge(mail);
+
+       if (mailbox_search_deinit(&ctx) < 0) {
+               mailbox_transaction_rollback(&t);
+               return -1;
+       }
+       return mailbox_transaction_commit(&t);
+}
+
 int index_storage_mailbox_delete(struct mailbox *box)
 {
        struct mailbox_metadata metadata;
+       struct mailbox_status status;
        int ret_guid;
 
        if (!box->opened) {
@@ -482,9 +522,28 @@ int index_storage_mailbox_delete(struct mailbox *box)
                return index_storage_mailbox_delete_dir(box, FALSE);
        }
 
+       /* we can't easily atomically delete all mails and the mailbox. so:
+          1) expunge all mails
+          2) mark the mailbox deleted (modifications after this will fail)
+          3) check if a race condition between 1) and 2) added any mails:
+            yes) abort and undelete mailbox
+            no) finish deleting the mailbox
+       */
+
+       if (mailbox_expunge_all_mails(box) < 0)
+               return -1;
        if (mailbox_mark_index_deleted(box, TRUE) < 0)
                return -1;
 
+       if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0)
+               return -1;
+       mailbox_get_open_status(box, STATUS_MESSAGES, &status);
+       if (status.messages != 0) {
+               mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS,
+                       "New mails were added to mailbox during deletion");
+               return -1;
+       }
+
        ret_guid = mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata);
 
        /* Make sure the indexes are closed before trying to delete the
index 9e1cb69dbe0da4b33b5f7da570f09ddd58229b58..ff3deb4ac0f7dae07b72bbd8bd158f8b301fd5b0 100644 (file)
@@ -209,186 +209,6 @@ static void lazy_expunge_mail_allocated(struct mail *_mail)
        MODULE_CONTEXT_SET_SELF(mail, lazy_expunge_mail_module, mmail);
 }
 
-static int
-mailbox_move(struct mailbox *src_box, struct mailbox_list *dest_list,
-            const char *wanted_destname, struct mailbox **dest_box_r)
-{
-       struct lazy_expunge_mailbox_list *src_llist =
-               LAZY_EXPUNGE_LIST_CONTEXT(src_box->list);
-       const char *dest_name = wanted_destname;
-       struct mailbox *dest_box;
-       const char *dir, *origin;
-       enum mail_error error;
-       mode_t file_mode, dir_mode;
-       gid_t gid;
-       int ret;
-
-       /* make sure the destination root directory exists */
-       mailbox_list_get_root_permissions(dest_list, &file_mode, &dir_mode,
-                                         &gid, &origin);
-       dir = mailbox_list_get_path(dest_list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
-       if (mkdir_parents_chgrp(dir, dir_mode, gid, origin) < 0 &&
-           errno != EEXIST) {
-               mail_storage_set_critical(src_box->storage,
-                       "mkdir_parents(%s) failed: %m", dir);
-               return -1;
-       }
-
-       for (;;) {
-               dest_box = mailbox_alloc(dest_list, dest_name,
-                                        MAILBOX_FLAG_OPEN_DELETED);
-
-               src_llist->allow_rename = TRUE;
-               ret = mailbox_rename(src_box, dest_box, FALSE);
-               src_llist->allow_rename = FALSE;
-
-               if (ret == 0)
-                       break;
-               mailbox_free(&dest_box);
-
-               error = mailbox_get_last_mail_error(src_box);
-               switch (error) {
-               case MAIL_ERROR_EXISTS:
-                       break;
-               case MAIL_ERROR_NOTFOUND:
-                       return 0;
-               default:
-                       return -1;
-               }
-
-               /* destination already exists. generate a different name. */
-               dest_name = t_strdup_printf("%s-%04u", wanted_destname,
-                                           (uint32_t)random());
-       }
-
-       *dest_box_r = dest_box;
-       return 1;
-}
-
-static int
-mailbox_move_all_mails(struct mailbox *src_box, const char *dest_name)
-{
-       struct mailbox *dest_box;
-       struct mail_search_args *search_args;
-        struct mailbox_transaction_context *src_trans, *dest_trans;
-       struct mail_search_context *search_ctx;
-       struct mail_save_context *save_ctx;
-       struct mail *mail;
-       const char *errstr;
-       int ret;
-
-       dest_box = mailbox_alloc(src_box->list, dest_name, 0);
-       if (mailbox_open(dest_box) < 0) {
-               errstr = mailbox_get_last_error(dest_box, NULL);
-               i_error("lazy_expunge: Couldn't open DELETE dest mailbox "
-                       "%s: %s", dest_name, errstr);
-               mailbox_free(&dest_box);
-               return -1;
-       }
-
-       src_trans = mailbox_transaction_begin(src_box, 0);
-       dest_trans = mailbox_transaction_begin(dest_box,
-                                       MAILBOX_TRANSACTION_FLAG_EXTERNAL);
-
-       search_args = mail_search_build_init();
-       mail_search_build_add_all(search_args);
-       search_ctx = mailbox_search_init(src_trans, search_args, NULL, 0, NULL);
-       mail_search_args_unref(&search_args);
-
-       while ((ret = mailbox_search_next(search_ctx, &mail)) > 0) {
-               save_ctx = mailbox_save_alloc(dest_trans);
-               mailbox_save_copy_flags(save_ctx, mail);
-               if (mailbox_copy(&save_ctx, mail) < 0) {
-                       if (!mail->expunged) {
-                               ret = -1;
-                               break;
-                       }
-               }
-       }
-
-       if (mailbox_search_deinit(&search_ctx) < 0)
-               ret = -1;
-
-       (void)mailbox_transaction_commit(&src_trans);
-       if (ret == 0)
-               ret = mailbox_transaction_commit(&dest_trans);
-       else
-               mailbox_transaction_rollback(&dest_trans);
-
-       if (ret == 0)
-               ret = mailbox_delete(src_box);
-
-       mailbox_free(&dest_box);
-       return ret;
-}
-
-static int mailbox_mark_index_undeleted(struct mailbox *box)
-{
-       struct mail_index_transaction *trans;
-
-       trans = mail_index_transaction_begin(box->view,
-                               MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
-       mail_index_set_undeleted(trans);
-       if (mail_index_transaction_commit(&trans) < 0) {
-               mail_storage_set_index_error(box);
-               return -1;
-       }
-       return 0;
-}
-
-static int lazy_expunge_mailbox_delete(struct mailbox *box)
-{
-       struct lazy_expunge_mail_user *luser =
-               LAZY_EXPUNGE_USER_CONTEXT(box->storage->user);
-       union mailbox_module_context *lbox =
-               LAZY_EXPUNGE_CONTEXT(box);
-       struct lazy_expunge_mailbox_list *llist =
-               LAZY_EXPUNGE_LIST_CONTEXT(box->list);
-       struct mailbox *expunge_box;
-       const char *str;
-       int ret;
-
-       if (llist->internal_namespace || !box->opened) {
-               /* a) deleting mailbox from lazy_expunge namespaces
-                  b) deleting a \noselect mailbox */
-               return lbox->super.delete(box);
-       }
-
-       /* avoid potential race conditions by marking it deleted */
-       if (mailbox_mark_index_deleted(box, TRUE) < 0)
-               return -1;
-
-       /* rename it into the lazy_expunge namespace */
-       ret = mailbox_move(box, luser->lazy_ns->list, box->name, &expunge_box);
-       if (ret < 0)
-               return -1;
-       if (ret == 0) {
-               mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
-                       T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
-               return -1;
-       }
-
-       /* other sessions now see the mailbox completely deleted.
-          since it's not really deleted in the lazy-expunge namespace,
-          we might want to change it again. so mark the index undeleted. */
-       if (mailbox_open(expunge_box) < 0) {
-               str = mailbox_get_last_error(expunge_box, NULL);
-               i_error("lazy_expunge: Couldn't open DELETEd mailbox "
-                       "%s: %s", expunge_box->name, str);
-               mailbox_free(&expunge_box);
-               return -1;
-       }
-       if (mailbox_mark_index_undeleted(expunge_box) < 0) {
-               mailbox_free(&expunge_box);
-               return -1;
-       }
-
-       if (strcmp(expunge_box->name, box->name) != 0)
-               ret = mailbox_move_all_mails(expunge_box, box->name);
-       mailbox_free(&expunge_box);
-       return ret < 0 ? -1 : 0;
-}
-
 static int
 lazy_expunge_mailbox_rename(struct mailbox *src, struct mailbox *dest,
                            bool rename_children)
@@ -428,7 +248,6 @@ static void lazy_expunge_mailbox_allocated(struct mailbox *box)
                v->transaction_begin = lazy_expunge_transaction_begin;
                v->transaction_commit = lazy_expunge_transaction_commit;
                v->transaction_rollback = lazy_expunge_transaction_rollback;
-               v->delete = lazy_expunge_mailbox_delete;
                v->rename = lazy_expunge_mailbox_rename;
        } else {
                v->rename = lazy_expunge_mailbox_rename;
index 8738c323ee153be0cc18f97107e81909d614c52f..fe7f8ebc3a4acebd862da8a4206a7b2a33a90069 100644 (file)
@@ -338,49 +338,6 @@ static void quota_mailbox_close(struct mailbox *box)
        qbox->module_ctx.super.close(box);
 }
 
-static int
-quota_mailbox_delete_shrink_quota(struct mailbox *box)
-{
-       struct mail_search_context *ctx;
-        struct mailbox_transaction_context *t;
-       struct quota_transaction_context *qt;
-       struct mail *mail;
-       struct mail_search_args *search_args;
-
-       if (mailbox_mark_index_deleted(box, TRUE) < 0)
-               return -1;
-
-       t = mailbox_transaction_begin(box, 0);
-       qt = quota_transaction_begin(box);
-
-       search_args = mail_search_build_init();
-       mail_search_build_add_all(search_args);
-       ctx = mailbox_search_init(t, search_args, NULL, 0, NULL);
-       mail_search_args_unref(&search_args);
-
-       while (mailbox_search_next(ctx, &mail))
-               quota_free(qt, mail);
-
-       if (mailbox_search_deinit(&ctx) < 0) {
-               /* maybe we missed some mails. */
-               quota_recalculate(qt);
-       }
-       (void)quota_transaction_commit(&qt);
-       mailbox_transaction_rollback(&t);
-       return 0;
-}
-
-static int quota_mailbox_delete(struct mailbox *box)
-{
-       struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
-
-       if (box->opened) {
-               if (quota_mailbox_delete_shrink_quota(box) < 0)
-                       return -1;
-       }
-       return qbox->module_ctx.super.delete(box);
-}
-
 static void quota_mailbox_free(struct mailbox *box)
 {
        struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
@@ -419,7 +376,6 @@ void quota_mailbox_allocated(struct mailbox *box)
        v->sync_notify = quota_mailbox_sync_notify;
        v->sync_deinit = quota_mailbox_sync_deinit;
        v->close = quota_mailbox_close;
-       v->delete = quota_mailbox_delete;
        v->free = quota_mailbox_free;
        MODULE_CONTEXT_SET(box, quota_storage_module, qbox);
 }