From f93e1838a134271ed707f77ae9d969f723eaebe9 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Tue, 24 Apr 2018 18:47:28 +0300 Subject: [PATCH] lib-storage: Add mail_user_home_mkdir() --- src/lib-storage/mail-user.c | 60 +++++++++++++++++++++++++++++++++++++ src/lib-storage/mail-user.h | 5 ++++ 2 files changed, 65 insertions(+) diff --git a/src/lib-storage/mail-user.c b/src/lib-storage/mail-user.c index efc0dd6901..125d864b67 100644 --- a/src/lib-storage/mail-user.c +++ b/src/lib-storage/mail-user.c @@ -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 } diff --git a/src/lib-storage/mail-user.h b/src/lib-storage/mail-user.h index 0d79a76138..cc705107fb 100644 --- a/src/lib-storage/mail-user.h +++ b/src/lib-storage/mail-user.h @@ -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