changeset 10711:292562f9b12c HEAD

lib-storage: Mailbox renaming API changed.
author Timo Sirainen <tss@iki.fi>
date Sun, 14 Feb 2010 22:32:59 +0200
parents 3d7fb69184b3
children 38897b223957
files src/dsync/dsync-worker-local.c src/imap/cmd-rename.c src/lib-storage/index/cydir/cydir-storage.c src/lib-storage/index/dbox-common/dbox-storage.c src/lib-storage/index/dbox-common/dbox-storage.h 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/lib-storage/index/index-storage.h src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/index/raw/raw-storage.c src/lib-storage/index/shared/shared-list.c src/lib-storage/list/mailbox-list-fs.c src/lib-storage/list/mailbox-list-maildir.c src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/lib-storage/mailbox-list-private.h src/lib-storage/mailbox-list.c src/lib-storage/mailbox-list.h src/lib-storage/test-mailbox.c src/plugins/lazy-expunge/lazy-expunge-plugin.c src/plugins/virtual/virtual-storage.c
diffstat 24 files changed, 323 insertions(+), 441 deletions(-) [+]
line wrap: on
line diff
--- a/src/dsync/dsync-worker-local.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/dsync/dsync-worker-local.c	Sun Feb 14 22:32:59 2010 +0200
@@ -1136,6 +1136,7 @@
 		(struct local_dsync_worker *)_worker;
 	struct local_dsync_mailbox *lbox;
 	struct mailbox_list *list;
+	struct mailbox *old_box, *new_box;
 	const char *newname;
 
 	lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
@@ -1156,14 +1157,19 @@
 	}
 
 	mailbox_list_set_changelog_timestamp(list, dsync_box->last_change);
-	if (mailbox_list_rename_mailbox(list, lbox->storage_name,
-					list, newname, FALSE) < 0) {
+	old_box = mailbox_alloc(list, lbox->storage_name, 0);
+	new_box = mailbox_alloc(list, newname, 0);
+	if (mailbox_rename(old_box, new_box, FALSE) < 0) {
+		struct mail_storage *storage = mailbox_get_storage(old_box);
+
 		i_error("Can't rename mailbox %s to %s: %s", lbox->storage_name,
-			newname, mailbox_list_get_last_error(list, NULL));
+			newname, mail_storage_get_last_error(storage, NULL));
 		dsync_worker_set_failure(_worker);
 	} else {
 		lbox->storage_name = p_strdup(worker->pool, newname);
 	}
+	mailbox_free(&old_box);
+	mailbox_free(&new_box);
 	mailbox_list_set_changelog_timestamp(list, (time_t)-1);
 }
 
--- a/src/imap/cmd-rename.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/imap/cmd-rename.c	Sun Feb 14 22:32:59 2010 +0200
@@ -7,6 +7,7 @@
 bool cmd_rename(struct client_command_context *cmd)
 {
 	struct mail_namespace *old_ns, *new_ns;
+	struct mailbox *old_box, *new_box;
 	const char *oldname, *newname;
 	unsigned int oldlen;
 
@@ -37,10 +38,13 @@
 		}
 	}
 
-	if (mailbox_list_rename_mailbox(old_ns->list, oldname,
-					new_ns->list, newname, TRUE) < 0)
-		client_send_list_error(cmd, old_ns->list);
+	old_box = mailbox_alloc(old_ns->list, oldname, 0);
+	new_box = mailbox_alloc(new_ns->list, newname, 0);
+	if (mailbox_rename(old_box, new_box, TRUE) < 0)
+		client_send_storage_error(cmd, mailbox_get_storage(old_box));
 	else
 		client_send_tagline(cmd, "OK Rename completed.");
+	mailbox_free(&old_box);
+	mailbox_free(&new_box);
 	return TRUE;
 }
--- a/src/lib-storage/index/cydir/cydir-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/cydir/cydir-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -203,6 +203,7 @@
 		cydir_mailbox_create,
 		index_storage_mailbox_update,
 		index_storage_mailbox_delete,
+		index_storage_mailbox_rename,
 		index_storage_get_status,
 		NULL,
 		NULL,
--- a/src/lib-storage/index/dbox-common/dbox-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/dbox-common/dbox-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -124,26 +124,6 @@
 	}
 }
 
-static const char *
-dbox_get_alt_path(struct mailbox_list *list, const char *path)
-{
-	struct mail_storage *storage = list->ns->storage;
-	const char *root;
-	unsigned int len;
-
-	if (list->set.alt_dir == NULL ||
-	    (storage->class_flags & MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT) != 0)
-		return NULL;
-
-	root = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
-	len = strlen(root);
-	if (strncmp(path, root, len) != 0 && path[len] == '/') {
-		/* can't determine the alt path - shouldn't happen */
-		return NULL;
-	}
-	return t_strconcat(list->set.alt_dir, path + len, NULL);
-}
-
 int dbox_mailbox_create(struct mailbox *box,
 			const struct mailbox_update *update, bool directory)
 {
@@ -226,94 +206,3 @@
 	}
 	return ret;
 }
