]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
When creating shared directories, preserve parent dir's permissions if it has setgid...
authorTimo Sirainen <tss@iki.fi>
Fri, 4 Jun 2010 17:05:28 +0000 (18:05 +0100)
committerTimo Sirainen <tss@iki.fi>
Fri, 4 Jun 2010 17:05:28 +0000 (18:05 +0100)
This works only if location path uses %variables. The directories up to
last variable are created by preserving parent directory's modes, while
the rest of the directories are created with 0700. For example
with "/var/mail/%d/%2n/%n/Maildir", "/var/mail/domain/nn" preserves /var/mail's
permissions, while the "username/Maildir" directories have 0700 mode.

--HG--
branch : HEAD

src/lib-storage/index/dbox-multi/mdbox-file.c
src/lib-storage/index/dbox-multi/mdbox-map-private.h
src/lib-storage/index/dbox-multi/mdbox-map.c
src/lib-storage/index/index-storage.c
src/lib-storage/index/maildir/maildir-util.c
src/lib-storage/mail-namespace.c
src/lib-storage/mail-namespace.h
src/lib-storage/mailbox-list-private.h
src/lib-storage/mailbox-list.c

index 84bee997050cad4de083d98c663c3fc882ae2eac..b96a15f21d91d407b6d4ddd7c142130f7db26c2a 100644 (file)
@@ -13,6 +13,7 @@
 #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"
@@ -275,11 +276,12 @@ int mdbox_file_create_fd(struct dbox_file *file, const char *path, bool parents)
        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 */
index 8bbeb78305c1c0305bfa999c1762538493b0c321..59a6a8151d8502c06dd34700c765ef45b8e216a2 100644 (file)
@@ -20,7 +20,8 @@ struct mdbox_map {
 
        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;
 };
index 9811d5c5d2bfe317bdc932d136cfe568bae8da1f..696f0e9c8f483cf95ef864c8597f04d55f160353 100644 (file)
@@ -6,6 +6,7 @@
 #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"
@@ -48,14 +49,13 @@ mdbox_map_init(struct mdbox_storage *storage, struct mailbox_list *root_list,
               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),
@@ -66,8 +66,6 @@ mdbox_map_init(struct mdbox_storage *storage, struct mailbox_list *root_list,
 
        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;
@@ -90,11 +88,9 @@ void mdbox_map_deinit(struct mdbox_map **_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;
index 85a531c1ea8d95e618b340764a21e0821fee344c..119dabb8fbf136f1b223baa45f0da1ebda723569 100644 (file)
@@ -29,11 +29,10 @@ struct index_storage_module index_storage_module =
 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);
@@ -54,16 +53,10 @@ int index_list_create_missing_index_dir(struct mailbox_list *list,
                        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;
 }
index 7ec527ded1971e005a9aaccbd25c3729b20c19ac..d0ef09d4e2507ce808232854bb07dc455d5ef32e 100644 (file)
@@ -5,6 +5,7 @@
 #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"
@@ -131,11 +132,9 @@ int maildir_file_do(struct maildir_mailbox *mbox, uint32_t uid,
 }
 
 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)
@@ -146,20 +145,17 @@ static int maildir_create_path(struct mailbox *box, const char *path,
                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;
@@ -174,16 +170,20 @@ static int maildir_create_subdirs(struct mailbox *box)
 {
        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));
@@ -197,8 +197,7 @@ static int maildir_create_subdirs(struct mailbox *box)
                                                  "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;
index 872ece1aa41bbd29453284a3630e47ed7877d3f8..659e70a396bbdcc23bfac88ffefeb6194a0872d7 100644 (file)
@@ -60,6 +60,7 @@ static void mail_namespace_free(struct mail_namespace *ns)
 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)
 {
@@ -115,6 +116,7 @@ namespace_add(struct mail_user *user,
                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);
 
@@ -253,24 +255,29 @@ int mail_namespaces_init(struct mail_user *user, const char **error_r)
 {
        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;
        }
index 3faa828e12e72608939d64de471aa6d77a24a9d3..994c8f6cccc2a616fe4b22822fa364847c19f96b 100644 (file)
@@ -62,7 +62,7 @@ struct mail_namespace {
        /* 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;
 };
 
index 0a3d41fc2911643c382d55b6420c6b18a30d52f6..a2603d5c25a7c9ebc3e3179376b4b6b87c77a370 100644 (file)
@@ -140,9 +140,13 @@ void mailbox_lists_deinit(void);
 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);
index 9839ed161ee289f3473be4072bb887d1fb37e180..b1f61ef4d4d4f66577f940a06c56adc01570f4e3 100644 (file)
@@ -306,6 +306,37 @@ int mailbox_list_settings_parse(struct mail_user *user, const char *data,
        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;
@@ -485,6 +516,129 @@ void mailbox_list_get_dir_permissions(struct mailbox_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)
 {