]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-storage: Add mail_user_home_mkdir()
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Tue, 24 Apr 2018 15:47:28 +0000 (18:47 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 26 Apr 2018 17:28:19 +0000 (20:28 +0300)
src/lib-storage/mail-user.c
src/lib-storage/mail-user.h

index efc0dd690151b528d79ec8f8f6006f0ca4e5e245..125d864b671cd54b44a5bd8eaea032353f9d871d 100644 (file)
@@ -8,6 +8,7 @@
 #include "module-dir.h"
 #include "home-expand.h"
 #include "file-create-locked.h"
+#include "mkdir-parents.h"
 #include "safe-mkstemp.h"
 #include "str.h"
 #include "strescape.h"
@@ -654,6 +655,65 @@ void mail_user_stats_fill(struct mail_user *user, struct stats *stats)
        user->v.stats_fill(user, stats);
 }
 
+static int
+mail_user_home_mkdir_try_ns(struct mail_namespace *ns, const char *home)
+{
+       const enum mailbox_list_path_type types[] = {
+               MAILBOX_LIST_PATH_TYPE_DIR,
+               MAILBOX_LIST_PATH_TYPE_ALT_DIR,
+               MAILBOX_LIST_PATH_TYPE_CONTROL,
+               MAILBOX_LIST_PATH_TYPE_INDEX,
+               MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE,
+               MAILBOX_LIST_PATH_TYPE_LIST_INDEX,
+       };
+       size_t home_len = strlen(home);
+       const char *path;
+
+       for (unsigned int i = 0; i < N_ELEMENTS(types); i++) {
+               if (!mailbox_list_get_root_path(ns->list, types[i], &path))
+                       continue;
+               if (strncmp(path, home, home_len) == 0 &&
+                   (path[home_len] == '\0' || path[home_len] == '/')) {
+                       return mailbox_list_mkdir_root(ns->list, path,
+                                                      types[i]) < 0 ? -1 : 1;
+               }
+       }
+       return 0;
+}
+
+int mail_user_home_mkdir(struct mail_user *user)
+{
+       struct mail_namespace *ns;
+       const char *home;
+       int ret;
+
+       if (mail_user_get_home(user, &home) < 0)
+               return -1;
+
+       /* Try to create the home directory by creating the root directory for
+          a namespace that exists under the home. This way we end up in the
+          special mkdir() code in mailbox_list_try_mkdir_root_parent().
+          Start from INBOX, since that's usually the correct place. */
+       ns = mail_namespace_find_inbox(user->namespaces);
+       if ((ret = mail_user_home_mkdir_try_ns(ns, home)) != 0)
+               return ret < 0 ? -1 : 0;
+       /* try other namespaces */
+       for (ns = user->namespaces; ns != NULL; ns = ns->next) {
+               if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
+                       /* already tried the INBOX namespace */
+                       continue;
+               }
+               if ((ret = mail_user_home_mkdir_try_ns(ns, home)) != 0)
+                       return ret < 0 ? -1 : 0;
+       }
+       /* fallback to a safe mkdir() with 0700 mode */
+       if (mkdir_parents(home, 0700) < 0 && errno != EEXIST) {
+               i_error("mkdir_parents(%s) failed: %m", home);
+               return -1;
+       }
+       return 0;
+}
+
 static const struct var_expand_func_table mail_user_var_expand_func_table_arr[] = {
        { "userdb", mail_user_var_expand_func_userdb },
        { NULL, NULL }
index 0d79a761381207cec15b289f97b1ee439f4fa496..cc705107fba59a3a1bdae272367fcd29d3b179b9 100644 (file)
@@ -196,4 +196,9 @@ void mail_user_init_fs_settings(struct mail_user *user,
    plugin must be loaded to have anything filled. */
 void mail_user_stats_fill(struct mail_user *user, struct stats *stats);
 
+/* Try to mkdir() user's home directory. Ideally this should be called only
+   after the caller tries to create a file to the home directory, but it fails
+   with ENOENT. This way it avoids unnecessary disk IO to the home. */
+int mail_user_home_mkdir(struct mail_user *user);
+
 #endif