-
-static int
-dbox_list_rename_get_alt_paths(struct mailbox_list *oldlist,
-			       const char *oldname,
-			       struct mailbox_list *newlist,
-			       const char *newname,
-			       enum mailbox_list_path_type path_type,
-			       const char **oldpath_r, const char **newpath_r)
-{
-	const char *path;
-
-	path = mailbox_list_get_path(oldlist, oldname, path_type);
-	*oldpath_r = dbox_get_alt_path(oldlist, path);
-	if (*oldpath_r == NULL)
-		return 0;
-
-	path = mailbox_list_get_path(newlist, newname, path_type);
-	*newpath_r = dbox_get_alt_path(newlist, path);
-	if (*newpath_r == NULL) {
-		/* destination dbox storage doesn't have alt-path defined.
-		   we can't do the rename easily. */
-		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
-			"Can't rename mailboxes across specified storages.");
-		return -1;
-	}
-	return 1;
-}
-
-int dbox_list_rename_mailbox_pre(struct mailbox_list *oldlist,
-				 const char *oldname,
-				 struct mailbox_list *newlist,
-				 const char *newname)
-{
-	const char *alt_oldpath, *alt_newpath;
-	struct stat st;
-	int ret;
-
-	ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname,
-					     MAILBOX_LIST_PATH_TYPE_DIR,
-					     &alt_oldpath, &alt_newpath);
-	if (ret <= 0)
-		return ret;
-
-	if (stat(alt_newpath, &st) == 0) {
-		/* race condition or a directory left there lying around?
-		   safest to just report error. */
-		mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
-				       "Target mailbox already exists");
-		return -1;
-	} else if (errno != ENOENT) {
-		mailbox_list_set_critical(oldlist, "stat(%s) failed: %m",
-					  alt_newpath);
-		return -1;
-	}
-	return 0;
-}
-
-int dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
-			     struct mailbox_list *newlist, const char *newname,
-			     bool rename_children)
-{
-	enum mailbox_list_path_type path_type;
-	const char *alt_oldpath, *alt_newpath, *path;
-	int ret;
-
-	path_type = rename_children ? MAILBOX_LIST_PATH_TYPE_DIR :
-		MAILBOX_LIST_PATH_TYPE_MAILBOX;
-	ret = dbox_list_rename_get_alt_paths(oldlist, oldname, newlist, newname,
-					     path_type, &alt_oldpath,
-					     &alt_newpath);
-	if (ret <= 0)
-		return ret;
-
-	if (rename(alt_oldpath, alt_newpath) == 0) {
-		/* ok */
-		if (!rename_children) {
-			path = mailbox_list_get_path(oldlist, oldname,
-						     MAILBOX_LIST_PATH_TYPE_DIR);
-			if (rmdir(path) < 0 &&
-			    errno != ENOENT && errno != ENOTEMPTY) {
-				mailbox_list_set_critical(oldlist,
-					"rmdir(%s) failed: %m", path);
-			}
-		}
-	} else if (errno != ENOENT) {
-		/* renaming is done already, so just log the error */
-		mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
-					  alt_oldpath, alt_newpath);
-	}
-	return 0;
-}
--- a/src/lib-storage/index/dbox-common/dbox-storage.h	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/dbox-common/dbox-storage.h	Sun Feb 14 22:32:59 2010 +0200
@@ -54,12 +54,5 @@
 			      const char *mailbox_name,
 			      enum mailbox_list_file_type type,
 			      enum mailbox_info_flags *flags);
-int dbox_list_rename_mailbox_pre(struct mailbox_list *oldlist,
-				 const char *oldname,
-				 struct mailbox_list *newlist,
-				 const char *newname);
-int dbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
-			     struct mailbox_list *newlist, const char *newname,
-			     bool rename_children);
 
 #endif
--- a/src/lib-storage/index/dbox-multi/mdbox-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -313,21 +313,6 @@
 	return index_storage_mailbox_delete(box);
 }
 
-static int
-mdbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
-			  struct mailbox_list *newlist, const char *newname,
-			  bool rename_children)
-{
-	struct mdbox_mailbox_list *oldmlist = MDBOX_LIST_CONTEXT(oldlist);
-
-	if (oldmlist->module_ctx.super.
-	    		rename_mailbox(oldlist, oldname, newlist, newname,
-				       rename_children) < 0)
-		return -1;
-	return dbox_list_rename_mailbox(oldlist, oldname, newlist, newname,
-					rename_children);
-}
-
 static void dbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
 				  struct mailbox_list *list)
 {
@@ -335,10 +320,7 @@
 
 	mlist = p_new(list->pool, struct mdbox_mailbox_list, 1);
 	mlist->module_ctx.super = list->v;
-
 	list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
-	list->v.rename_mailbox = mdbox_list_rename_mailbox;
-	list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre;
 
 	MODULE_CONTEXT_SET(list, mdbox_mailbox_list_module, mlist);
 }
@@ -370,6 +352,7 @@
 		dbox_mailbox_create,
 		mdbox_mailbox_update,
 		mdbox_mailbox_delete,
+		index_storage_mailbox_rename,
 		index_storage_get_status,
 		mdbox_mailbox_get_guid,
 		NULL,
--- a/src/lib-storage/index/dbox-single/sdbox-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/dbox-single/sdbox-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -203,21 +203,6 @@
 	return sdbox_write_index_header(box, update);
 }
 
-static int
-sdbox_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
-			  struct mailbox_list *newlist, const char *newname,
-			  bool rename_children)
-{
-	struct sdbox_mailbox_list *oldmlist = SDBOX_LIST_CONTEXT(oldlist);
-
-	if (oldmlist->module_ctx.super.
-	    		rename_mailbox(oldlist, oldname, newlist, newname,
-				       rename_children) < 0)
-		return -1;
-	return dbox_list_rename_mailbox(oldlist, oldname, newlist, newname,
-					rename_children);
-}
-
 static void sdbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED,
 				   struct mailbox_list *list)
 {
@@ -225,10 +210,7 @@
 
 	mlist = p_new(list->pool, struct sdbox_mailbox_list, 1);
 	mlist->module_ctx.super = list->v;
-
 	list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
-	list->v.rename_mailbox = sdbox_list_rename_mailbox;
-	list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre;
 
 	MODULE_CONTEXT_SET(list, sdbox_mailbox_list_module, mlist);
 }
@@ -260,6 +242,7 @@
 		dbox_mailbox_create,
 		dbox_mailbox_update,
 		index_storage_mailbox_delete,
+		index_storage_mailbox_rename,
 		index_storage_get_status,
 		sdbox_mailbox_get_guid,
 		NULL,
--- a/src/lib-storage/index/index-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/index-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -475,6 +475,23 @@
 	return index_storage_mailbox_delete_dir(box, TRUE);
 }
 
