# HG changeset patch # User Timo Sirainen # Date 1194122061 -7200 # Node ID b502a49262986b895139758316699b546a9c4cd3 # Parent 715ef6f98f440c01e3080e1a3c55f957760af1ad Use a fast rename() method for maildir -> dbox conversion. 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 +#include #define CONVERT_LOCK_FILENAME ".dovecot.convert" @@ -113,6 +115,106 @@ 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,13 +229,14 @@ 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 " @@ -143,6 +246,18 @@ 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; + } + /* First open the source mailbox. If we can't open it, don't create the destination mailbox either. */ srcbox = mailbox_open(source_storage, name, NULL, @@ -158,8 +273,6 @@ } /* 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 @@ 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. */