From: Timo Sirainen Date: Sat, 30 Apr 2011 12:00:42 +0000 (+0300) Subject: lib-storage: mailbox_delete() now always first expunges all mails before trying deletion. X-Git-Tag: 2.1.alpha1~263 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=af9edddbb704a640055898846add4e386e83fe43;p=thirdparty%2Fdovecot%2Fcore.git lib-storage: mailbox_delete() now always first expunges all mails before trying deletion. 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. --- diff --git a/src/lib-storage/index/dbox-multi/mdbox-storage.c b/src/lib-storage/index/dbox-multi/mdbox-storage.c index 9fc55bad01..4e667196bf 100644 --- a/src/lib-storage/index/dbox-multi/mdbox-storage.c +++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c @@ -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, diff --git a/src/lib-storage/index/dbox-single/sdbox-storage.c b/src/lib-storage/index/dbox-single/sdbox-storage.c index 6e8a2f8b67..6f6fffac53 100644 --- a/src/lib-storage/index/dbox-single/sdbox-storage.c +++ b/src/lib-storage/index/dbox-single/sdbox-storage.c @@ -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, diff --git a/src/lib-storage/index/index-storage.c b/src/lib-storage/index/index-storage.c index 379d68454e..1a2494259e 100644 --- a/src/lib-storage/index/index-storage.c +++ b/src/lib-storage/index/index-storage.c @@ -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 diff --git a/src/plugins/lazy-expunge/lazy-expunge-plugin.c b/src/plugins/lazy-expunge/lazy-expunge-plugin.c index 9e1cb69dbe..ff3deb4ac0 100644 --- a/src/plugins/lazy-expunge/lazy-expunge-plugin.c +++ b/src/plugins/lazy-expunge/lazy-expunge-plugin.c @@ -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; diff --git a/src/plugins/quota/quota-storage.c b/src/plugins/quota/quota-storage.c index 8738c323ee..fe7f8ebc3a 100644 --- a/src/plugins/quota/quota-storage.c +++ b/src/plugins/quota/quota-storage.c @@ -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); }