+int index_storage_mailbox_rename(struct mailbox *src, struct mailbox *dest,
+				 bool rename_children)
+{
+	uint8_t guid[MAIL_GUID_128_SIZE];
+
+	if (src->list->v.rename_mailbox(src->list, src->name,
+					dest->list, dest->name,
+					rename_children) < 0)
+		return -1;
+
+	/* we'll track mailbox names, instead of GUIDs. We may be renaming a
+	   non-selectable mailbox (directory), which doesn't even have a GUID */
+	mailbox_name_get_sha128(dest->name, guid);
+	mailbox_list_add_change(src->list, MAILBOX_LOG_RECORD_RENAME, guid);
+	return 0;
+}
+
 bool index_storage_is_readonly(struct mailbox *box)
 {
 	return (box->flags & MAILBOX_FLAG_READONLY) != 0 ||
--- a/src/lib-storage/index/index-storage.h	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/index-storage.h	Sun Feb 14 22:32:59 2010 +0200
@@ -77,6 +77,8 @@
 				 const struct mailbox_update *update);
 int index_storage_mailbox_delete(struct mailbox *box);
 int index_storage_mailbox_delete_dir(struct mailbox *box, bool mailbox_deleted);
+int index_storage_mailbox_rename(struct mailbox *src, struct mailbox *dest,
+				 bool rename_children);
 
 bool index_storage_is_readonly(struct mailbox *box);
 bool index_storage_allow_new_keywords(struct mailbox *box);
--- a/src/lib-storage/index/maildir/maildir-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -24,12 +24,6 @@
 	const struct maildir_settings *set;
 };
 
-struct rename_context {
-	bool found;
-	size_t oldnamelen;
-	const char *newname;
-};
-
 extern struct mail_storage maildir_storage;
 extern struct mailbox maildir_mailbox;
 
@@ -507,34 +501,6 @@
 	return maildir_uidlist_get_mailbox_guid(mbox->uidlist, guid);
 }
 
-static int
-maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
-			    struct mailbox_list *newlist, const char *newname,
-			    bool rename_children)
-{
-	struct maildir_mailbox_list_context *oldmlist =
-		MAILDIR_LIST_CONTEXT(oldlist);
-	const char *path1, *path2;
-
-	if (strcmp(oldname, "INBOX") == 0) {
-		/* INBOX often exists as the root ~/Maildir.
-		   We can't rename it then. */
-		path1 = mailbox_list_get_path(oldlist, oldname,
-					      MAILBOX_LIST_PATH_TYPE_MAILBOX);
-		path2 = mailbox_list_get_path(oldlist, NULL,
-					      MAILBOX_LIST_PATH_TYPE_MAILBOX);
-		if (strcmp(path1, path2) == 0) {
-			mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
-				"Renaming INBOX isn't supported.");
-			return -1;
-		}
-	}
-
-	return oldmlist->module_ctx.super.
-		rename_mailbox(oldlist, oldname, newlist, newname,
-			       rename_children);
-}
-
 static void maildir_mailbox_close(struct mailbox *box)
 {
 	struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
@@ -773,7 +739,6 @@
 			maildir_storage_is_valid_create_name;
 		list->v.iter_is_mailbox = maildir_list_iter_is_mailbox;
 	}
-	list->v.rename_mailbox = maildir_list_rename_mailbox;
 	MODULE_CONTEXT_SET(list, maildir_mailbox_list_module, mlist);
 }
 
@@ -804,6 +769,7 @@
 		maildir_mailbox_create,
 		maildir_mailbox_update,
 		index_storage_mailbox_delete,
+		index_storage_mailbox_rename,
 		index_storage_get_status,
 		maildir_mailbox_get_guid,
 		maildir_list_index_has_changed,
--- a/src/lib-storage/index/mbox/mbox-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -792,6 +792,7 @@
 		mbox_mailbox_create,
 		mbox_mailbox_update,
 		index_storage_mailbox_delete,
+		index_storage_mailbox_rename,
 		index_storage_get_status,
 		mbox_mailbox_get_guid,
 		NULL,
--- a/src/lib-storage/index/raw/raw-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/raw/raw-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -185,6 +185,7 @@
 		raw_mailbox_create,
 		raw_mailbox_update,
 		index_storage_mailbox_delete,
+		index_storage_mailbox_rename,
 		index_storage_get_status,
 		NULL,
 		NULL,
--- a/src/lib-storage/index/shared/shared-list.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/index/shared/shared-list.c	Sun Feb 14 22:32:59 2010 +0200
@@ -306,27 +306,9 @@
 	if (shared_list_rename_get_ns(oldlist, &oldname,
 				      newlist, &newname, &ns) < 0)
 		return -1;
-	ret = mailbox_list_rename_mailbox(ns->list, oldname, ns->list, newname,
-					  rename_children);
-	if (ret < 0)
-		shared_list_copy_error(oldlist, ns);
-	return ret;
-}
 
-static int
-shared_list_rename_mailbox_pre(struct mailbox_list *oldlist,
-			       const char *oldname,
-			       struct mailbox_list *newlist,
-			       const char *newname)
-{
-	struct mail_namespace *ns;
-	int ret;
-
-	if (shared_list_rename_get_ns(oldlist, &oldname,
-				      newlist, &newname, &ns) < 0)
-		return -1;
-	ret = ns->list->v.rename_mailbox_pre(ns->list, oldname,
-					     ns->list, newname);
+	ret = ns->list->v.rename_mailbox(ns->list, oldname, ns->list, newname,
+					 rename_children);
 	if (ret < 0)
 		shared_list_copy_error(oldlist, ns);
 	return ret;
@@ -358,7 +340,6 @@
 		shared_list_create_mailbox_dir,
 		shared_list_delete_mailbox,
 		shared_list_delete_dir,
-		shared_list_rename_mailbox,
-		shared_list_rename_mailbox_pre
+		shared_list_rename_mailbox
 	}
 };
