dovecot: Use a fast rename() method for maildir -> dbox conversion.

dovecot at dovecot.org dovecot at dovecot.org
Sat Nov 3 22:34:25 EET 2007


details:   http://hg.dovecot.org/dovecot/rev/b502a4926298
changeset: 6681:b502a4926298
user:      Timo Sirainen <tss at iki.fi>
date:      Sat Nov 03 22:34:21 2007 +0200
description:
Use a fast rename() method for maildir -> dbox conversion.

diffstat:

1 file changed, 122 insertions(+), 8 deletions(-)
src/plugins/convert/convert-storage.c |  130 ++++++++++++++++++++++++++++++---

diffs (185 lines):

diff -r 715ef6f98f44 -r b502a4926298 src/plugins/convert/convert-storage.c
--- a/src/plugins/convert/convert-storage.c	Sat Nov 03 22:33:16 2007 +0200
+++ b/src/plugins/convert/convert-storage.c	Sat Nov 03 22:34:21 2007 +0200
@@ -1,6 +1,7 @@
 /* Copyright (c) 2006-2007 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "str.h"
 #include "file-lock.h"
 #include "file-dotlock.h"
 #include "mail-storage-private.h"
@@ -9,6 +10,7 @@
 #include "convert-storage.h"
 
 #include <stdio.h>
+#include <dirent.h>
 
 #define CONVERT_LOCK_FILENAME ".dovecot.convert"
 
@@ -113,6 +115,106 @@ mailbox_name_convert(struct mail_storage
 	return dest_name;
 }
 
+static int mailbox_convert_maildir_to_dbox(struct mail_storage *src_storage,
+					   struct mail_storage *dest_storage,
+					   const char *src_name,
+					   const char *dest_name)
+{
+	static const char *maildir_files[] = {
+		"dovecot-uidlist",
+		"dovecot-keywords",
+		"dovecot.index",
+		"dovecot.index.log",
+		"dovecot.index.cache"
+	};
+	string_t *src, *dest;
+	DIR *dir;
+	struct dirent *dp;
+	const char *src_path, *dest_path, *new_path, *cur_path;
+	unsigned int i, src_dir_len, dest_dir_len;
+	bool t;
+	int ret;
+
+	src_path = mail_storage_get_mailbox_path(src_storage, src_name, &t);
+	dest_path = mail_storage_get_mailbox_path(dest_storage, dest_name, &t);
+
+	/* rename cur/ directory as the destination directory */
+	cur_path = t_strconcat(src_path, "/cur", NULL);
+
+	if (rename(cur_path, dest_path) < 0) {
+		i_error("rename(%s, %s) failed: %m", cur_path, dest_path);
+		return -1;
+	}
+
+	/* move metadata files */
+	src = t_str_new(256);
+	str_printfa(src, "%s/", src_path);
+	src_dir_len = str_len(src);
+
+	dest = t_str_new(256);
+	str_printfa(dest, "%s/", dest_path);
+	dest_dir_len = str_len(dest);
+
+	for (i = 0; i < N_ELEMENTS(maildir_files); i++) {
+		str_truncate(src, src_dir_len);
+		str_truncate(dest, dest_dir_len);
+		str_append(src, maildir_files[i]);
+		str_append(dest, maildir_files[i]);
+
+		if (rename(str_c(src), str_c(dest)) < 0 && errno != ENOENT) {
+			i_error("rename(%s, %s) failed: %m",
+				str_c(src), str_c(dest));
+		}
+	}
+
+	/* move files in new/ */
+	new_path = t_strconcat(src_path, "/new", NULL);
+	str_truncate(src, src_dir_len);
+	str_append(src, "new/");
+	src_dir_len = str_len(src);
+
+	dir = opendir(new_path);
+	if (dir == NULL) {
+		if (errno == ENOENT)
+			return 0;
+
+		i_error("opendir(%s) failed: %m", new_path);
+		return -1;
+	}
+	ret = 0;
+	errno = 0;
+	while ((dp = readdir(dir)) != NULL) {
+		if (dp->d_name[0] == '.' &&
+		    (dp->d_name[1] == '\0' ||
+		     (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
+			continue;
+
+		str_truncate(src, src_dir_len);
+		str_truncate(dest, dest_dir_len);
+		str_append(src, dp->d_name);
+		str_append(dest, dp->d_name);
+
+		if (strstr(dp->d_name, ":2,") == NULL)
+			str_append(dest, ":2,");
+
+		if (rename(str_c(src), str_c(dest)) < 0) {
+			i_error("rename(%s, %s) failed: %m",
+				str_c(src), str_c(dest));
+			ret = -1;
+			errno = 0;
+		}
+	}
+	if (errno != 0) {
+		i_error("readdir(%s) failed: %m", new_path);
+		ret = -1;
+	}
+	if (closedir(dir) < 0) {
+		i_error("closedir(%s) failed: %m", new_path);
+		ret = -1;
+	}
+	return ret;
+}
+
 static int mailbox_convert_list_item(struct mail_storage *source_storage,
 				     struct mail_storage *dest_storage,
 				     const struct mailbox_info *info,
@@ -127,17 +229,30 @@ static int mailbox_convert_list_item(str
 		return 0;
 
 	name = strcasecmp(info->name, "INBOX") == 0 ? "INBOX" : info->name;
+	dest_name = mailbox_name_convert(dest_storage, source_storage,
+					 set, name);
+
 	if ((info->flags & MAILBOX_NOSELECT) != 0) {
 		/* \NoSelect mailbox, so it's probably a "directory" */
 		if (*info->name == '.' && set->skip_dotdirs)
 			return 0;
 
-		dest_name = mailbox_name_convert(dest_storage, source_storage,
-						 set, name);
 		if (mail_storage_mailbox_create(dest_storage, dest_name,
 						TRUE) < 0) {
 			i_error("Mailbox conversion: Couldn't create mailbox "
 				"directory %s", dest_name);
+			return -1;
+		}
+		return 0;
+	}
+
+	if (strcmp(source_storage->name, "maildir") == 0 &&
+	    strcmp(dest_storage->name, "dbox") == 0) {
+		if (mailbox_convert_maildir_to_dbox(source_storage,
+						    dest_storage,
+						    name, dest_name) < 0) {
+			i_error("Mailbox conversion failed for mailbox %s",
+				name);
 			return -1;
 		}
 		return 0;
@@ -158,8 +273,6 @@ static int mailbox_convert_list_item(str
 	}
 
 	/* Create and open the destination mailbox. */
-	dest_name = mailbox_name_convert(dest_storage, source_storage,
-					 set, name);
 	if (strcmp(dest_name, "INBOX") != 0) {
 		if (mail_storage_mailbox_create(dest_storage, dest_name,
 						FALSE) < 0) {
@@ -202,11 +315,12 @@ static int mailbox_list_copy(struct mail
 	iter = mailbox_list_iter_init(mail_storage_get_list(source_storage),
 				      "*", MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
 	while ((info = mailbox_list_iter_next(iter)) != NULL) {
-		if (mailbox_convert_list_item(source_storage, dest_storage,
-					      info, dotlock, set) < 0) {
-			ret = -1;
+		t_push();
+		ret = mailbox_convert_list_item(source_storage, dest_storage,
+						info, dotlock, set);
+		t_pop();
+		if (ret < 0)
 			break;
-		}
 
 		/* In case there are lots of mailboxes. Also the other touch
 		   is done only after 100 mails. */


More information about the dovecot-cvs mailing list