#include "fdatasync-path.h"
#include "eacces-error.h"
#include "str.h"
+#include "mailbox-list-private.h"
#include "mdbox-storage.h"
#include "mdbox-map-private.h"
#include "mdbox-file.h"
if (fd == -1 && errno == ENOENT && parents &&
(p = strrchr(path, '/')) != NULL) {
dir = t_strdup_until(path, p);
- if (mkdir_parents_chgrp(dir, map->create_dir_mode,
- map->create_gid,
- map->create_gid_origin) < 0) {
- mail_storage_set_critical(&file->storage->storage,
- "mkdir_parents(%s) failed: %m", dir);
+ if (mailbox_list_mkdir(map->root_list, dir,
+ path != file->alt_path ?
+ MAILBOX_LIST_PATH_TYPE_DIR :
+ MAILBOX_LIST_PATH_TYPE_ALT_DIR) < 0) {
+ mail_storage_copy_list_error(&file->storage->storage,
+ map->root_list);
return -1;
}
/* try again */
uint32_t map_ext_id, ref_ext_id;
- mode_t create_mode, create_dir_mode;
+ struct mailbox_list *root_list;
+ mode_t create_mode;
gid_t create_gid;
const char *create_gid_origin;
};
#include "ostream.h"
#include "mkdir-parents.h"
#include "unlink-old-files.h"
+#include "mailbox-list-private.h"
#include "mdbox-storage.h"
#include "mdbox-file.h"
#include "mdbox-map-private.h"
const char *path)
{
struct mdbox_map *map;
- gid_t tmp_gid;
- const char *tmp_origin;
map = i_new(struct mdbox_map, 1);
map->storage = storage;
map->set = storage->set;
map->path = i_strdup(path);
map->index = mail_index_alloc(path, MDBOX_GLOBAL_INDEX_PREFIX);
+ map->root_list = root_list;
map->map_ext_id = mail_index_ext_register(map->index, "map",
sizeof(struct mdbox_map_mail_index_header),
sizeof(struct mdbox_map_mail_index_record),
mailbox_list_get_permissions(root_list, NULL, &map->create_mode,
&map->create_gid, &map->create_gid_origin);
- mailbox_list_get_dir_permissions(root_list, NULL, &map->create_dir_mode,
- &tmp_gid, &tmp_origin);
mail_index_set_permissions(map->index, map->create_mode,
map->create_gid, map->create_gid_origin);
return map;
static int mdbox_map_mkdir_storage(struct mdbox_map *map)
{
- if (mkdir_parents_chgrp(map->path, map->create_dir_mode,
- map->create_gid, map->create_gid_origin) < 0 &&
- errno != EEXIST) {
- mail_storage_set_critical(MAP_STORAGE(map),
- "mkdir(%s) failed: %m", map->path);
+ if (mailbox_list_mkdir(map->root_list, map->path,
+ MAILBOX_LIST_PATH_TYPE_DIR) < 0) {
+ mail_storage_copy_list_error(MAP_STORAGE(map), map->root_list);
return -1;
}
return 0;
int index_list_create_missing_index_dir(struct mailbox_list *list,
const char *name)
{
- const char *root_dir, *index_dir, *p, *parent_dir;
- const char *origin, *parent_origin;
- mode_t mode, parent_mode;
- gid_t gid, parent_gid;
- int n = 0;
+ const char *root_dir, *index_dir, *parent_dir, *p, *origin;
+ mode_t mode;
+ gid_t gid;
+ unsigned int n = 0;
root_dir = mailbox_list_get_path(list, name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
return -1;
}
/* create the parent directory first */
- mailbox_list_get_dir_permissions(list, NULL, &parent_mode,
- &parent_gid, &parent_origin);
parent_dir = t_strdup_until(index_dir, p);
- if (mkdir_parents_chgrp(parent_dir, parent_mode,
- parent_gid, parent_origin) < 0 &&
- errno != EEXIST) {
- mailbox_list_set_critical(list,
- "mkdir(%s) failed: %m", parent_dir);
+ if (mailbox_list_mkdir(list, parent_dir,
+ MAILBOX_LIST_PATH_TYPE_INDEX) < 0)
return -1;
- }
}
return 0;
}
#include "ioloop.h"
#include "str.h"
#include "mkdir-parents.h"
+#include "mailbox-list-private.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include "maildir-keywords.h"
}
static int maildir_create_path(struct mailbox *box, const char *path,
- bool is_mail_dir)
+ enum mailbox_list_path_type type, bool retry)
{
- const char *p, *parent, *origin;
- mode_t parent_mode;
- gid_t parent_gid;
+ const char *p, *parent;
if (mkdir_chgrp(path, box->dir_create_mode, box->file_create_gid,
box->file_create_gid_origin) == 0)
return 0;
case ENOENT:
p = strrchr(path, '/');
- if (is_mail_dir || p == NULL) {
+ if (type == MAILBOX_LIST_PATH_TYPE_MAILBOX ||
+ p == NULL || !retry) {
/* mailbox was being deleted just now */
mailbox_set_deleted(box);
return -1;
}
/* create index/control root directory */
parent = t_strdup_until(path, p);
- mailbox_list_get_dir_permissions(box->list, NULL, &parent_mode,
- &parent_gid, &origin);
- if (mkdir_parents_chgrp(parent, parent_mode, parent_gid,
- origin) == 0 ||
- errno == EEXIST) {
+ if (mailbox_list_mkdir(box->list, parent, type) == 0) {
/* should work now, try again */
- return maildir_create_path(box, path, TRUE);
+ return maildir_create_path(box, path, type, FALSE);
}
/* fall through */
path = parent;
{
static const char *subdirs[] = { "cur", "new", "tmp" };
const char *dirs[N_ELEMENTS(subdirs) + 2];
+ enum mailbox_list_path_type types[N_ELEMENTS(subdirs) + 2];
struct stat st;
const char *path;
unsigned int i;
- bool is_mail_dir;
/* @UNSAFE: get a list of directories we want to create */
- for (i = 0; i < N_ELEMENTS(subdirs); i++)
+ for (i = 0; i < N_ELEMENTS(subdirs); i++) {
+ types[i] = MAILBOX_LIST_PATH_TYPE_MAILBOX;
dirs[i] = t_strconcat(box->path, "/", subdirs[i], NULL);
+ }
+ types[i] = MAILBOX_LIST_PATH_TYPE_CONTROL;
dirs[i++] = mailbox_list_get_path(box->list, box->name,
MAILBOX_LIST_PATH_TYPE_CONTROL);
+ types[i] = MAILBOX_LIST_PATH_TYPE_INDEX;
dirs[i++] = mailbox_list_get_path(box->list, box->name,
MAILBOX_LIST_PATH_TYPE_INDEX);
i_assert(i == N_ELEMENTS(dirs));
"stat(%s) failed: %m", path);
break;
}
- is_mail_dir = i < N_ELEMENTS(subdirs);
- if (maildir_create_path(box, path, is_mail_dir) < 0)
+ if (maildir_create_path(box, path, types[i], TRUE) < 0)
break;
}
return i == N_ELEMENTS(dirs) ? 0 : -1;
static int
namespace_add(struct mail_user *user,
struct mail_namespace_settings *ns_set,
+ struct mail_namespace_settings *unexpanded_ns_set,
const struct mail_storage_settings *mail_set,
struct mail_namespace **ns_p, const char **error_r)
{
ns_set->location = mail_set->mail_location;
ns->set = ns_set;
+ ns->unexpanded_set = unexpanded_ns_set;
ns->mail_set = mail_set;
ns->prefix = i_strdup(ns_set->prefix);
{
const struct mail_storage_settings *mail_set;
struct mail_namespace_settings *const *ns_set;
+ struct mail_namespace_settings *const *unexpanded_ns_set;
struct mail_namespace *namespaces, *ns, **ns_p;
struct mail_namespace_settings *inbox_set;
const char *error, *driver, *location_source;
- unsigned int i, count;
+ unsigned int i, count, count2;
i_assert(user->initialized);
namespaces = NULL; ns_p = &namespaces;
mail_set = mail_user_set_get_storage_set(user);
- if (array_is_created(&user->set->namespaces))
+ if (array_is_created(&user->set->namespaces)) {
ns_set = array_get(&user->set->namespaces, &count);
- else {
- ns_set = NULL;
+ unexpanded_ns_set =
+ array_get(&user->unexpanded_set->namespaces, &count2);
+ i_assert(count == count2);
+ } else {
+ ns_set = unexpanded_ns_set = NULL;
count = 0;
}
for (i = 0; i < count; i++) {
- if (namespace_add(user, ns_set[i], mail_set, ns_p, error_r) < 0)
+ if (namespace_add(user, ns_set[i], unexpanded_ns_set[i],
+ mail_set, ns_p, error_r) < 0)
return -1;
ns_p = &(*ns_p)->next;
}
/* FIXME: we should support multiple storages in one namespace */
struct mail_storage *storage;
- const struct mail_namespace_settings *set;
+ const struct mail_namespace_settings *set, *unexpanded_set;
const struct mail_storage_settings *mail_set;
};
int mailbox_list_settings_parse(struct mail_user *user, const char *data,
struct mailbox_list_settings *set_r,
const char **error_r);
+const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
+ enum mailbox_list_path_type type);
const char *
mailbox_list_get_root_path(const struct mailbox_list_settings *set,
enum mailbox_list_path_type type);
+int mailbox_list_mkdir(struct mailbox_list *list, const char *path,
+ enum mailbox_list_path_type type);
int mailbox_list_delete_index_control(struct mailbox_list *list,
const char *name);
return 0;
}
+const char *mailbox_list_get_unexpanded_path(struct mailbox_list *list,
+ enum mailbox_list_path_type type)
+{
+ const struct mail_storage_settings *mail_set;
+ const char *location = list->ns->unexpanded_set->location;
+ struct mail_user *user = list->ns->user;
+ struct mailbox_list_settings set;
+ const char *p, *error;
+
+ i_assert(*location == '0');
+ location++;
+
+ if (*location == '\0') {
+ mail_set = mail_user_set_get_driver_settings(user->set_info,
+ user->unexpanded_set, MAIL_STORAGE_SET_DRIVER_NAME);
+ i_assert(mail_set != NULL);
+ location = mail_set->mail_location;
+ i_assert(*location == '0');
+ location++;
+ }
+
+ /* type:settings */
+ p = strchr(location, ':');
+ if (p == NULL)
+ return "";
+
+ if (mailbox_list_settings_parse(user, p + 1, &set, &error) < 0)
+ return "";
+ return mailbox_list_get_root_path(&set, type);
+}
+
void mailbox_list_destroy(struct mailbox_list **_list)
{
struct mailbox_list *list = *_list;
mode_r, gid_r, gid_origin_r);
}
+static int
+mailbox_list_stat_parent(struct mailbox_list *list, const char *path,
+ const char **root_dir_r, struct stat *st_r)
+{
+ const char *p;
+
+ while (stat(path, st_r) < 0) {
+ if (errno != ENOENT || strcmp(path, "/") == 0) {
+ mailbox_list_set_critical(list, "stat(%s) failed: %m",
+ path);
+ return -1;
+ }
+ p = strrchr(path, '/');
+ if (p == NULL)
+ path = "/";
+ else
+ path = t_strdup_until(path, p);
+ }
+ *root_dir_r = path;
+ return 0;
+}
+
+static const char *
+get_expanded_path(const char *unexpanded_start, const char *unexpanded_stop,
+ const char *expanded_full)
+{
+ const char *ret;
+ unsigned int i, slash_count = 0, slash2_count = 0;
+
+ /* get the expanded path up to the same amount of '/' characters.
+ if there isn't the same amount of '/' characters, it means %variable
+ expansion added more of them and we can't handle this. */
+ for (i = 0; unexpanded_start+i != unexpanded_stop; i++) {
+ if (unexpanded_start[i] == '/')
+ slash_count++;
+ }
+ for (; unexpanded_start[i] != '\0'; i++) {
+ if (unexpanded_start[i] == '/')
+ slash2_count++;
+ }
+
+ for (i = 0; expanded_full[i] != '\0'; i++) {
+ if (expanded_full[i] == '/') {
+ if (slash_count == 0)
+ break;
+ slash_count--;
+ }
+ }
+ if (slash_count != 0)
+ return "";
+
+ ret = t_strndup(expanded_full, i);
+ for (; expanded_full[i] != '\0'; i++) {
+ if (expanded_full[i] == '/') {
+ if (slash2_count == 0)
+ return "";
+ slash2_count--;
+ }
+ }
+ if (slash2_count != 0)
+ return "";
+ return ret;
+}
+
+int mailbox_list_mkdir(struct mailbox_list *list, const char *path,
+ enum mailbox_list_path_type type)
+{
+ const char *expanded, *unexpanded, *root_dir, *p, *origin;
+ struct stat st;
+ mode_t mode;
+ gid_t gid;
+
+ mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
+
+ /* get the directory path up to last %variable. for example
+ unexpanded path may be "/var/mail/%d/%2n/%n/Maildir", and we want
+ to get expanded="/var/mail/domain/nn" */
+ unexpanded = mailbox_list_get_unexpanded_path(list, type);
+ p = strrchr(unexpanded, '%');
+ if (p == NULL)
+ expanded = "";
+ else {
+ while (p != unexpanded && *p != '/') p--;
+ if (p == unexpanded)
+ expanded = "";
+ else {
+ expanded = mailbox_list_get_path(list, NULL, type);
+ expanded = get_expanded_path(unexpanded, p, expanded);
+ }
+ }
+
+ if (*expanded != '\0') {
+ /* up to this directory get the permissions from the first
+ parent directory that exists, if it has setgid bit
+ enabled. */
+ if (mailbox_list_stat_parent(list, expanded,
+ &root_dir, &st) < 0)
+ return -1;
+ if ((st.st_mode & S_ISGID) != 0 && root_dir != expanded) {
+ if (mkdir_parents_chgrp(expanded, st.st_mode,
+ (gid_t)-1, root_dir) < 0 &&
+ errno != EEXIST) {
+ mailbox_list_set_critical(list,
+ "mkdir(%s) failed: %m", expanded);
+ return -1;
+ }
+ }
+ if (gid == (gid_t)-1 && (mode & S_ISGID) == 0) {
+ /* change the group for user directories */
+ gid = getegid();
+ }
+ }
+
+ /* the rest of the directories exist only for one user. create them
+ with default directory permissions */
+ if (mkdir_parents_chgrp(path, mode, gid, origin) < 0 &&
+ errno != EEXIST) {
+ mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path);
+ return -1;
+ }
+ return 0;
+}
+
bool mailbox_list_is_valid_pattern(struct mailbox_list *list,
const char *pattern)
{