--- a/src/lib-storage/list/mailbox-list-fs.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/list/mailbox-list-fs.c	Sun Feb 14 22:32:59 2010 +0200
@@ -471,8 +471,9 @@
 				  const char *newname, bool rename_children)
 {
 	struct mail_storage *oldstorage;
-	const char *oldpath, *newpath, *p, *origin;
-	enum mailbox_list_path_type path_type;
+	const char *oldpath, *newpath, *alt_oldpath, *alt_newpath, *root_path;
+	const char *p, *origin;
+	enum mailbox_list_path_type path_type, alt_path_type;
 	struct stat st;
 	mode_t mode;
 	gid_t gid;
@@ -481,12 +482,14 @@
 	if (mailbox_list_get_storage(&oldlist, &oldname, &oldstorage) < 0)
 		return -1;
 
-	if (rename_children)
+	if (rename_children) {
 		path_type = MAILBOX_LIST_PATH_TYPE_DIR;
-	else if (mail_storage_is_mailbox_file(oldstorage) ||
-		 *oldlist->set.maildir_name != '\0')
+		alt_path_type = MAILBOX_LIST_PATH_TYPE_ALT_DIR;
+	} else if (mail_storage_is_mailbox_file(oldstorage) ||
+		   *oldlist->set.maildir_name != '\0') {
 		path_type = MAILBOX_LIST_PATH_TYPE_MAILBOX;
-	else {
+		alt_path_type = MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX;
+	} else {
 		/* we can't do this, our children would get renamed with us */
 		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
 			"Can't rename mailbox without its children.");
@@ -495,6 +498,18 @@
 
 	oldpath = mailbox_list_get_path(oldlist, oldname, path_type);
 	newpath = mailbox_list_get_path(newlist, newname, path_type);
+	alt_oldpath = mailbox_list_get_path(oldlist, oldname, alt_path_type);
+	alt_newpath = mailbox_list_get_path(newlist, newname, alt_path_type);
+
+	root_path = mailbox_list_get_path(oldlist, NULL,
+					  MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (strcmp(oldpath, root_path) == 0) {
+		/* most likely INBOX */
+		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+			t_strdup_printf("Renaming %s isn't supported.",
+					oldname));
+		return -1;
+	}
 
 	/* create the hierarchy */
 	p = strrchr(newpath, '/');
@@ -531,14 +546,30 @@
 		return -1;
 	}
 
-	if (oldlist->v.rename_mailbox_pre != NULL) {
-		if (oldlist->v.rename_mailbox_pre(oldlist, oldname,
-						  newlist, newname) < 0)
-			return -1;
+	if ((alt_newpath != NULL && alt_oldpath == NULL) ||
+	    (alt_newpath == NULL && alt_oldpath != NULL)) {
+		/* both or neither source/dest must to have alt path defined.
+		   otherwise we'd have to do the merging ourself, which would
+		   be possible but a bit too much trouble for now */
+		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+			"Can't rename mailboxes across specified storages.");
+		return -1;
 	}
 
-	/* NOTE: renaming INBOX works just fine with us, it's simply recreated
-	   the next time it's needed. */
+	if (alt_newpath != NULL) {
+		if (stat(alt_newpath, &st) == 0) {
+			/* race condition or a directory left there lying around?
+			   safest to just report error. */
+			mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
+					       "Target mailbox already exists");
+			return -1;
+		} else if (errno != ENOENT) {
+			mailbox_list_set_critical(oldlist, "stat(%s) failed: %m",
+						  alt_newpath);
+			return -1;
+		}
+	}
+
 	if (rename(oldpath, newpath) < 0) {
 		if (ENOTFOUND(errno)) {
 			mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
@@ -563,6 +594,10 @@
 		}
 	}
 
+	if (alt_newpath != NULL) {
+		(void)rename_dir(oldlist, oldname, newlist, newname,
+				 alt_path_type, rmdir_parent);
+	}
 	(void)rename_dir(oldlist, oldname, newlist, newname,
 			 MAILBOX_LIST_PATH_TYPE_CONTROL, rmdir_parent);
 	(void)rename_dir(oldlist, oldname, newlist, newname,
@@ -596,7 +631,6 @@
 		fs_list_create_mailbox_dir,
 		fs_list_delete_mailbox,
 		fs_list_delete_dir,
-		fs_list_rename_mailbox,
-		NULL
+		fs_list_rename_mailbox
 	}
 };
--- a/src/lib-storage/list/mailbox-list-maildir.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/list/mailbox-list-maildir.c	Sun Feb 14 22:32:59 2010 +0200
@@ -289,116 +289,6 @@
 				       name, set);
 }
 
-static int rename_dir(struct mailbox_list *oldlist, const char *oldname,
-		      struct mailbox_list *newlist, const char *newname,
-		      enum mailbox_list_path_type type)
-{
-	const char *oldpath, *newpath;
-
-	oldpath = mailbox_list_get_path(oldlist, oldname, type);
-	newpath = mailbox_list_get_path(newlist, newname, type);
-
-	if (strcmp(oldpath, newpath) == 0)
-		return 0;
-
-	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
-		mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
-					  oldpath, newpath);
-		return -1;
-	}
-	return 0;
-}
-
-static int
-maildir_rename_children(struct mailbox_list *oldlist, const char *oldname,
-			struct mailbox_list *newlist, const char *newname)
-{
-	struct mailbox_list_iterate_context *iter;
-        const struct mailbox_info *info;
-	ARRAY_DEFINE(names_arr, const char *);
-	const char *pattern, *oldpath, *newpath, *old_listname, *new_listname;
-	const char *const *names;
-	unsigned int i, count;
-	size_t oldnamelen;
-	pool_t pool;
-	char old_sep;
-	int ret;
-
-	ret = 0;
-	oldnamelen = strlen(oldname);
-
-	/* first get the list of the children and save them to memory, because
-	   we can't rely on readdir() not skipping files while the directory
-	   is being modified. this doesn't protect against modifications by
-	   other processes though. */
-	pool = pool_alloconly_create("Maildir++ children list", 1024);
-	i_array_init(&names_arr, 64);
-
-	old_sep = mailbox_list_get_hierarchy_sep(oldlist);
-	pattern = t_strdup_printf("%s%c*", oldname, old_sep);
-	iter = mailbox_list_iter_init(oldlist, pattern,
-				      MAILBOX_LIST_ITER_RETURN_NO_FLAGS |
-				      MAILBOX_LIST_ITER_RAW_LIST);
-	while ((info = mailbox_list_iter_next(iter)) != NULL) {
-		const char *name;
-
-		/* verify that the prefix matches, otherwise we could have
-		   problems with mailbox names containing '%' and '*' chars */
-		if (strncmp(info->name, oldname, oldnamelen) == 0 &&
-		    info->name[oldnamelen] == old_sep) {
-			name = p_strdup(pool, info->name + oldnamelen);
-			array_append(&names_arr, &name, 1);
-		}
-	}
-	if (mailbox_list_iter_deinit(&iter) < 0) {
-		ret = -1;
-		names = NULL; count = 0;
-	} else {
-		names = array_get(&names_arr, &count);
-	}
-
-	for (i = 0; i < count; i++) {
-		old_listname = t_strconcat(oldname, names[i], NULL);
-		if (strcmp(old_listname, newname) == 0) {
-			/* When doing RENAME "a" "a.b" we see "a.b" here.
-			   We don't want to rename it anymore to "a.b.b". */
-			continue;
-		}
-
-		new_listname = t_strconcat(newname, names[i], NULL);
-		oldpath = mailbox_list_get_path(oldlist, old_listname,
-						MAILBOX_LIST_PATH_TYPE_MAILBOX);
-		newpath = mailbox_list_get_path(newlist, new_listname,
-						MAILBOX_LIST_PATH_TYPE_MAILBOX);
-
-		/* FIXME: it's possible to merge two mailboxes if either one of
-		   them doesn't have existing root mailbox. We could check this
-		   but I'm not sure if it's worth it. It could be even
-		   considered as a feature.
-
-		   Anyway, the bug with merging is that if both mailboxes have
-		   identically named child mailbox they conflict. Just ignore
-		   those and leave them under the old mailbox. */
-		if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
-			ret = 1;
-		else {
-			mailbox_list_set_critical(oldlist,
-				"rename(%s, %s) failed: %m", oldpath, newpath);
-			ret = -1;
-			break;
-		}
-
-		(void)rename_dir(oldlist, old_listname, newlist, new_listname,
-				 MAILBOX_LIST_PATH_TYPE_CONTROL);
-		(void)rename_dir(oldlist, old_listname, newlist, new_listname,
-				 MAILBOX_LIST_PATH_TYPE_INDEX);
-	}
-	array_free(&names_arr);
-	pool_unref(&pool);
-
-	return ret;
-}
-
 static int
 maildir_list_create_maildirfolder_file(struct mailbox_list *list,
 				       const char *dir)
