]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
dbox: Fixes to creating, deleting and renaming mailboxes when using alt
authorTimo Sirainen <tss@iki.fi>
Wed, 5 Mar 2008 01:53:39 +0000 (03:53 +0200)
committerTimo Sirainen <tss@iki.fi>
Wed, 5 Mar 2008 01:53:39 +0000 (03:53 +0200)
directories.

--HG--
branch : HEAD

src/lib-storage/index/dbox/dbox-storage.c
src/lib-storage/list/mailbox-list-fs.c
src/lib-storage/list/mailbox-list-maildir.c
src/lib-storage/mailbox-list-private.h

index ed906e48ae48d07cc67e572625a2a1c63c2bdfc7..18f0dc94995ce273447b48e28499e0f9ba0009c8 100644 (file)
@@ -33,6 +33,12 @@ static MODULE_CONTEXT_DEFINE_INIT(dbox_mailbox_list_module,
 
 static int
 dbox_list_delete_mailbox(struct mailbox_list *list, const char *name);
+static int
+dbox_list_rename_mailbox(struct mailbox_list *list,
+                        const char *oldname, const char *newname);
+static int
+dbox_list_rename_mailbox_pre(struct mailbox_list *list,
+                            const char *oldname, const char *newname);
 static int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx,
                                     const char *dir, const char *fname,
                                     enum mailbox_list_file_type type,
@@ -126,6 +132,8 @@ static int dbox_create(struct mail_storage *_storage, const char *data,
        storage->alt_dir = p_strdup(_storage->pool, alt_dir);
        _storage->list->v.iter_is_mailbox = dbox_list_iter_is_mailbox;
        _storage->list->v.delete_mailbox = dbox_list_delete_mailbox;
+       _storage->list->v.rename_mailbox = dbox_list_rename_mailbox;
+       _storage->list->v.rename_mailbox_pre = dbox_list_rename_mailbox_pre;
 
        MODULE_CONTEXT_SET_FULL(_storage->list, dbox_mailbox_list_module,
                                storage, &storage->list_module_ctx);
@@ -313,7 +321,8 @@ static int dbox_storage_mailbox_close(struct mailbox *box)
 static int dbox_mailbox_create(struct mail_storage *_storage,
                               const char *name, bool directory ATTR_UNUSED)
 {
-       const char *path;
+       struct dbox_storage *storage = (struct dbox_storage *)_storage;
+       const char *path, *alt_path;
        struct stat st;
 
        path = mailbox_list_get_path(_storage->list, name,
@@ -324,6 +333,17 @@ static int dbox_mailbox_create(struct mail_storage *_storage,
                return -1;
        }
 
+       /* make sure the alt path doesn't exist yet. it shouldn't (except with
+          race conditions with RENAME/DELETE), but if something crashed and
+          left it lying around we don't want to start overwriting files in
+          it. */
+       alt_path = dbox_get_alt_path(storage, path);
+       if (alt_path != NULL && stat(alt_path, &st) == 0) {
+               mail_storage_set_error(_storage, MAIL_ERROR_NOTPOSSIBLE,
+                                      "Mailbox already exists");
+               return -1;
+       }
+
        return create_dbox(_storage, path);
 }
 
@@ -339,6 +359,8 @@ dbox_delete_nonrecursive(struct mailbox_list *list, const char *path,
 
        dir = opendir(path);
        if (dir == NULL) {
+               if (errno == ENOENT)
+                       return 0;
                if (!mailbox_list_set_error_from_errno(list)) {
                        mailbox_list_set_critical(list,
                                "opendir(%s) failed: %m", path);
@@ -394,7 +416,7 @@ dbox_delete_nonrecursive(struct mailbox_list *list, const char *path,
                                        "can't delete it.", name));
                return -1;
        }
-       return 0;
+       return 1;
 }
 
 static int
@@ -404,6 +426,7 @@ dbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
        struct stat st;
        const char *path, *alt_path;
        bool deleted = FALSE;
+       int ret;
 
        /* Make sure the indexes are closed before trying to delete the
           directory that contains them. It can still fail with some NFS
@@ -418,11 +441,8 @@ dbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
        /* check if the mailbox actually exists */
        path = mailbox_list_get_path(list, name,
                                     MAILBOX_LIST_PATH_TYPE_MAILBOX);
-       if (stat(path, &st) == 0) {
+       if ((ret = dbox_delete_nonrecursive(list, path, name)) > 0) {
                /* delete the mailbox first */
-               if (dbox_delete_nonrecursive(list, path, name) < 0)
-                       return -1;
-
                alt_path = dbox_get_alt_path(storage, path);
                if (alt_path != NULL) {
                        if (dbox_delete_nonrecursive(list, alt_path, name) < 0)
@@ -452,6 +472,10 @@ dbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
                }
        }
 
+       alt_path = dbox_get_alt_path(storage, path);
+       if (alt_path != NULL)
+               (void)rmdir(alt_path);
+
        if (rmdir(path) == 0)
                return 0;
        else if (errno == ENOTEMPTY) {
@@ -467,6 +491,78 @@ dbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
        return -1;
 }
 
+static bool
+dbox_list_rename_get_alt_paths(struct mailbox_list *list,
+                              const char *oldname, const char *newname,
+                              const char **oldpath_r, const char **newpath_r)
+{
+       struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
+       const char *path;
+
+       if (storage->alt_dir == NULL)
+               return FALSE;
+
+       path = mailbox_list_get_path(list, oldname, MAILBOX_LIST_PATH_TYPE_DIR);
+       *oldpath_r = dbox_get_alt_path(storage, path);
+       if (*oldpath_r == NULL)
+               return FALSE;
+
+       path = mailbox_list_get_path(list, newname, MAILBOX_LIST_PATH_TYPE_DIR);
+       *newpath_r = dbox_get_alt_path(storage, path);
+       i_assert(*newpath_r != NULL);
+       return TRUE;
+}
+
+static int
+dbox_list_rename_mailbox_pre(struct mailbox_list *list,
+                            const char *oldname, const char *newname)
+{
+       const char *alt_oldpath, *alt_newpath;
+       struct stat st;
+
+       if (!dbox_list_rename_get_alt_paths(list, oldname, newname,
+                                           &alt_oldpath, &alt_newpath))
+               return 0;
+
+       if (stat(alt_newpath, &st) == 0) {
+               /* race condition or a directory left there lying around?
+                  safest to just report error. */
+               mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
+                                      "Target mailbox already exists");
+               return -1;
+       } else if (errno != ENOENT) {
+               mailbox_list_set_critical(list, "stat(%s) failed: %m",
+                                         alt_newpath);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+dbox_list_rename_mailbox(struct mailbox_list *list,
+                        const char *oldname, const char *newname)
+{
+       struct dbox_storage *storage = DBOX_LIST_CONTEXT(list);
+       const char *alt_oldpath, *alt_newpath;
+
+       if (storage->list_module_ctx.super.rename_mailbox(list, oldname,
+                                                         newname) < 0)
+               return -1;
+
+       if (!dbox_list_rename_get_alt_paths(list, oldname, newname,
+                                           &alt_oldpath, &alt_newpath))
+               return 0;
+
+       if (rename(alt_oldpath, alt_newpath) == 0) {
+               /* ok */
+       } else if (errno != ENOENT) {
+               /* renaming is done already, so just log the error */
+               mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+                                         alt_oldpath, alt_newpath);
+       }
+       return 0;
+}
+
 static void dbox_notify_changes(struct mailbox *box)
 {
        struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
index 5ef43594ab85edc06e9c3198aee60b172e5af638..a3c4baaa5bdb9eea119efd3844dc718533a40a9c 100644 (file)
@@ -285,9 +285,9 @@ static int fs_list_rename_mailbox(struct mailbox_list *list,
        struct stat st;
 
        oldpath = mailbox_list_get_path(list, oldname,
-                                       MAILBOX_LIST_PATH_TYPE_MAILBOX);
+                                       MAILBOX_LIST_PATH_TYPE_DIR);
        newpath = mailbox_list_get_path(list, newname,
-                                       MAILBOX_LIST_PATH_TYPE_MAILBOX);
+                                       MAILBOX_LIST_PATH_TYPE_DIR);
 
        /* create the hierarchy */
        p = strrchr(newpath, '/');
@@ -321,6 +321,11 @@ static int fs_list_rename_mailbox(struct mailbox_list *list,
                return -1;
        }
 
+       if (list->v.rename_mailbox_pre != NULL) {
+               if (list->v.rename_mailbox_pre(list, oldname, newname) < 0)
+                       return -1;
+       }
+
        /* NOTE: renaming INBOX works just fine with us, it's simply recreated
           the next time it's needed. */
        if (rename(oldpath, newpath) < 0) {
@@ -372,6 +377,7 @@ struct mailbox_list fs_mailbox_list = {
                NULL,
                fs_list_set_subscribed,
                fs_list_delete_mailbox,
-               fs_list_rename_mailbox
+               fs_list_rename_mailbox,
+               NULL
        }
 };
index 8d97866ab0f4f58765951af0196593a72ebacdb8..ec25ba60f081316904af23facadbba27ec83b224 100644 (file)
@@ -457,7 +457,8 @@ struct mailbox_list maildir_mailbox_list = {
                NULL,
                maildir_list_set_subscribed,
                maildir_list_delete_mailbox,
-               maildir_list_rename_mailbox
+               maildir_list_rename_mailbox,
+               NULL
        }
 };
 
@@ -482,6 +483,7 @@ struct mailbox_list imapdir_mailbox_list = {
                NULL,
                maildir_list_set_subscribed,
                maildir_list_delete_mailbox,
-               maildir_list_rename_mailbox
+               maildir_list_rename_mailbox,
+               NULL
        }
 };
index e2320e90bb878ef8b3c0b938d54cd7a632297d21..9544b856002024d9e7cc48481ab42bf7c6f944d2 100644 (file)
@@ -49,6 +49,9 @@ struct mailbox_list_vfuncs {
        int (*delete_mailbox)(struct mailbox_list *list, const char *name);
        int (*rename_mailbox)(struct mailbox_list *list, const char *oldname,
                              const char *newname);
+       /* called by rename_mailbox() just before running the actual rename() */
+       int (*rename_mailbox_pre)(struct mailbox_list *list,
+                                 const char *oldname, const char *newname);
 };
 
 struct mailbox_list_module_register {