# HG changeset patch # User Timo Sirainen # Date 1265500249 -7200 # Node ID 5fe70b5da1b84900274db71a683a8d7e91f9c2f6 # Parent bdb1ea37ccee041d1acf1d796562aa89ce7f7136 lib-storage: Moved mailbox directory creation code to mailbox_list backend. diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/index/cydir/cydir-storage.c --- a/src/lib-storage/index/cydir/cydir-storage.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/index/cydir/cydir-storage.c Sun Feb 07 01:50:49 2010 +0200 @@ -48,25 +48,6 @@ set->subscription_fname = CYDIR_SUBSCRIPTION_FILE_NAME; } -static int create_cydir(struct mail_storage *storage, struct mailbox_list *list, - const char *path) -{ - const char *origin; - mode_t mode; - gid_t gid; - - mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin); - if (mkdir_parents_chgrp(path, mode, gid, origin) < 0 && - errno != EEXIST) { - if (!mail_storage_set_error_from_errno(storage)) { - mail_storage_set_critical(storage, - "mkdir(%s) failed: %m", path); - } - return -1; - } - return 0; -} - static struct mailbox * cydir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *name, struct istream *input, @@ -114,7 +95,8 @@ /* exists, open it */ } else if (errno == ENOENT && strcmp(box->name, "INBOX") == 0) { /* INBOX always exists, create it */ - if (create_cydir(box->storage, box->list, box->path) < 0) + if (box->list->v.create_mailbox_dir(box->list, + box->name, FALSE) < 0) return -1; } else if (errno == ENOENT) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, @@ -136,21 +118,11 @@ cydir_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { - const char *path; - struct stat st; + if (directory && + (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0) + return 0; - path = mailbox_list_get_path(box->list, box->name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (stat(path, &st) == 0) { - mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, - "Mailbox already exists"); - return -1; - } - - if (create_cydir(box->storage, box->list, path) < 0) - return -1; - - return directory || update == NULL ? 0 : + return update == NULL ? 0 : index_storage_mailbox_update(box, update); } diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/index/dbox-common/dbox-storage.c --- a/src/lib-storage/index/dbox-common/dbox-storage.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/index/dbox-common/dbox-storage.c Sun Feb 07 01:50:49 2010 +0200 @@ -157,48 +157,17 @@ int dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { - const char *path, *alt_path, *origin; - struct stat st; + struct dbox_storage *storage = (struct dbox_storage *)box->storage; - path = mailbox_list_get_path(box->list, box->name, - directory ? MAILBOX_LIST_PATH_TYPE_DIR : - MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (stat(path, &st) == 0) { - mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, - "Mailbox already exists"); - return -1; - } - - if (directory) { - mode_t mode; - gid_t gid; + if (directory && + (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0) + return 0; - mailbox_list_get_dir_permissions(box->list, NULL, &mode, - &gid, &origin); - if (mkdir_parents_chgrp(path, mode, gid, origin) == 0) - return 0; - else if (errno == EEXIST) { - mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, - "Mailbox already exists"); - } else if (!mail_storage_set_error_from_errno(box->storage)) { - mail_storage_set_critical(box->storage, - "mkdir(%s) failed: %m", path); - } + if (index_storage_mailbox_open(box) < 0) 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(box->list, path); - if (alt_path != NULL && stat(alt_path, &st) == 0) { - mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, - "Mailbox already exists"); + if (storage->v.mailbox_create_indexes(box, update) < 0) return -1; - } - - return dbox_mailbox_create_indexes(box, update); + return 0; } int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx ATTR_UNUSED, diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/index/index-storage.c --- a/src/lib-storage/index/index-storage.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/index/index-storage.c Sun Feb 07 01:50:49 2010 +0200 @@ -451,8 +451,6 @@ { struct mailbox *box = &ibox->box; const char *path; - gid_t dir_gid; - const char *origin, *dir_origin; string_t *vname; if (name != NULL) { @@ -487,18 +485,11 @@ ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL; ibox->index = index_storage_alloc(box->list, name, flags, index_prefix); - if (box->file_create_mode == 0) { - mailbox_list_get_permissions(box->list, name, - &box->file_create_mode, - &box->file_create_gid, &origin); - box->file_create_gid_origin = p_strdup(box->pool, origin); - mailbox_list_get_dir_permissions(box->list, name, - &box->dir_create_mode, - &dir_gid, &dir_origin); - mail_index_set_permissions(ibox->index, - box->file_create_mode, - box->file_create_gid, origin); - } + if (box->file_create_mode == 0) + mailbox_refresh_permissions(box); + mail_index_set_permissions(ibox->index, box->file_create_mode, + box->file_create_gid, + box->file_create_gid_origin); } int index_storage_mailbox_enable(struct mailbox *box, diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/index/maildir/maildir-storage.c --- a/src/lib-storage/index/maildir/maildir-storage.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/index/maildir/maildir-storage.c Sun Feb 07 01:50:49 2010 +0200 @@ -23,8 +23,6 @@ #include #include -#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder" - #define MAILDIR_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, maildir_mailbox_list_module) @@ -293,19 +291,16 @@ } /* create or fix maildir, ignore if it already exists */ -static int -create_maildir(struct mail_storage *storage, struct mail_namespace *ns, - const char *dir, mode_t mode, gid_t gid, const char *gid_origin, - bool verify) +static int create_maildir(struct mailbox *box, bool verify) { const char *path; unsigned int i; int ret; if (!verify) { - ret = maildir_check_tmp(storage, dir); + ret = maildir_check_tmp(box->storage, box->path); if (ret > 0) { - mail_storage_set_error(storage, + mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, "Mailbox already exists"); return -1; } @@ -314,9 +309,10 @@ } for (i = 0; i < N_ELEMENTS(maildir_subdirs); i++) { - path = t_strconcat(dir, "/", maildir_subdirs[i], NULL); - if (mkdir_verify(storage, ns, path, mode, gid, - gid_origin, verify) < 0) + path = t_strconcat(box->path, "/", maildir_subdirs[i], NULL); + if (mkdir_verify(box->storage, box->list->ns, path, + box->dir_create_mode, box->file_create_gid, + box->file_create_gid_origin, verify) < 0) return -1; } return 0; @@ -327,15 +323,6 @@ (void)maildir_uidlist_lock_touch(mbox->uidlist); } -static mode_t get_dir_mode(mode_t mode) -{ - /* add the execute bit if either read or write bit is set */ - if ((mode & 0600) != 0) mode |= 0100; - if ((mode & 0060) != 0) mode |= 0010; - if ((mode & 0006) != 0) mode |= 0001; - return mode; -} - static struct mailbox * maildir_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, const char *name, struct istream *input, @@ -374,26 +361,9 @@ struct stat st; const char *shared_path; - /* for shared mailboxes get the create mode from the - permissions of dovecot-shared file. */ shared_path = t_strconcat(box->path, "/dovecot-shared", NULL); - if (stat(shared_path, &st) == 0) { - if ((st.st_mode & S_ISGID) != 0 || - (st.st_mode & 0060) == 0) { - /* Ignore GID */ - st.st_gid = (gid_t)-1; - } - mail_index_set_permissions(mbox->ibox.index, - st.st_mode & 0666, st.st_gid, - shared_path); - - box->file_create_mode = st.st_mode & 0666; - box->dir_create_mode = get_dir_mode(st.st_mode & 0666); - box->file_create_gid = st.st_gid; - mbox->ibox.box.file_create_gid_origin = - p_strdup(box->pool, shared_path); + if (stat(shared_path, &st) == 0) box->private_flags_mask = MAIL_SEEN; - } if ((box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0) { if (maildir_uidlist_lock(mbox->uidlist) <= 0) @@ -412,9 +382,6 @@ static int maildir_mailbox_open(struct mailbox *box) { struct stat st; - const char *gid_origin; - mode_t mode; - gid_t gid; int ret; bool inbox; @@ -440,10 +407,7 @@ /* tmp/ directory doesn't exist. does the maildir? */ if (inbox || (*box->name != '\0' && stat(box->path, &st) == 0)) { /* yes, we'll need to create the missing dirs */ - mailbox_list_get_dir_permissions(box->list, box->name, - &mode, &gid, &gid_origin); - if (create_maildir(box->storage, box->list->ns, box->path, - mode, gid, gid_origin, TRUE) < 0) + if (create_maildir(box, TRUE) < 0) return -1; return maildir_mailbox_open_existing(box); @@ -458,40 +422,31 @@ } } -static int -maildir_create_shared(struct mail_storage *storage, struct mail_namespace *ns, - const char *dir, mode_t mode, gid_t gid, - const char *gid_origin) +static int maildir_create_shared(struct mailbox *box) { const char *path; mode_t old_mask; int fd; - /* add the execute bit if either read or write bit is set */ - if ((mode & 0600) != 0) mode |= 0100; - if ((mode & 0060) != 0) mode |= 0010; - if ((mode & 0006) != 0) mode |= 0001; - - if (create_maildir(storage, ns, dir, mode, gid, gid_origin, FALSE) < 0) - return -1; - - old_mask = umask(0777 ^ mode); - path = t_strconcat(dir, "/dovecot-shared", NULL); - fd = open(path, O_WRONLY | O_CREAT, mode & 0666); + old_mask = umask(0); + path = t_strconcat(box->path, "/dovecot-shared", NULL); + fd = open(path, O_WRONLY | O_CREAT, box->file_create_mode); umask(old_mask); if (fd == -1) { - mail_storage_set_critical(storage, "open(%s) failed: %m", path); + mail_storage_set_critical(box->storage, "open(%s) failed: %m", + path); return -1; } - if (fchown(fd, (uid_t)-1, gid) < 0) { + if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) { if (errno == EPERM) { - mail_storage_set_critical(storage, "%s", + mail_storage_set_critical(box->storage, "%s", eperm_error_get_chgrp("fchown", path, - gid, gid_origin)); + box->file_create_gid, + box->file_create_gid_origin)); } else { - mail_storage_set_critical(storage, + mail_storage_set_critical(box->storage, "fchown(%s) failed: %m", path); } } @@ -533,65 +488,27 @@ maildir_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { + const char *root_dir, *shared_path; struct stat st; - const char *path, *root_dir, *shared_path, *gid_origin; - mode_t old_mask; - int fd; + + if (directory && + (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0) + return 0; - path = mailbox_list_get_path(box->list, box->name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (create_maildir(box, FALSE) < 0) + return -1; + + /* if dovecot-shared exists in the root dir, copy it to newly + created mailboxes */ root_dir = mailbox_list_get_path(box->list, NULL, MAILBOX_LIST_PATH_TYPE_MAILBOX); - - /* if dovecot-shared exists in the root dir, create the mailbox using - its permissions and gid, and copy the dovecot-shared inside it. */ shared_path = t_strconcat(root_dir, "/dovecot-shared", NULL); if (stat(shared_path, &st) == 0) { - gid_origin = shared_path; - if (maildir_create_shared(box->storage, box->list->ns, path, - st.st_mode & 0666, st.st_gid, - gid_origin) < 0) - return -1; - } else { - mailbox_list_get_dir_permissions(box->list, NULL, &st.st_mode, - &st.st_gid, &gid_origin); - if (create_maildir(box->storage, box->list->ns, path, - st.st_mode, st.st_gid, gid_origin, - FALSE) < 0) + if (maildir_create_shared(box) < 0) return -1; } - /* Maildir++ spec wants that maildirfolder named file is created for - all subfolders. */ - path = t_strconcat(path, "/" MAILDIR_SUBFOLDER_FILENAME, NULL); - old_mask = umask(0777 ^ (st.st_mode & 0666)); - fd = open(path, O_CREAT | O_WRONLY, 0666); - umask(old_mask); - if (fd != -1) { - /* if dovecot-shared exists, use the same group */ - if (st.st_gid == (gid_t)-1) { - /* doesn't exist */ - } else if (fchown(fd, (uid_t)-1, st.st_gid) == 0) { - /* ok */ - } else if (errno == EPERM) { - mail_storage_set_critical(box->storage, "%s", - eperm_error_get_chgrp("fchown", path, - st.st_gid, gid_origin)); - } else { - mail_storage_set_critical(box->storage, - "fchown(%s) failed: %m", path); - } - (void)close(fd); - } else if (errno == ENOENT) { - mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, - "Mailbox was deleted while it was being created"); - return -1; - } else { - mail_storage_set_critical(box->storage, - "open(%s, O_CREAT) failed: %m", path); - } - return directory || update == NULL ? 0 : - maildir_mailbox_update(box, update); + return update == NULL ? 0 : maildir_mailbox_update(box, update); } static void diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/index/mbox/mbox-storage.c --- a/src/lib-storage/index/mbox/mbox-storage.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/index/mbox/mbox-storage.c Sun Feb 07 01:50:49 2010 +0200 @@ -515,70 +515,23 @@ mbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { - struct mail_storage *storage = box->storage; - const char *path, *p, *origin; - struct stat st; - mode_t mode; - gid_t gid; - int fd; + int fd, ret; + + if (directory && + (box->list->props & MAILBOX_LIST_PROP_NO_NOSELECT) == 0) + return 0; - /* make sure it doesn't exist already */ - path = mailbox_list_get_path(box->list, box->name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (stat(path, &st) == 0) { - mail_storage_set_error(storage, MAIL_ERROR_EXISTS, - "Mailbox already exists"); - return -1; - } - - if (errno != ENOENT) { - if (errno == ENOTDIR) { - mail_storage_set_error(storage, MAIL_ERROR_NOTPOSSIBLE, - "Mailbox doesn't allow inferior mailboxes"); - } else if (!mail_storage_set_error_from_errno(storage)) { - mail_storage_set_critical(storage, - "stat() failed for mbox file %s: %m", path); + /* create the mbox file */ + ret = mailbox_create_fd(box, box->path, O_RDWR | O_CREAT | O_EXCL, &fd); + if (ret <= 0) { + if (ret == 0) { + mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, + "Mailbox already exists"); } return -1; } - - /* create the hierarchy if needed */ - p = directory ? path + strlen(path) : strrchr(path, '/'); - if (p != NULL) { - p = t_strdup_until(path, p); - mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid, - &origin); - if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 && - errno != EEXIST) { - if (!mail_storage_set_error_from_errno(storage)) { - mail_storage_set_critical(storage, - "mkdir_parents(%s) failed: %m", p); - } - return -1; - } - - if (directory) { - /* wanted to create only the directory */ - return 0; - } - } - - /* create the mailbox file */ - fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660); - if (fd != -1) { - (void)close(fd); - return update == NULL ? 0 : mbox_mailbox_update(box, update); - } - - if (errno == EEXIST) { - /* mailbox was just created between stat() and open() call.. */ - mail_storage_set_error(storage, MAIL_ERROR_EXISTS, - "Mailbox already exists"); - } else if (!mail_storage_set_error_from_errno(storage)) { - mail_storage_set_critical(storage, - "Can't create mailbox %s: %m", box->name); - } - return -1; + (void)close(fd); + return update == NULL ? 0 : mbox_mailbox_update(box, update); } static void mbox_mailbox_close(struct mailbox *box) diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/index/shared/shared-list.c --- a/src/lib-storage/index/shared/shared-list.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/index/shared/shared-list.c Sun Feb 07 01:50:49 2010 +0200 @@ -95,7 +95,9 @@ shared_storage_get_namespace(&ns, &name) < 0) { switch (type) { case MAILBOX_LIST_PATH_TYPE_DIR: + case MAILBOX_LIST_PATH_TYPE_ALT_DIR: case MAILBOX_LIST_PATH_TYPE_MAILBOX: + case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: case MAILBOX_LIST_PATH_TYPE_CONTROL: break; case MAILBOX_LIST_PATH_TYPE_INDEX: @@ -231,6 +233,21 @@ } static int +shared_list_create_mailbox_dir(struct mailbox_list *list, const char *name, + bool directory) +{ + struct mail_namespace *ns = list->ns; + int ret; + + if (shared_storage_get_namespace(&ns, &name) < 0) + return -1; + ret = ns->list->v.create_mailbox_dir(ns->list, name, directory); + if (ret < 0) + shared_list_copy_error(list, ns); + return ret; +} + +static int shared_list_delete_mailbox(struct mailbox_list *list, const char *name) { struct mail_namespace *ns = list->ns; @@ -337,6 +354,7 @@ shared_list_iter_deinit, NULL, shared_list_set_subscribed, + shared_list_create_mailbox_dir, shared_list_delete_mailbox, shared_list_delete_dir, shared_list_rename_mailbox, diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/list/mailbox-list-fs.c --- a/src/lib-storage/list/mailbox-list-fs.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/list/mailbox-list-fs.c Sun Feb 07 01:50:49 2010 +0200 @@ -142,17 +142,23 @@ enum mailbox_list_path_type type) { const struct mailbox_list_settings *set = &_list->set; - const char *path; + const char *path, *root_dir; if (name == NULL) { /* return root directories */ switch (type) { case MAILBOX_LIST_PATH_TYPE_DIR: return set->root_dir; + case MAILBOX_LIST_PATH_TYPE_ALT_DIR: + return _list->set.alt_dir; case MAILBOX_LIST_PATH_TYPE_MAILBOX: path = t_strconcat(set->root_dir, "/", set->mailbox_dir_name, NULL); return t_strndup(path, strlen(path)-1); + case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: + path = t_strconcat(set->alt_dir, "/", + set->mailbox_dir_name, NULL); + return t_strndup(path, strlen(path)-1); case MAILBOX_LIST_PATH_TYPE_CONTROL: return set->control_dir != NULL ? set->control_dir : set->root_dir; @@ -168,14 +174,25 @@ if (mailbox_list_try_get_absolute_path(_list, &name)) return name; + root_dir = set->root_dir; switch (type) { case MAILBOX_LIST_PATH_TYPE_DIR: if (*set->maildir_name != '\0') return t_strdup_printf("%s/%s%s", set->root_dir, set->mailbox_dir_name, name); break; + case MAILBOX_LIST_PATH_TYPE_ALT_DIR: + if (*set->maildir_name != '\0') + return t_strdup_printf("%s/%s%s", set->alt_dir, + set->mailbox_dir_name, name); + break; case MAILBOX_LIST_PATH_TYPE_MAILBOX: break; + case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: + if (set->alt_dir == NULL) + return NULL; + root_dir = set->alt_dir; + break; case MAILBOX_LIST_PATH_TYPE_CONTROL: if (set->control_dir != NULL) return t_strdup_printf("%s/%s%s", set->control_dir, @@ -191,19 +208,23 @@ break; } - /* If INBOX is a file, index and control directories are located - in root directory. */ - if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL && - ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 || - type == MAILBOX_LIST_PATH_TYPE_MAILBOX || - type == MAILBOX_LIST_PATH_TYPE_DIR)) - return set->inbox_path; + if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR || + type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) { + /* don't use inbox_path */ + } else if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL) { + /* If INBOX is a file, index and control directories are + located in root directory. */ + if ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 || + type == MAILBOX_LIST_PATH_TYPE_MAILBOX || + type == MAILBOX_LIST_PATH_TYPE_DIR) + return set->inbox_path; + } if (*set->maildir_name == '\0') { - return t_strdup_printf("%s/%s%s", set->root_dir, + return t_strdup_printf("%s/%s%s", root_dir, set->mailbox_dir_name, name); } else { - return t_strdup_printf("%s/%s%s/%s", set->root_dir, + return t_strdup_printf("%s/%s%s/%s", root_dir, set->mailbox_dir_name, name, set->maildir_name); } @@ -275,6 +296,61 @@ name, set); } +static int +fs_list_create_mailbox_dir(struct mailbox_list *list, const char *name, + bool directory) +{ + const char *path, *alt_path, *gid_origin, *p; + struct stat st; + mode_t mode; + gid_t gid; + bool create_parent_dir; + + /* 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. */ + if (!directory) { + alt_path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX); + if (alt_path != NULL && stat(alt_path, &st) == 0) { + mailbox_list_set_error(list, MAIL_ERROR_EXISTS, + "Mailbox already exists"); + return -1; + } + } + + path = mailbox_list_get_path(list, name, + directory ? MAILBOX_LIST_PATH_TYPE_DIR : + MAILBOX_LIST_PATH_TYPE_MAILBOX); + create_parent_dir = !directory && + (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0; + if (create_parent_dir) { + /* we only need to make sure that the parent directory exists */ + p = strrchr(path, '/'); + if (p == NULL) + return 0; + path = t_strdup_until(path, p); + } + + mailbox_list_get_dir_permissions(list, NULL, &mode, + &gid, &gid_origin); + if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0) + return 0; + else if (errno == EEXIST) { + if (create_parent_dir) + return 0; + mailbox_list_set_error(list, MAIL_ERROR_EXISTS, + "Mailbox already exists"); + } else if (errno == ENOTDIR) { + mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, + "Mailbox doesn't allow inferior mailboxes"); + } else if (!mailbox_list_set_error_from_errno(list)) { + mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path); + } + return -1; +} + static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name) { /* let the backend handle the rest */ @@ -461,6 +537,7 @@ fs_list_iter_deinit, NULL, fs_list_set_subscribed, + fs_list_create_mailbox_dir, fs_list_delete_mailbox, fs_list_delete_dir, fs_list_rename_mailbox, diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/list/mailbox-list-maildir.c --- a/src/lib-storage/list/mailbox-list-maildir.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/list/mailbox-list-maildir.c Sun Feb 07 01:50:49 2010 +0200 @@ -3,12 +3,15 @@ #include "lib.h" #include "array.h" #include "hostpid.h" +#include "eacces-error.h" +#include "mkdir-parents.h" #include "subscription-file.h" #include "mailbox-list-maildir.h" #include #include +#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder" #define MAILDIR_GLOBAL_TEMP_PREFIX "temp." #define IMAPDIR_GLOBAL_TEMP_PREFIX ".temp." @@ -168,12 +171,17 @@ maildir_list_get_path(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type) { + const char *root_dir; + if (name == NULL) { /* return root directories */ switch (type) { case MAILBOX_LIST_PATH_TYPE_DIR: case MAILBOX_LIST_PATH_TYPE_MAILBOX: return _list->set.root_dir; + case MAILBOX_LIST_PATH_TYPE_ALT_DIR: + case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: + return _list->set.alt_dir; case MAILBOX_LIST_PATH_TYPE_CONTROL: return _list->set.control_dir != NULL ? _list->set.control_dir : _list->set.root_dir; @@ -188,10 +196,17 @@ (*name == '/' || *name == '~')) return maildir_list_get_absolute_path(_list, name); + root_dir = _list->set.root_dir; switch (type) { case MAILBOX_LIST_PATH_TYPE_DIR: case MAILBOX_LIST_PATH_TYPE_MAILBOX: break; + case MAILBOX_LIST_PATH_TYPE_ALT_DIR: + case MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX: + if (_list->set.alt_dir == NULL) + return NULL; + root_dir = _list->set.alt_dir; + break; case MAILBOX_LIST_PATH_TYPE_CONTROL: if (_list->set.control_dir != NULL) { return maildir_list_get_dirname_path(_list, @@ -208,10 +223,13 @@ break; } - if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL) + if (type == MAILBOX_LIST_PATH_TYPE_ALT_DIR || + type == MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX) { + /* don't use inbox_path */ + } else if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL) return _list->set.inbox_path; - return maildir_list_get_dirname_path(_list, _list->set.root_dir, name); + return maildir_list_get_dirname_path(_list, root_dir, name); } static int @@ -381,6 +399,92 @@ } static int +maildir_list_create_maildirfolder_file(struct mailbox_list *list, + const char *dir) +{ + const char *path, *gid_origin; + mode_t mode, old_mask; + gid_t gid; + int fd; + + /* Maildir++ spec wants that maildirfolder named file is created for + all subfolders. */ + mailbox_list_get_permissions(list, NULL, &mode, &gid, &gid_origin); + + path = t_strconcat(dir, "/" MAILDIR_SUBFOLDER_FILENAME, NULL); + old_mask = umask(0); + fd = open(path, O_CREAT | O_WRONLY, mode); + umask(old_mask); + if (fd != -1) { + /* ok */ + } else if (errno == ENOENT) { + mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, + "Mailbox was deleted while it was being created"); + return -1; + } else { + mailbox_list_set_critical(list, + "open(%s, O_CREAT) failed: %m", path); + return -1; + } + + if (gid != (gid_t)-1) { + if (fchown(fd, (uid_t)-1, gid) == 0) { + /* ok */ + } else if (errno == EPERM) { + mailbox_list_set_critical(list, "%s", + eperm_error_get_chgrp("fchown", path, + gid, gid_origin)); + } else { + mailbox_list_set_critical(list, + "fchown(%s) failed: %m", path); + } + } + (void)close(fd); + return 0; +} + +static int +maildir_list_create_mailbox_dir(struct mailbox_list *list, const char *name, + bool directory ATTR_UNUSED) +{ + const char *path, *gid_origin, *p; + mode_t mode; + gid_t gid; + bool create_parent_dir; + + path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + create_parent_dir = !directory && + (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0; + if (create_parent_dir) { + /* we only need to make sure that the parent directory exists */ + p = strrchr(path, '/'); + if (p == NULL) + return 0; + path = t_strdup_until(path, p); + } + + mailbox_list_get_dir_permissions(list, NULL, &mode, + &gid, &gid_origin); + if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0) { + /* ok */ + } else if (errno == EEXIST) { + if (!create_parent_dir) { + mailbox_list_set_error(list, MAIL_ERROR_EXISTS, + "Mailbox already exists"); + return -1; + } + } else if (mailbox_list_set_error_from_errno(list)) { + return -1; + } else { + mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path); + return -1; + } + return create_parent_dir ? 0 : + maildir_list_create_maildirfolder_file(list, path); +} + +static int maildir_list_delete_mailbox(struct mailbox_list *list, const char *name) { /* let the backend handle the rest */ @@ -461,7 +565,8 @@ struct mailbox_list maildir_mailbox_list = { .name = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS, .hierarchy_sep = '.', - .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME, + .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME | + MAILBOX_LIST_PROP_NO_NOSELECT, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, { @@ -480,6 +585,7 @@ maildir_list_iter_deinit, NULL, maildir_list_set_subscribed, + maildir_list_create_mailbox_dir, maildir_list_delete_mailbox, maildir_list_delete_dir, maildir_list_rename_mailbox, @@ -490,7 +596,8 @@ struct mailbox_list imapdir_mailbox_list = { .name = MAILBOX_LIST_NAME_IMAPDIR, .hierarchy_sep = '.', - .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME, + .props = MAILBOX_LIST_PROP_NO_MAILDIR_NAME | + MAILBOX_LIST_PROP_NO_NOSELECT, .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH, { @@ -509,6 +616,7 @@ maildir_list_iter_deinit, NULL, maildir_list_set_subscribed, + maildir_list_create_mailbox_dir, maildir_list_delete_mailbox, maildir_list_delete_dir, maildir_list_rename_mailbox, diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/mail-storage-private.h --- a/src/lib-storage/mail-storage-private.h Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/mail-storage-private.h Sun Feb 07 01:50:49 2010 +0200 @@ -430,5 +430,10 @@ int mail_set_aborted(struct mail *mail); void mail_set_expunged(struct mail *mail); void mailbox_set_deleted(struct mailbox *box); +void mailbox_refresh_permissions(struct mailbox *box); + +/* Returns -1 if error, 0 if failed with EEXIST, 1 if ok */ +int mailbox_create_fd(struct mailbox *box, const char *path, int flags, + int *fd_r); #endif diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/mail-storage.c --- a/src/lib-storage/mail-storage.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/mail-storage.c Sun Feb 07 01:50:49 2010 +0200 @@ -4,6 +4,7 @@ #include "ioloop.h" #include "array.h" #include "llist.h" +#include "eacces-error.h" #include "mkdir-parents.h" #include "var-expand.h" #include "mail-index-private.h" @@ -520,6 +521,17 @@ return -1; } + if (box->list->v.create_mailbox_dir(box->list, box->name, + directory) < 0) { + const char *str; + enum mail_error error; + + str = mailbox_list_get_last_error(box->list, &error); + mail_storage_set_error(box->storage, error, str); + return -1; + } + mailbox_refresh_permissions(box); + return box->v.create(box, update, directory); } @@ -1079,3 +1091,74 @@ "Mailbox was deleted under us"); box->mailbox_deleted = TRUE; } + +void mailbox_refresh_permissions(struct mailbox *box) +{ + const char *origin, *dir_origin; + gid_t dir_gid; + + if (box->input != NULL) { + box->file_create_mode = 0600; + box->dir_create_mode = 0700; + box->file_create_gid = (gid_t)-1; + box->file_create_gid_origin = "defaults"; + return; + } + + mailbox_list_get_permissions(box->list, box->name, + &box->file_create_mode, + &box->file_create_gid, &origin); + + box->file_create_gid_origin = p_strdup(box->pool, origin); + mailbox_list_get_dir_permissions(box->list, box->name, + &box->dir_create_mode, + &dir_gid, &dir_origin); +} + +int mailbox_create_fd(struct mailbox *box, const char *path, int flags, + int *fd_r) +{ + mode_t old_mask; + int fd; + + i_assert(box->file_create_mode != 0); + i_assert((flags & O_CREAT) != 0); + + *fd_r = -1; + + old_mask = umask(0); + fd = open(path, flags, box->file_create_mode); + umask(old_mask); + + if (fd != -1) { + /* ok */ + } else if (errno == EEXIST) { + /* O_EXCL used, caller will handle this error */ + return 0; + } else if (errno == ENOENT) { + mailbox_set_deleted(box); + return -1; + } else if (mail_storage_set_error_from_errno(box->storage)) { + return -1; + } else { + mail_storage_set_critical(box->storage, + "open(%s, O_CREAT) failed: %m", path); + return -1; + } + + if (box->file_create_gid != (gid_t)-1) { + if (fchown(fd, (uid_t)-1, box->file_create_gid) == 0) { + /* ok */ + } else if (errno == EPERM) { + mail_storage_set_critical(box->storage, "%s", + eperm_error_get_chgrp("fchown", path, + box->file_create_gid, + box->file_create_gid_origin)); + } else { + mail_storage_set_critical(box->storage, + "fchown(%s) failed: %m", path); + } + } + *fd_r = fd; + return 1; +} diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/mailbox-list-private.h --- a/src/lib-storage/mailbox-list-private.h Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/mailbox-list-private.h Sun Feb 07 01:50:49 2010 +0200 @@ -57,6 +57,8 @@ int (*set_subscribed)(struct mailbox_list *list, const char *name, bool set); + int (*create_mailbox_dir)(struct mailbox_list *list, const char *name, + bool directory); int (*delete_mailbox)(struct mailbox_list *list, const char *name); int (*delete_dir)(struct mailbox_list *list, const char *name); int (*rename_mailbox)(struct mailbox_list *oldlist, const char *oldname, diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/lib-storage/mailbox-list.h --- a/src/lib-storage/mailbox-list.h Sat Feb 06 23:41:33 2010 +0200 +++ b/src/lib-storage/mailbox-list.h Sun Feb 07 01:50:49 2010 +0200 @@ -17,7 +17,9 @@ enum mailbox_list_properties { /* maildir_name must always be empty */ - MAILBOX_LIST_PROP_NO_MAILDIR_NAME = 0x01 + MAILBOX_LIST_PROP_NO_MAILDIR_NAME = 0x01, + /* no support for \noselect directories, only mailboxes */ + MAILBOX_LIST_PROP_NO_NOSELECT = 0x02 }; enum mailbox_list_flags { @@ -76,8 +78,10 @@ enum mailbox_list_path_type { /* Return directory's path (eg. ~/dbox/INBOX) */ MAILBOX_LIST_PATH_TYPE_DIR, + MAILBOX_LIST_PATH_TYPE_ALT_DIR, /* Return mailbox path (eg. ~/dbox/INBOX/dbox-Mails) */ MAILBOX_LIST_PATH_TYPE_MAILBOX, + MAILBOX_LIST_PATH_TYPE_ALT_MAILBOX, /* Return control directory */ MAILBOX_LIST_PATH_TYPE_CONTROL, /* Return index file directory */ diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/plugins/acl/acl-mailbox-list.c --- a/src/plugins/acl/acl-mailbox-list.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/plugins/acl/acl-mailbox-list.c Sun Feb 07 01:50:49 2010 +0200 @@ -463,6 +463,30 @@ } static int +acl_mailbox_list_create_dir(struct mailbox_list *list, const char *name, + bool directory) +{ + struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); + int ret; + + /* we're looking up CREATE permission from our parent's rights */ + ret = acl_mailbox_list_have_right(list, name, TRUE, + ACL_STORAGE_RIGHT_CREATE, NULL); + if (ret <= 0) { + if (ret < 0) + return -1; + /* Note that if user didn't have LOOKUP permission to parent + mailbox, this may reveal the mailbox's existence to user. + Can't help it. */ + mailbox_list_set_error(list, MAIL_ERROR_PERM, + MAIL_ERRSTR_NO_PERMISSION); + return -1; + } + return alist->module_ctx.super. + create_mailbox_dir(list, name, directory); +} + +static int acl_mailbox_list_delete(struct mailbox_list *list, const char *name) { struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); @@ -600,6 +624,7 @@ list->v.iter_next = acl_mailbox_list_iter_next; list->v.iter_deinit = acl_mailbox_list_iter_deinit; list->v.get_mailbox_name_status = acl_get_mailbox_name_status; + list->v.create_mailbox_dir = acl_mailbox_list_create_dir; list->v.delete_mailbox = acl_mailbox_list_delete; list->v.rename_mailbox = acl_mailbox_list_rename; diff -r bdb1ea37ccee -r 5fe70b5da1b8 src/plugins/acl/acl-mailbox.c --- a/src/plugins/acl/acl-mailbox.c Sat Feb 06 23:41:33 2010 +0200 +++ b/src/plugins/acl/acl-mailbox.c Sun Feb 07 01:50:49 2010 +0200 @@ -124,26 +124,14 @@ acl_object_list_deinit(&iter); acl_object_deinit(&parent_aclobj); } + static int acl_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory) { struct acl_mailbox *abox = ACL_CONTEXT(box); - int ret; - /* we're looking up CREATE permission from our parent's rights */ - ret = acl_mailbox_list_have_right(box->list, box->name, TRUE, - ACL_STORAGE_RIGHT_CREATE, NULL); - if (ret <= 0) { - if (ret < 0) - return -1; - /* Note that if user didn't have LOOKUP permission to parent - mailbox, this may reveal the mailbox's existence to user. - Can't help it. */ - mail_storage_set_error(box->storage, MAIL_ERROR_PERM, - MAIL_ERRSTR_NO_PERMISSION); - return -1; - } + /* we already checked permissions in list.mailbox_create_dir(). */ if (abox->module_ctx.super.create(box, update, directory) < 0) return -1;