@@ -547,12 +437,122 @@
 	return -1;
 }
 
+static int rename_dir(struct mailbox_list *oldlist, const char *oldname,
+		      struct mailbox_list *newlist, const char *newname,
+		      enum mailbox_list_path_type type)
+{
+	const char *oldpath, *newpath;
+
+	oldpath = mailbox_list_get_path(oldlist, oldname, type);
+	newpath = mailbox_list_get_path(newlist, newname, type);
+
+	if (strcmp(oldpath, newpath) == 0)
+		return 0;
+
+	if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
+		mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
+					  oldpath, newpath);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+maildir_rename_children(struct mailbox_list *oldlist, const char *oldname,
+			struct mailbox_list *newlist, const char *newname)
+{
+	struct mailbox_list_iterate_context *iter;
+        const struct mailbox_info *info;
+	ARRAY_DEFINE(names_arr, const char *);
+	const char *pattern, *oldpath, *newpath, *old_listname, *new_listname;
+	const char *const *names;
+	unsigned int i, count;
+	size_t oldnamelen;
+	pool_t pool;
+	char old_sep;
+	int ret;
+
+	ret = 0;
+	oldnamelen = strlen(oldname);
+
+	/* first get the list of the children and save them to memory, because
+	   we can't rely on readdir() not skipping files while the directory
+	   is being modified. this doesn't protect against modifications by
+	   other processes though. */
+	pool = pool_alloconly_create("Maildir++ children list", 1024);
+	i_array_init(&names_arr, 64);
+
+	old_sep = mailbox_list_get_hierarchy_sep(oldlist);
+	pattern = t_strdup_printf("%s%c*", oldname, old_sep);
+	iter = mailbox_list_iter_init(oldlist, pattern,
+				      MAILBOX_LIST_ITER_RETURN_NO_FLAGS |
+				      MAILBOX_LIST_ITER_RAW_LIST);
+	while ((info = mailbox_list_iter_next(iter)) != NULL) {
+		const char *name;
+
+		/* verify that the prefix matches, otherwise we could have
+		   problems with mailbox names containing '%' and '*' chars */
+		if (strncmp(info->name, oldname, oldnamelen) == 0 &&
+		    info->name[oldnamelen] == old_sep) {
+			name = p_strdup(pool, info->name + oldnamelen);
+			array_append(&names_arr, &name, 1);
+		}
+	}
+	if (mailbox_list_iter_deinit(&iter) < 0) {
+		ret = -1;
+		names = NULL; count = 0;
+	} else {
+		names = array_get(&names_arr, &count);
+	}
+
+	for (i = 0; i < count; i++) {
+		old_listname = t_strconcat(oldname, names[i], NULL);
+		if (strcmp(old_listname, newname) == 0) {
+			/* When doing RENAME "a" "a.b" we see "a.b" here.
+			   We don't want to rename it anymore to "a.b.b". */
+			continue;
+		}
+
+		new_listname = t_strconcat(newname, names[i], NULL);
+		oldpath = mailbox_list_get_path(oldlist, old_listname,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
+		newpath = mailbox_list_get_path(newlist, new_listname,
+						MAILBOX_LIST_PATH_TYPE_MAILBOX);
+
+		/* FIXME: it's possible to merge two mailboxes if either one of
+		   them doesn't have existing root mailbox. We could check this
+		   but I'm not sure if it's worth it. It could be even
+		   considered as a feature.
+
+		   Anyway, the bug with merging is that if both mailboxes have
+		   identically named child mailbox they conflict. Just ignore
+		   those and leave them under the old mailbox. */
+		if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
+			ret = 1;
+		else {
+			mailbox_list_set_critical(oldlist,
+				"rename(%s, %s) failed: %m", oldpath, newpath);
+			ret = -1;
+			break;
+		}
+
+		(void)rename_dir(oldlist, old_listname, newlist, new_listname,
+				 MAILBOX_LIST_PATH_TYPE_CONTROL);
+		(void)rename_dir(oldlist, old_listname, newlist, new_listname,
+				 MAILBOX_LIST_PATH_TYPE_INDEX);
+	}
+	array_free(&names_arr);
+	pool_unref(&pool);
+
+	return ret;
+}
+
 static int
 maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
 			    struct mailbox_list *newlist, const char *newname,
 			    bool rename_children)
 {
-	const char *oldpath, *newpath;
+	const char *oldpath, *newpath, *root_path;
 	int ret;
         bool found;
 
@@ -563,6 +563,16 @@
 	newpath = mailbox_list_get_path(newlist, newname,
 					MAILBOX_LIST_PATH_TYPE_MAILBOX);
 
+	root_path = mailbox_list_get_path(oldlist, NULL,
+					  MAILBOX_LIST_PATH_TYPE_MAILBOX);
+	if (strcmp(oldpath, root_path) == 0) {
+		/* most likely INBOX */
+		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
+			t_strdup_printf("Renaming %s isn't supported.",
+					oldname));
+		return -1;
+	}
+
 	ret = rename(oldpath, newpath);
 	if (ret == 0 || errno == ENOENT) {
 		(void)rename_dir(oldlist, oldname, newlist, newname,
@@ -602,6 +612,7 @@
 	.name = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS,
 	.hierarchy_sep = '.',
 	.props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME |
+		MAILBOX_LIST_PROP_NO_ALT_DIR |
 		MAILBOX_LIST_PROP_NO_NOSELECT,
 	.mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
 
@@ -625,8 +636,7 @@
 		maildir_list_create_mailbox_dir,
 		maildir_list_delete_mailbox,
 		maildir_list_delete_dir,
-		maildir_list_rename_mailbox,
-		NULL
+		maildir_list_rename_mailbox
 	}
 };
 
