changeset 12896:a8fe529ea72b

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.
author Timo Sirainen <tss@iki.fi>
date Sat, 30 Apr 2011 15:00:42 +0300
parents 5f0ed47db523
children 9949b7987409
files 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
diffstat 5 files changed, 78 insertions(+), 327 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/dbox-multi/mdbox-storage.c	Sat Apr 30 14:54:16 2011 +0300
+++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c	Sat Apr 30 15:00:42 2011 +0300
@@ -350,49 +350,6 @@
 	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 @@
 		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,
--- a/src/lib-storage/index/dbox-single/sdbox-storage.c	Sat Apr 30 14:54:16 2011 +0300
+++ b/src/lib-storage/index/dbox-single/sdbox-storage.c	Sat Apr 30 15:00:42 2011 +0300
@@ -263,46 +263,6 @@
 	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 @@
 		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,
--- a/src/lib-storage/index/index-storage.c	Sat Apr 30 14:54:16 2011 +0300
+++ b/src/lib-storage/index/index-storage.c	Sat Apr 30 15:00:42 2011 +0300
@@ -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,6 +197,25 @@
 	return 0;
 }
 
+static int index_storage_mailbox_alloc_index(struct mailbox *box)
+{
+	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);
+		return -1;
+	}
+
+	box->index = index_mailbox_alloc_index(box);
+	mail_index_set_fsync_mode(box->index,
+				  box->storage->set->parsed_fsync_mode, 0);
+	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);
@@ -208,17 +228,8 @@
 	if (move_to_memory)
 		ibox->index_flags &= ~MAIL_INDEX_OPEN_FLAG_CREATE;
 
-	if (mailbox_list_create_missing_index_dir(box->list, box->name) < 0) {
-		mail_storage_set_internal_error(box->storage);
+	if (index_storage_mailbox_alloc_index(box) < 0)
 		return -1;
-	}
-
-	box->index = index_mailbox_alloc_index(box);
-	mail_index_set_fsync_mode(box->index,
-				  box->storage->set->parsed_fsync_mode, 0);
-	mail_index_set_lock_method(box->index,
-		box->storage->set->parsed_lock_method,
-		mail_storage_get_lock_timeout(box->storage, -1U));
 
 	/* make sure mail_index_set_permissions() has been called */
 	(void)mailbox_get_permissions(box);
@@ -243,6 +254,15 @@
 		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 @@
 	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 @@
 	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 @@
 		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
--- a/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Sat Apr 30 14:54:16 2011 +0300
+++ b/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Sat Apr 30 15:00:42 2011 +0300
@@ -210,186 +210,6 @@
 }
 
 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 @@
 		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;
--- a/src/plugins/quota/quota-storage.c	Sat Apr 30 14:54:16 2011 +0300
+++ b/src/plugins/quota/quota-storage.c	Sat Apr 30 15:00:42 2011 +0300
@@ -338,49 +338,6 @@
 	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 @@
 	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);
 }