Mercurial > dovecot > core-2.2
changeset 10711:292562f9b12c HEAD
lib-storage: Mailbox renaming API changed.
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,