@@ -634,6 +644,7 @@
 	.name = MAILBOX_LIST_NAME_IMAPDIR,
 	.hierarchy_sep = '.',
 	.props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME |
+		MAILBOX_LIST_PROP_NO_ALT_DIR |
 		MAILBOX_LIST_PROP_NO_NOSELECT,
 	.mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
 
@@ -657,7 +668,6 @@
 		maildir_list_create_mailbox_dir,
 		maildir_list_delete_mailbox,
 		maildir_list_delete_dir,
-		maildir_list_rename_mailbox,
-		NULL
+		maildir_list_rename_mailbox
 	}
 };
--- a/src/lib-storage/mail-storage-private.h	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/mail-storage-private.h	Sun Feb 14 22:32:59 2010 +0200
@@ -98,6 +98,8 @@
 		      bool directory);
 	int (*update)(struct mailbox *box, const struct mailbox_update *update);
 	int (*delete)(struct mailbox *box);
+	int (*rename)(struct mailbox *src, struct mailbox *dest,
+		      bool rename_children);
 
 	void (*get_status)(struct mailbox *box, enum mailbox_status_items items,
 			   struct mailbox_status *status_r);
--- a/src/lib-storage/mail-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/mail-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -270,8 +270,11 @@
 		if (mail_storage_is_mailbox_file(storage_class))
 			list_flags |= MAILBOX_LIST_FLAG_MAILBOX_FILES;
 		if (mailbox_list_create(list_set.layout, ns, &list_set,
-					list_flags, error_r) < 0)
+					list_flags, error_r) < 0) {
+			*error_r = t_strdup_printf("Mailbox list driver %s: %s",
+						   list_set.layout, *error_r);
 			return -1;
+		}
 		if (mail_storage_create_root(ns->list, flags, error_r) < 0)
 			return -1;
 	}
@@ -656,6 +659,42 @@
 	return ret;
 }
 
+static bool nullequals(const void *p1, const void *p2)
+{
+	return (p1 == NULL && p2 == NULL) || (p1 != NULL && p2 != NULL);
+}
+
+int mailbox_rename(struct mailbox *src, struct mailbox *dest,
+		   bool rename_children)
+{
+	if (!mailbox_list_is_valid_existing_name(src->list, src->name) ||
+	    *src->name == '\0' ||
+	    !mailbox_list_is_valid_create_name(dest->list, dest->name)) {
+		mailbox_list_set_error(src->list, MAIL_ERROR_PARAMS,
+				       "Invalid mailbox name");
+		return -1;
+	}
+	if (strcmp(src->storage->name, dest->storage->name) != 0) {
+		mailbox_list_set_error(src->list, MAIL_ERROR_NOTPOSSIBLE,
+			"Can't rename mailbox to another storage type.");
+		return -1;
+	}
+	if (!nullequals(src->list->set.index_dir, dest->list->set.index_dir) ||
+	    !nullequals(src->list->set.control_dir, dest->list->set.control_dir)) {
+		mailbox_list_set_error(src->list, MAIL_ERROR_NOTPOSSIBLE,
+			"Can't rename mailboxes across specified storages.");
+		return -1;
+	}
+	if (src->list->ns->type != NAMESPACE_PRIVATE ||
+	    dest->list->ns->type != NAMESPACE_PRIVATE) {
+		mailbox_list_set_error(src->list, MAIL_ERROR_NOTPOSSIBLE,
+			"Renaming not supported across non-private namespaces.");
+		return -1;
+	}
+
+	return src->v.rename(src, dest, rename_children);
+}
+
 struct mail_storage *mailbox_get_storage(const struct mailbox *box)
 {
 	return box->storage;
--- a/src/lib-storage/mail-storage.h	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/mail-storage.h	Sun Feb 14 22:32:59 2010 +0200
@@ -346,6 +346,11 @@
 int mailbox_update(struct mailbox *box, const struct mailbox_update *update);
 /* Delete mailbox (and its parent directory, if it has no siblings) */
 int mailbox_delete(struct mailbox *box);
+/* Rename mailbox. Renaming across different mailbox lists is possible only
+   between private namespaces and storages of the same type. If the rename
+   fails, the error is set to src's storage. */
+int mailbox_rename(struct mailbox *src, struct mailbox *dest,
+		   bool rename_children);
 
 /* Enable the given feature for the mailbox. */
 int mailbox_enable(struct mailbox *box, enum mailbox_feature features);
--- a/src/lib-storage/mailbox-list-private.h	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/mailbox-list-private.h	Sun Feb 14 22:32:59 2010 +0200
@@ -68,11 +68,6 @@
 	int (*rename_mailbox)(struct mailbox_list *oldlist, const char *oldname,
 			      struct mailbox_list *newlist, const char *newname,
 			      bool rename_children);
-	/* called by rename_mailbox() just before running the actual rename() */
-	int (*rename_mailbox_pre)(struct mailbox_list *oldlist,
-				  const char *oldname,
-				  struct mailbox_list *newlist,
-				  const char *newname);
 };
 
 struct mailbox_list_module_register {
--- a/src/lib-storage/mailbox-list.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/mailbox-list.c	Sun Feb 14 22:32:59 2010 +0200
@@ -108,12 +108,22 @@
 		 *set->subscription_fname != '\0');
 
 	if (!mailbox_list_driver_find(driver, &idx)) {
-		*error_r = t_strdup_printf("Unknown mailbox list driver: %s",
-					   driver);
+		*error_r = "Unknown driver name";
 		return -1;
 	}
 
 	class_p = array_idx(&mailbox_list_drivers, idx);
