]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Add mailbox_list_try_migrate_legacy_escape()
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 7 May 2026 21:42:43 +0000 (21:42 +0000)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 8 May 2026 11:33:50 +0000 (11:33 +0000)
Before recent changes, the mailbox_list_escape_name_params() leading-'~' rule
fired on every hierarchy part, so vname "parent/~child" was written to
disk as "parent/\7echild". The new code applies the rule only on the
first part, encoding the same vname as "parent/~child". On-disk
directories written by older Dovecot versions therefore become
inaccessible after upgrade: the lookup constructs the new-format
storage name and stat()s a path that no longer matches.

src/lib-storage/mailbox-list-private.h
src/lib-storage/mailbox-list.c

index a37af4a8a03390ce6973bdb76eb0e9049436ee19..64f1b9869ecc90f287cd0ab5c307d8524ead3aad 100644 (file)
@@ -210,6 +210,15 @@ int mailbox_list_default_get_storage(struct mailbox_list **list,
                                     struct mail_storage **storage_r);
 const char *mailbox_list_default_get_storage_name(struct mailbox_list *list,
                                                  const char *vname);
+/* If legacy_fname differs from new_fname, rename legacy_fname to new_fname
+   in the parent_dir_path. If it fails, log an error. Returns 1 if the mailbox
+   name changed, 0 if they are equal, -1 on error. Intended to be used by
+   mailbox listing code to automatically rename mailboxes with legacy escaping
+   format. */
+int mailbox_list_try_migrate_legacy_escape(struct mailbox_list *list,
+                                          const char *parent_dir_path,
+                                          const char *legacy_fname,
+                                          const char *new_fname);
 const char *mailbox_list_default_get_vname(struct mailbox_list *list,
                                           const char *storage_name);
 const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
index bee0b9c30c96e8353ddc03fda5256979542dcac7..3da2b36c172986ecd61b1c3710eccc9425c11e40 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <time.h>
 #include <ctype.h>
+#include <stdio.h>
 #include <unistd.h>
 #include <dirent.h>
 #include <sys/stat.h>
@@ -408,6 +409,46 @@ const char *mailbox_list_get_storage_name(struct mailbox_list *list,
        return list->v.get_storage_name(list, vname);
 }
 
+int mailbox_list_try_migrate_legacy_escape(struct mailbox_list *list,
+                                          const char *parent_dir_path,
+                                          const char *legacy_fname,
+                                          const char *new_fname)
+{
+       const char *legacy_path, *new_path;
+
+       if (strcmp(legacy_fname, new_fname) == 0)
+               return 0;
+
+       legacy_path = t_strdup_printf("%s/%s", parent_dir_path, legacy_fname);
+       new_path = t_strdup_printf("%s/%s", parent_dir_path, new_fname);
+
+       if (rename(legacy_path, new_path) == 0) {
+               e_debug(list->event,
+                       "Legacy escaped mailbox name '%s' renamed to '%s'",
+                       legacy_fname, new_path);
+               return 1;
+       }
+       if (errno == ENOENT) {
+               /* legacy entry was just lost (migrated by another process?) */
+               return 1;
+       }
+       if (errno == EEXIST || errno == ENOTEMPTY) {
+               mailbox_list_set_critical(list,
+                       "Found conflicting mailbox names: "
+                       "Legacy escaped mailbox name '%s' "
+                       "was attempted to be renamed to '%s', "
+                       "but it already existed - needs to be resolved manually",
+                       legacy_fname, new_path);
+               /* return success, since we don't want to fail the
+                  mailbox listing. */
+               return 1;
+       }
+       /* Unexpected error - fail mailbox listing. */
+       mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
+                                 legacy_path, new_path);
+       return -1;
+}
+
 const char *
 mailbox_list_unescape_name_params(const char *src, char ns_sep, char list_sep,
                                  char escape_char)