+	if (((*class_p)->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 &&
+	    set->maildir_name != NULL) {
+		*error_r = "maildir_name not supported by this driver";
+		return -1;
+	}
+	if (((*class_p)->props & MAILBOX_LIST_PROP_NO_ALT_DIR) != 0 &&
+	    set->alt_dir != NULL) {
+		*error_r = "alt_dir not supported by this driver";
+		return -1;
+	}
+
 	list = (*class_p)->v.alloc();
 	array_create(&list->module_contexts, list->pool, sizeof(void *), 5);
 
@@ -137,8 +147,7 @@
 	list->set.inbox_path = p_strdup(list->pool, set->inbox_path);
 	list->set.subscription_fname =
 		p_strdup(list->pool, set->subscription_fname);
-	list->set.maildir_name = set->maildir_name == NULL ||
-		(list->props & MAILBOX_LIST_PROP_NO_MAILDIR_NAME) != 0 ? "" :
+	list->set.maildir_name = set->maildir_name == NULL ? "" :
 		p_strdup(list->pool, set->maildir_name);
 	list->set.mailbox_dir_name =
 		p_strdup(list->pool, set->mailbox_dir_name);
@@ -741,62 +750,6 @@
 	return list->v.delete_dir(list, name);
 }
 
-static bool nullequals(const void *p1, const void *p2)
-{
-	return (p1 == NULL && p2 == NULL) || (p1 != NULL && p2 != NULL);
-}
-
-int mailbox_list_rename_mailbox(struct mailbox_list *oldlist,
-				const char *oldname,
-				struct mailbox_list *newlist,
-				const char *newname, bool rename_children)
-{
-	struct mail_storage *oldstorage;
-	struct mail_storage *newstorage;
-	uint8_t guid[MAIL_GUID_128_SIZE];
-
-	if (mailbox_list_get_storage(&oldlist, &oldname, &oldstorage) < 0)
-		return -1;
-
-	newstorage = oldstorage;
-	mailbox_list_get_closest_storage(newlist, &newstorage);
-
-	if (!mailbox_list_is_valid_existing_name(oldlist, oldname) ||
-	    *oldname == '\0' ||
-	    !mailbox_list_is_valid_create_name(newlist, newname)) {
-		mailbox_list_set_error(oldlist, MAIL_ERROR_PARAMS,
-				       "Invalid mailbox name");
-		return -1;
-	}
-	if (strcmp(oldstorage->name, newstorage->name) != 0) {
-		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
-			"Can't rename mailbox to another storage type.");
-		return -1;
-	}
-	if (!nullequals(oldlist->set.index_dir, newlist->set.index_dir) ||
-	    !nullequals(oldlist->set.control_dir, newlist->set.control_dir)) {
-		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
-			"Can't rename mailboxes across specified storages.");
-		return -1;
-	}
-	if (oldlist->ns->type != NAMESPACE_PRIVATE ||
-	    newlist->ns->type != NAMESPACE_PRIVATE) {
-		mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
-			"Renaming not supported across non-private namespaces.");
-		return -1;
-	}
-
-	if (oldlist->v.rename_mailbox(oldlist, oldname, newlist, newname,
-				      rename_children) < 0)
-		return -1;
-
-	/* we'll track mailbox names, instead of GUIDs. We may be renaming a
-	   non-selectable mailbox (directory), which doesn't even have a GUID */
-	mailbox_name_get_sha128(newname, guid);
-	mailbox_list_add_change(oldlist, MAILBOX_LOG_RECORD_RENAME, guid);
-	return 0;
-}
-
 void mailbox_name_get_sha128(const char *name, uint8_t guid[MAIL_GUID_128_SIZE])
 {
 	unsigned char sha[SHA1_RESULTLEN];
--- a/src/lib-storage/mailbox-list.h	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/mailbox-list.h	Sun Feb 14 22:32:59 2010 +0200
@@ -18,8 +18,10 @@
 enum mailbox_list_properties {
 	/* maildir_name must always be empty */
 	MAILBOX_LIST_PROP_NO_MAILDIR_NAME	= 0x01,
+	/* alt directories not supported */
+	MAILBOX_LIST_PROP_NO_ALT_DIR		= 0x02,
 	/* no support for \noselect directories, only mailboxes */
-	MAILBOX_LIST_PROP_NO_NOSELECT		= 0x02
+	MAILBOX_LIST_PROP_NO_NOSELECT		= 0x04
 };
 
 enum mailbox_list_flags {
@@ -250,13 +252,6 @@
 
 /* Delete a non-selectable mailbox. Fail if the mailbox is selectable. */
 int mailbox_list_delete_dir(struct mailbox_list *list, const char *name);
-/* Rename mailbox. Renaming across different mailbox lists is possible only
-   between private namespaces and storages of the same type. If the rename
-   fails, the error is set to oldlist. */
-int mailbox_list_rename_mailbox(struct mailbox_list *oldlist,
-				const char *oldname,
-				struct mailbox_list *newlist,
-				const char *newname, bool rename_children);
 
 /* Returns the error message of last occurred error. */
 const char *mailbox_list_get_last_error(struct mailbox_list *list,
--- a/src/lib-storage/test-mailbox.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/lib-storage/test-mailbox.c	Sun Feb 14 22:32:59 2010 +0200
@@ -59,6 +59,15 @@
 	return -1;
 }
 
+static int test_mailbox_rename(struct mailbox *src,
+			       struct mailbox *dest ATTR_UNUSED,
+			       bool rename_children ATTR_UNUSED)
+{
+	mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE,
+			       "Test mailbox rename isn't supported");
+	return -1;
+}
+
 static void test_mailbox_get_status(struct mailbox *box ATTR_UNUSED,
 				    enum mailbox_status_items items ATTR_UNUSED,
 				    struct mailbox_status *status_r)
@@ -316,6 +325,7 @@
 		test_mailbox_create,
 		test_mailbox_update,
 		test_mailbox_delete,
+		test_mailbox_rename,
 		test_mailbox_get_status,
 		NULL,
 		NULL,
--- a/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/plugins/lazy-expunge/lazy-expunge-plugin.c	Sun Feb 14 22:32:59 2010 +0200
@@ -231,10 +231,12 @@
 }
 
 static int
-mailbox_move(struct mailbox_list *src_list, const char *src_name,
-	     struct mailbox_list *dest_list, const char **_dest_name)
+mailbox_move(struct mailbox *src_box, struct mailbox_list *dest_list,
+	     const char *wanted_destname, struct mailbox **dest_box_r)
 {
-	const char *dir, *origin, *dest_name = *_dest_name;
+	const char *dest_name = wanted_destname;
+	struct mailbox *dest_box;
+	const char *dir, *origin;
 	enum mail_error error;
 	mode_t mode;
 	gid_t gid;
@@ -244,14 +246,19 @@
 	dir = mailbox_list_get_path(dest_list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
 	if (mkdir_parents_chgrp(dir, mode, gid, origin) < 0 &&
 	    errno != EEXIST) {
-		mailbox_list_set_critical(src_list,
+		mail_storage_set_critical(src_box->storage,
 			"mkdir_parents(%s) failed: %m", dir);
 		return -1;
 	}
 
-	while (mailbox_list_rename_mailbox(src_list, src_name,
-					   dest_list, dest_name, FALSE) < 0) {
-		mailbox_list_get_last_error(src_list, &error);
+	for (;;) {
+		dest_box = mailbox_alloc(dest_list, dest_name,
+					 MAILBOX_FLAG_OPEN_DELETED);
+		if (mailbox_rename(src_box, dest_box, FALSE) == 0)
+			break;
+		mailbox_free(&dest_box);
+
+		mail_storage_get_last_error(src_box->storage, &error);
 		switch (error) {
 		case MAIL_ERROR_EXISTS:
 			break;
@@ -261,12 +268,12 @@
 			return -1;
 		}
 
-		/* mailbox is being deleted multiple times per second.
-		   update the filename. */
-		dest_name = t_strdup_printf("%s-%04u", *_dest_name,
+		/* destination already exists. generate a different name. */
+		dest_name = t_strdup_printf("%s-%04u", wanted_destname,
 					    (uint32_t)random());
 	}
-	*_dest_name = dest_name;
+
+	*dest_box_r = dest_box;
 	return 1;
 }
 
@@ -378,10 +385,11 @@
 	}
 
 	/* first move the actual mailbox */
-	if ((ret = mailbox_move(list, box->name, dest_ns->list, &destname)) < 0)
+	if ((ret = mailbox_move(box, dest_ns->list, destname,
+				&expunge_box)) < 0)
 		return -1;
 	if (ret == 0) {
-		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
+		mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND,
 			T_MAIL_ERR_MAILBOX_NOT_FOUND(box->name));
 		return -1;
 	}
@@ -389,12 +397,10 @@
 	/* 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. */
-	expunge_box = mailbox_alloc(dest_ns->list, destname,
-				    MAILBOX_FLAG_OPEN_DELETED);
 	if (mailbox_open(expunge_box) < 0) {
 		str = mail_storage_get_last_error(expunge_box->storage, &error);
 		i_error("lazy_expunge: Couldn't open DELETEd mailbox "
-			"%s: %s", destname, str);
+			"%s: %s", expunge_box->name, str);
 		mailbox_free(&expunge_box);
 		return -1;
 	}
@@ -403,7 +409,7 @@
 		return -1;
 	}
 
-	if (expunge_ns == dest_ns && strcmp(destname, box->name) != 0)
+	if (expunge_ns == dest_ns && strcmp(expunge_box->name, box->name) != 0)
 		ret = mailbox_move_all_mails(expunge_box, box->name);
 	else
 		ret = 0;
@@ -412,11 +418,16 @@
 	/* next move the expunged messages mailbox, if it exists */
 	dest_ns = get_lazy_ns(list->ns->user, LAZY_NAMESPACE_DELETE_EXPUNGE);
 	if (expunge_ns != dest_ns) {
-		if (mailbox_move(expunge_ns->list, box->name,
-				 dest_ns->list, &destname) < 0)
-			ret = -1;
+		struct mailbox *ret_box;
+
+		expunge_box = mailbox_alloc(expunge_ns->list, box->name, 0);
+		ret = mailbox_move(expunge_box, dest_ns->list,
+				   destname, &ret_box);
+		if (ret > 0)
+			mailbox_free(&ret_box);
+		mailbox_free(&expunge_box);
 	}
-	return ret;
+	return ret < 0 ? -1 : 0;
 }
 
 static void lazy_expunge_mailbox_allocated(struct mailbox *box)
--- a/src/plugins/virtual/virtual-storage.c	Sun Feb 14 22:30:43 2010 +0200
+++ b/src/plugins/virtual/virtual-storage.c	Sun Feb 14 22:32:59 2010 +0200
@@ -481,6 +481,7 @@
 		virtual_mailbox_create,
 		virtual_mailbox_update,
 		index_storage_mailbox_delete,
+		index_storage_mailbox_rename,
 		index_storage_get_status,
 		NULL,
 		NULL,