]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Whenever file's group changing fails, show the group origin in the error message.
authorTimo Sirainen <tss@iki.fi>
Sun, 28 Jun 2009 00:39:38 +0000 (20:39 -0400)
committerTimo Sirainen <tss@iki.fi>
Sun, 28 Jun 2009 00:39:38 +0000 (20:39 -0400)
--HG--
branch : HEAD

36 files changed:
src/lda/main.c
src/lib-index/mail-index-private.h
src/lib-index/mail-index-strmap.c
src/lib-index/mail-index.c
src/lib-index/mail-index.h
src/lib-storage/index/cydir/cydir-storage.c
src/lib-storage/index/dbox/dbox-file.c
src/lib-storage/index/dbox/dbox-map.c
src/lib-storage/index/dbox/dbox-storage.c
src/lib-storage/index/dbox/dbox-storage.h
src/lib-storage/index/index-storage.c
src/lib-storage/index/maildir/maildir-keywords.c
src/lib-storage/index/maildir/maildir-save.c
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/maildir/maildir-uidlist.c
src/lib-storage/index/maildir/maildir-util.c
src/lib-storage/index/mbox/mbox-storage.c
src/lib-storage/list/mailbox-list-fs.c
src/lib-storage/list/subscription-file.c
src/lib-storage/mail-storage-private.h
src/lib-storage/mail-storage.c
src/lib-storage/mailbox-list-private.h
src/lib-storage/mailbox-list.c
src/lib-storage/mailbox-list.h
src/lib/eacces-error.c
src/lib/eacces-error.h
src/lib/file-dotlock.c
src/lib/file-dotlock.h
src/lib/mkdir-parents.c
src/lib/mkdir-parents.h
src/lib/safe-mkstemp.c
src/lib/safe-mkstemp.h
src/plugins/acl/acl-backend-vfile-acllist.c
src/plugins/acl/acl-backend-vfile.c
src/plugins/lazy-expunge/lazy-expunge-plugin.c
src/plugins/quota/quota-maildir.c

index ad2fae130aedd69e21fa86f32862c79a747de344..948e59b2b07a0a9070621da468c5832d6de6cbfb 100644 (file)
@@ -90,6 +90,7 @@ static const char *address_sanitize(const char *address)
 static int deliver_create_dir(struct mail_user *user, const char *dir)
 {
        struct mail_namespace *ns;
+       const char *origin;
        mode_t mode;
        gid_t gid;
 
@@ -97,8 +98,8 @@ static int deliver_create_dir(struct mail_user *user, const char *dir)
        if (ns == NULL)
                ns = user->namespaces;
 
-       mailbox_list_get_dir_permissions(ns->list, NULL, &mode, &gid);
-       if (mkdir_parents_chown(dir, mode, (uid_t)-1, gid) == 0) {
+       mailbox_list_get_dir_permissions(ns->list, NULL, &mode, &gid, &origin);
+       if (mkdir_parents_chgrp(dir, mode, gid, origin) == 0) {
                return 0;
        } else if (errno == EACCES) {
                i_error("%s", eacces_error_get_creating("mkdir_parents_chown",
index b918d3f08901a6dc3d16a0771f036b6a4e415b59..8395db0829653ecb7c1806943dc0e97ef0f528be 100644 (file)
@@ -174,6 +174,7 @@ struct mail_index {
        enum mail_index_sync_type fsync_mask;
        mode_t mode;
        gid_t gid;
+       char *gid_origin;
 
        pool_t extension_pool;
        ARRAY_DEFINE(extensions, struct mail_index_registered_ext);
index b40f7f74c0eb0a0a3262f1112cd4e58049e71157..ec3a39e465d2b63303e1681a45e95fdcde739203 100644 (file)
@@ -998,8 +998,9 @@ static int mail_index_strmap_recreate(struct mail_index_strmap_view *view)
 
        str = t_str_new(256);
        str_append(str, strmap->path);
-       fd = safe_mkstemp_hostpid(str, view->view->index->mode,
-                                 (uid_t)-1, view->view->index->gid);
+       fd = safe_mkstemp_hostpid_group(str, view->view->index->mode,
+                                       view->view->index->gid,
+                                       view->view->index->gid_origin);
        temp_path = str_c(str);
 
        if (fd == -1) {
index 1ed8be4e81e65fefe6e62919086ca8399db3265d..9774759fc1824592da70bebc9eecd827c4e7785e 100644 (file)
@@ -73,6 +73,7 @@ void mail_index_free(struct mail_index **_index)
        array_free(&index->keywords);
        array_free(&index->module_contexts);
 
+       i_free(index->gid_origin);
        i_free(index->error);
        i_free(index->dir);
        i_free(index->prefix);
@@ -86,10 +87,13 @@ void mail_index_set_fsync_types(struct mail_index *index,
 }
 
 void mail_index_set_permissions(struct mail_index *index,
-                               mode_t mode, gid_t gid)
+                               mode_t mode, gid_t gid, const char *gid_origin)
 {
        index->mode = mode & 0666;
        index->gid = gid;
+
+       i_free(index->gid_origin);
+       index->gid_origin = i_strdup(gid_origin);
 }
 
 uint32_t mail_index_ext_register(struct mail_index *index, const char *name,
@@ -662,7 +666,13 @@ void mail_index_fchown(struct mail_index *index, int fd, const char *path)
                   really matter. ignore silently. */
                return;
        }
-       mail_index_file_set_syscall_error(index, path, "fchown()");
+       if (errno != EACCES)
+               mail_index_file_set_syscall_error(index, path, "fchown()");
+       else {
+               mail_index_set_error(index, "%s",
+                       eperm_error_get_chgrp("fchown", path, index->gid,
+                                             index->gid_origin));
+       }
 
        /* continue, but change permissions so that only the common
           subset of group and world is used. this makes sure no one
index 7d775d4e22b8669c86ec5c1d80bbeb5578f00b05..c1679fa2e8c2081bf5a5e5c9785d8a4cce6341eb 100644 (file)
@@ -195,7 +195,7 @@ void mail_index_free(struct mail_index **index);
 void mail_index_set_fsync_types(struct mail_index *index,
                                enum mail_index_sync_type fsync_mask);
 void mail_index_set_permissions(struct mail_index *index,
-                               mode_t mode, gid_t gid);
+                               mode_t mode, gid_t gid, const char *gid_origin);
 
 /* Open index. Returns 1 if ok, 0 if index doesn't exist and CREATE flags
    wasn't given, -1 if error. */
index 422e8211171c8703be57b524a785b9d0fdbb58e7..47de345759e31d974be139b16334d8060421c8c8 100644 (file)
@@ -51,11 +51,12 @@ cydir_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
 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);
-       if (mkdir_parents_chown(path, mode, (uid_t)-1, gid) < 0 &&
+       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,
index 6913a261683767c6436e2a37ed6e109b668a9e2d..863198e34961361218f40ea8ab6309c74bab5835 100644 (file)
@@ -10,6 +10,7 @@
 #include "file-lock.h"
 #include "mkdir-parents.h"
 #include "fdatasync-path.h"
+#include "eacces-error.h"
 #include "str.h"
 #include "dbox-storage.h"
 #include "dbox-file.h"
@@ -441,13 +442,20 @@ int dbox_create_fd(struct dbox_storage *storage, const char *path)
        if (fd == -1) {
                mail_storage_set_critical(&storage->storage,
                        "open(%s, O_CREAT) failed: %m", path);
-       } else if (storage->create_gid != (gid_t)-1) {
-               if (fchown(fd, (uid_t)-1, storage->create_gid) < 0) {
+       } else if (storage->create_gid == (gid_t)-1) {
+               /* no group change */
+       } else if (fchown(fd, (uid_t)-1, storage->create_gid) < 0) {
+               if (errno == EPERM) {
+                       mail_storage_set_critical(&storage->storage, "%s",
+                               eperm_error_get_chgrp("fchown", path,
+                                       storage->create_gid,
+                                       storage->create_gid_origin));
+               } else {
                        mail_storage_set_critical(&storage->storage,
                                "fchown(%s, -1, %ld) failed: %m",
                                path, (long)storage->create_gid);
-                       /* continue anyway */
                }
+               /* continue anyway */
        }
        return fd;
 }
index 2990dbbbda6f7a1ab022e997339077b37d4ea65f..3602ef05ee4b3ef391b006aaf000687357e400ed 100644 (file)
@@ -69,8 +69,9 @@ void dbox_map_deinit(struct dbox_map **_map)
 
 static int dbox_map_mkdir_storage(struct dbox_storage *storage)
 {
-       if (mkdir_parents_chown(storage->storage_dir, storage->create_mode,
-                               (uid_t)-1, storage->create_gid) < 0 &&
+       if (mkdir_parents_chgrp(storage->storage_dir, storage->create_mode,
+                               storage->create_gid,
+                               storage->create_gid_origin) < 0 &&
            errno != EEXIST) {
                mail_storage_set_critical(&storage->storage,
                        "mkdir(%s) failed: %m", storage->storage_dir);
index daef2d517ce2457beebcd083d1c911f6ef879ded..e43ebcbd0b088477b86653ae10093f4c97ad1e36 100644 (file)
@@ -56,7 +56,7 @@ dbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
                    const char **error_r)
 {
        struct dbox_storage *storage = (struct dbox_storage *)_storage;
-       const char *dir;
+       const char *dir, *origin;
 
        storage->set = mail_storage_get_driver_settings(_storage);
        i_assert(storage->set->dbox_max_open_files >= 2);
@@ -80,7 +80,8 @@ dbox_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
 
        storage->map = dbox_map_init(storage);
        mailbox_list_get_dir_permissions(ns->list, NULL, &storage->create_mode,
-                                        &storage->create_gid);
+                                        &storage->create_gid, &origin);
+       storage->create_gid_origin = p_strdup(_storage->pool, origin);
        return 0;
 }
 
@@ -282,12 +283,13 @@ static int dbox_mailbox_create_indexes(struct mailbox *box,
                                       const struct mailbox_update *update)
 {
        struct dbox_mailbox *mbox = (struct dbox_mailbox *)box;
+       const char *origin;
        mode_t mode;
        gid_t gid;
        int ret;
 
-       mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid);
-       if (mkdir_parents_chown(box->path, mode, (uid_t)-1, gid) == 0) {
+       mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid, &origin);
+       if (mkdir_parents_chgrp(box->path, mode, gid, origin) == 0) {
                /* create indexes immediately with the dbox header */
                if (index_storage_mailbox_open(box) < 0)
                        return -1;
@@ -404,7 +406,7 @@ static int
 dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                    bool directory)
 {
-       const char *path, *alt_path;
+       const char *path, *alt_path, *origin;
        struct stat st;
 
        path = mailbox_list_get_path(box->list, box->name,
@@ -420,8 +422,9 @@ dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                mode_t mode;
                gid_t gid;
 
-               mailbox_list_get_dir_permissions(box->list, NULL, &mode, &gid);
-               if (mkdir_parents_chown(path, mode, (uid_t)-1, gid) == 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,
@@ -560,11 +563,13 @@ dbox_list_delete_mailbox(struct mailbox_list *list, const char *name)
        if (ret < 0 && errno == ENOENT) {
                /* either source mailbox doesn't exist or trash directory
                   doesn't exist. try creating the trash and retrying. */
+               const char *origin;
                mode_t mode;
                gid_t gid;
 
-               mailbox_list_get_dir_permissions(list, NULL, &mode, &gid);
-               if (mkdir_parents_chown(trash_dir, mode, (uid_t)-1, gid) < 0 &&
+               mailbox_list_get_dir_permissions(list, NULL, &mode,
+                                                &gid, &origin);
+               if (mkdir_parents_chgrp(trash_dir, mode, gid, origin) < 0 &&
                    errno != EEXIST) {
                        mailbox_list_set_critical(list,
                                "mkdir(%s) failed: %m", trash_dir);
index 2d9bc9319f4d12aa43bdff1b5fa42b12fc2f97e2..8544790f33f6c23ddf1e9b03ca7c7a76104308a0 100644 (file)
@@ -51,6 +51,7 @@ struct dbox_storage {
        /* mode/gid to use for new dbox storage files */
        mode_t create_mode;
        gid_t create_gid;
+       const char *create_gid_origin;
 
        ARRAY_DEFINE(open_files, struct dbox_file *);
 
index f815c57777f7d1ec419b503d8eed58a103f3ac87..aafa5d1da57b96d87dfa27752596404fecd07a8a 100644 (file)
@@ -73,6 +73,7 @@ static void index_list_free(struct index_list *list)
 static int create_missing_index_dir(struct mailbox *box)
 {
        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;
@@ -84,8 +85,9 @@ static int create_missing_index_dir(struct mailbox *box)
        if (strcmp(index_dir, root_dir) == 0 || *index_dir == '\0')
                return 0;
 
-       mailbox_list_get_dir_permissions(box->list, box->name, &mode, &gid);
-       while (mkdir_chown(index_dir, mode, (uid_t)-1, gid) < 0) {
+       mailbox_list_get_dir_permissions(box->list, box->name, &mode,
+                                        &gid, &origin);
+       while (mkdir_chgrp(index_dir, mode, gid, origin) < 0) {
                if (errno == EEXIST)
                        break;
 
@@ -97,10 +99,11 @@ static int create_missing_index_dir(struct mailbox *box)
                }
                /* create the parent directory first */
                mailbox_list_get_dir_permissions(box->list, NULL,
-                                                &parent_mode, &parent_gid);
+                                                &parent_mode, &parent_gid,
+                                                &parent_origin);
                parent_dir = t_strdup_until(index_dir, p);
-               if (mkdir_parents_chown(parent_dir, parent_mode,
-                                       (uid_t)-1, parent_gid) < 0 &&
+               if (mkdir_parents_chgrp(parent_dir, parent_mode,
+                                       parent_gid, parent_origin) < 0 &&
                    errno != EEXIST) {
                        mail_storage_set_critical(box->storage,
                                "mkdir(%s) failed: %m", parent_dir);
@@ -449,6 +452,7 @@ void index_storage_mailbox_alloc(struct index_mailbox *ibox, const char *name,
        struct mailbox *box = &ibox->box;
        const char *path;
        gid_t dir_gid;
+       const char *origin, *dir_origin;
 
        if (name != NULL)
                box->name = p_strdup(box->pool, name);
@@ -484,13 +488,14 @@ void index_storage_mailbox_alloc(struct index_mailbox *ibox, const char *name,
        if (box->file_create_mode == 0) {
                mailbox_list_get_permissions(box->list, name,
                                             &box->file_create_mode,
-                                            &box->file_create_gid);
+                                            &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_gid, &dir_origin);
                mail_index_set_permissions(ibox->index,
                                           box->file_create_mode,
-                                          box->file_create_gid);
+                                          box->file_create_gid, origin);
        }
 }
 
index c243c5433ce2cf32462fdf78cfa69e46e6a5ded0..8b528d8dd7d38174f352b02d0a7cec944c5d9350 100644 (file)
@@ -10,6 +10,7 @@
 #include "hash.h"
 #include "str.h"
 #include "istream.h"
+#include "eacces-error.h"
 #include "file-dotlock.h"
 #include "write-full.h"
 #include "nfs-workarounds.h"
@@ -285,6 +286,7 @@ static int maildir_keywords_write_fd(struct maildir_keywords *mk,
                                     const char *path, int fd)
 {
        struct maildir_mailbox *mbox = mk->mbox;
+       struct mailbox *box = &mbox->ibox.box;
        const char *const *keywords;
        unsigned int i, count;
        string_t *str;
@@ -308,11 +310,18 @@ static int maildir_keywords_write_fd(struct maildir_keywords *mk,
                return -1;
        }
 
-       if (st.st_gid != mbox->ibox.box.file_create_gid &&
-           mbox->ibox.box.file_create_gid != (gid_t)-1) {
-               if (fchown(fd, (uid_t)-1, mbox->ibox.box.file_create_gid) < 0) {
-                       mail_storage_set_critical(mk->storage,
-                               "fchown(%s) failed: %m", path);
+       if (st.st_gid != box->file_create_gid &&
+           box->file_create_gid != (gid_t)-1) {
+               if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+                       if (errno == EPERM) {
+                               mail_storage_set_critical(mk->storage, "%s",
+                                       eperm_error_get_chgrp("fchown", path,
+                                               box->file_create_gid,
+                                               box->file_create_gid_origin));
+                       } else {
+                               mail_storage_set_critical(mk->storage,
+                                       "fchown(%s) failed: %m", path);
+                       }
                }
        }
 
index c73694366a8aad8e72f326002b8a0fb1f5d2008f..cb21f0e8a1ad1545cd6341cd12efafa30e24ce47 100644 (file)
@@ -8,6 +8,7 @@
 #include "istream-crlf.h"
 #include "ostream.h"
 #include "fdatasync-path.h"
+#include "eacces-error.h"
 #include "str.h"
 #include "index-mail.h"
 #include "maildir-storage.h"
@@ -340,8 +341,16 @@ static int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir,
                }
        } else if (box->file_create_gid != (gid_t)-1) {
                if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
-                       mail_storage_set_critical(box->storage,
-                               "fchown(%s) failed: %m", str_c(path));
+                       if (errno == EPERM) {
+                               mail_storage_set_critical(box->storage, "%s",
+                                       eperm_error_get_chgrp("fchown",
+                                               str_c(path),
+                                               box->file_create_gid,
+                                               box->file_create_gid_origin));
+                       } else {
+                               mail_storage_set_critical(box->storage,
+                                       "fchown(%s) failed: %m", str_c(path));
+                       }
                }
        }
 
index 90776a44094a1281ddc59c7966f88b8888bff25c..f55166142fdfb4ce0ab23eff3366de63f44d15b6 100644 (file)
@@ -6,6 +6,7 @@
 #include "hostpid.h"
 #include "str.h"
 #include "mkdir-parents.h"
+#include "eacces-error.h"
 #include "unlink-directory.h"
 #include "unlink-old-files.h"
 #include "mailbox-uidvalidity.h"
@@ -213,8 +214,10 @@ static bool maildir_storage_autodetect(const struct mail_namespace *ns,
        return TRUE;
 }
 
-static int mkdir_verify(struct mail_storage *storage, struct mail_namespace *ns,
-                       const char *dir, mode_t mode, gid_t gid, bool verify)
+static int
+mkdir_verify(struct mail_storage *storage, struct mail_namespace *ns,
+            const char *dir, mode_t mode, gid_t gid, const char *gid_origin,
+            bool verify)
 {
        struct stat st;
 
@@ -229,7 +232,7 @@ static int mkdir_verify(struct mail_storage *storage, struct mail_namespace *ns,
                }
        }
 
-       if (mkdir_parents_chown(dir, mode, (uid_t)-1, gid) == 0)
+       if (mkdir_parents_chgrp(dir, mode, gid, gid_origin) == 0)
                return 0;
 
        if (errno == EEXIST) {
@@ -289,7 +292,8 @@ static int maildir_check_tmp(struct mail_storage *storage, const char *dir)
 /* 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, bool verify)
+              const char *dir, mode_t mode, gid_t gid, const char *gid_origin,
+              bool verify)
 {
        const char *path;
        unsigned int i;
@@ -308,7 +312,8 @@ create_maildir(struct mail_storage *storage, struct mail_namespace *ns,
 
        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, verify) < 0)
+               if (mkdir_verify(storage, ns, path, mode, gid,
+                                gid_origin, verify) < 0)
                        return -1;
        }
        return 0;
@@ -360,21 +365,26 @@ static int maildir_mailbox_open_existing(struct mailbox *box)
 {
        struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
        struct stat st;
+       const char *shared_path;
 
        /* for shared mailboxes get the create mode from the
           permissions of dovecot-shared file. */
-       if (stat(t_strconcat(box->path, "/dovecot-shared", NULL), &st) == 0) {
+       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);
+                                          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);
                box->private_flags_mask = MAIL_SEEN;
        }
 
@@ -395,6 +405,7 @@ static int maildir_mailbox_open_existing(struct mailbox *box)
 static int maildir_mailbox_open(struct mailbox *box)
 {
        struct stat st;
+       const char *gid_origin;
        mode_t mode;
        gid_t gid;
        int ret;
@@ -423,9 +434,9 @@ static int maildir_mailbox_open(struct mailbox *box)
        if (inbox || 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);
+                                                &mode, &gid, &gid_origin);
                if (create_maildir(box->storage, box->list->ns, box->path,
-                                  mode, gid, TRUE) < 0)
+                                  mode, gid, gid_origin, TRUE) < 0)
                        return -1;
 
                return maildir_mailbox_open_existing(box);
@@ -442,7 +453,8 @@ static int maildir_mailbox_open(struct mailbox *box)
 
 static int
 maildir_create_shared(struct mail_storage *storage, struct mail_namespace *ns,
-                     const char *dir, mode_t mode, gid_t gid)
+                     const char *dir, mode_t mode, gid_t gid,
+                     const char *gid_origin)
 {
        const char *path;
        mode_t old_mask;
@@ -453,7 +465,7 @@ maildir_create_shared(struct mail_storage *storage, struct mail_namespace *ns,
        if ((mode & 0060) != 0) mode |= 0010;
        if ((mode & 0006) != 0) mode |= 0001;
 
-       if (create_maildir(storage, ns, dir, mode, gid, FALSE) < 0)
+       if (create_maildir(storage, ns, dir, mode, gid, gid_origin, FALSE) < 0)
                return -1;
 
        old_mask = umask(0777 ^ mode);
@@ -467,8 +479,14 @@ maildir_create_shared(struct mail_storage *storage, struct mail_namespace *ns,
        }
 
        if (fchown(fd, (uid_t)-1, gid) < 0) {
-               mail_storage_set_critical(storage, "fchown(%s) failed: %m",
-                                         path);
+               if (errno == EPERM) {
+                       mail_storage_set_critical(storage, "%s",
+                               eperm_error_get_chgrp("fchown", path,
+                                                     gid, gid_origin));
+               } else {
+                       mail_storage_set_critical(storage,
+                               "fchown(%s) failed: %m", path);
+               }
        }
        (void)close(fd);
        return 0;
@@ -504,7 +522,7 @@ maildir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                       bool directory)
 {
        struct stat st;
-       const char *path, *root_dir, *shared_path;
+       const char *path, *root_dir, *shared_path, *gid_origin;
        mode_t old_mask;
        int fd;
 
@@ -517,14 +535,17 @@ maildir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
           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) < 0)
+                                         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);
+               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, FALSE) < 0)
+                                  st.st_mode, st.st_gid, gid_origin,
+                                  FALSE) < 0)
                        return -1;
        }
 
@@ -536,8 +557,15 @@ maildir_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
        umask(old_mask);
        if (fd != -1) {
                /* if dovecot-shared exists, use the same group */
-               if (st.st_gid != (gid_t)-1 &&
-                   fchown(fd, (uid_t)-1, st.st_gid) < 0) {
+               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);
                }
index 2d9f56a8fd2896710298b639034dacf09113f5f7..098f896406b07f34070053ec16628ffb86addd25 100644 (file)
@@ -1316,8 +1316,14 @@ static int maildir_uidlist_recreate(struct maildir_uidlist *uidlist)
                        return -1;
        }
 
-       if (box->file_create_gid != (gid_t)-1) {
-               if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+       if (box->file_create_gid != (gid_t)-1 &&
+           fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
+               if (errno == EPERM) {
+                       mail_storage_set_critical(box->storage, "%s",
+                               eperm_error_get_chgrp("fchown", temp_path,
+                                               box->file_create_gid,
+                                               box->file_create_gid_origin));
+               } else {
                        mail_storage_set_critical(box->storage,
                                "fchown(%s) failed: %m", temp_path);
                }
index f0b19e32a0977e5c6612ce86f62fd1d502f877a2..956302a7e1dc19f8658bde69902a027c430308a7 100644 (file)
@@ -133,12 +133,12 @@ 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)
 {
-       const char *p, *parent;
+       const char *p, *parent, *origin;
        mode_t parent_mode;
        gid_t parent_gid;
 
-       if (mkdir_chown(path, box->dir_create_mode,
-                       (uid_t)-1, box->file_create_gid) == 0)
+       if (mkdir_chgrp(path, box->dir_create_mode, box->file_create_gid,
+                       box->file_create_gid_origin) == 0)
                return 0;
 
        switch (errno) {
@@ -153,10 +153,11 @@ static int maildir_create_path(struct mailbox *box, const char *path,
                }
                /* create index/control root directory */
                parent = t_strdup_until(path, p);
-               mailbox_list_get_dir_permissions(box->list, NULL,
-                                                &parent_mode, &parent_gid);
-               if (mkdir_parents_chown(parent, parent_mode, (uid_t)-1,
-                                       parent_gid) == 0 || errno == EEXIST) {
+               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) {
                        /* should work now, try again */
                        return maildir_create_path(box, path, TRUE);
                }
index 824287ce2fc6bf07aba8c6bdd520a94992cc1e69..47af38d451409296cffe5bf18c1597297dacc72d 100644 (file)
@@ -509,7 +509,7 @@ mbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
                    bool directory)
 {
        struct mail_storage *storage = box->storage;
-       const char *path, *p;
+       const char *path, *p, *origin;
        struct stat st;
        mode_t mode;
        gid_t gid;
@@ -539,8 +539,9 @@ mbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update,
        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);
-               if (mkdir_parents_chown(p, mode, (uid_t)-1, gid) < 0 &&
+               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,
index 5bb65a6fe094d1f94196e40a4a7f6e1038b72490..424dded5d8cf945be1f161ec5b6e2fa5ef48c0e0 100644 (file)
@@ -314,7 +314,7 @@ static int fs_list_rename_mailbox(struct mailbox_list *oldlist,
                                  const char *newname, bool rename_children)
 {
        struct mail_storage *oldstorage;
-       const char *oldpath, *newpath, *p;
+       const char *oldpath, *newpath, *p, *origin;
        enum mailbox_list_path_type path_type;
        struct stat st;
        mode_t mode;
@@ -342,9 +342,10 @@ static int fs_list_rename_mailbox(struct mailbox_list *oldlist,
        /* create the hierarchy */
        p = strrchr(newpath, '/');
        if (p != NULL) {
-               mailbox_list_get_dir_permissions(newlist, NULL, &mode, &gid);
+               mailbox_list_get_dir_permissions(newlist, NULL, &mode,
+                                                &gid, &origin);
                p = t_strdup_until(newpath, p);
-               if (mkdir_parents_chown(p, mode, (uid_t)-1, gid) < 0 &&
+               if (mkdir_parents_chgrp(p, mode, gid, origin) < 0 &&
                    errno != EEXIST) {
                        if (mailbox_list_set_error_from_errno(oldlist))
                                return -1;
index f93b0c2cebfc1f24b93361c000e2ef3cb6061f23..174dd55dd1d7a5e7cea9b049f91aa59b0f3a1782 100644 (file)
@@ -89,7 +89,7 @@ int subsfile_set_subscribed(struct mailbox_list *list, const char *path,
 {
        struct dotlock_settings dotlock_set;
        struct dotlock *dotlock;
-       const char *line, *p, *dir;
+       const char *line, *p, *dir, *origin;
        struct istream *input;
        struct ostream *output;
        int fd_in, fd_out;
@@ -107,22 +107,22 @@ int subsfile_set_subscribed(struct mailbox_list *list, const char *path,
        dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
        dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;
 
-       mailbox_list_get_permissions(list, NULL, &mode, &gid);
-       mailbox_list_get_dir_permissions(list, NULL, &dir_mode, &gid);
-       fd_out = file_dotlock_open_mode(&dotlock_set, path, 0,
-                                       mode, (uid_t)-1, gid, &dotlock);
+       mailbox_list_get_permissions(list, NULL, &mode, &gid, &origin);
+       mailbox_list_get_dir_permissions(list, NULL, &dir_mode, &gid, &origin);
+       fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
+                                        mode, gid, origin, &dotlock);
        if (fd_out == -1 && errno == ENOENT) {
                /* directory hasn't been created yet. */
                p = strrchr(path, '/');
                dir = p == NULL ? NULL : t_strdup_until(path, p);
                if (dir != NULL &&
-                   mkdir_parents_chown(dir, dir_mode, (uid_t)-1, gid) < 0 &&
+                   mkdir_parents_chgrp(dir, dir_mode, gid, origin) < 0 &&
                    errno != EEXIST) {
                        subswrite_set_syscall_error(list, "mkdir()", dir);
                        return -1;
                }
-               fd_out = file_dotlock_open_mode(&dotlock_set, path, 0,
-                                               mode, (uid_t)-1, gid, &dotlock);
+               fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
+                                                mode, gid, origin, &dotlock);
        }
        if (fd_out == -1) {
                if (errno == EAGAIN) {
index 566c7bbeb960e5f0dc0eb1adc876ad182ce29ff7..12a2ba7b0a2950025325b5b2087f07ccf5175e4a 100644 (file)
@@ -235,6 +235,8 @@ struct mailbox {
        /* mode and GID to use for newly created files/dirs */
        mode_t file_create_mode, dir_create_mode;
        gid_t file_create_gid;
+       /* origin (e.g. path) where the file_create_gid was got from */
+       const char *file_create_gid_origin;
 
        /* Mailbox notification settings: */
        unsigned int notify_min_interval;
index 22f3252c52cdca822d94887acadd1517b85b2379..460312549e8d2d244c445c677c4e2d0f2f8a5daa 100644 (file)
@@ -161,7 +161,7 @@ static int
 mail_storage_create_root(struct mailbox_list *list,
                         enum mail_storage_flags flags, const char **error_r)
 {
-       const char *root_dir;
+       const char *root_dir, *origin;
        struct stat st;
        mode_t mode;
        gid_t gid;
@@ -187,8 +187,8 @@ mail_storage_create_root(struct mailbox_list *list,
        }
 
        /* we need to create the root directory. */
-       mailbox_list_get_dir_permissions(list, NULL, &mode, &gid);
-       if (mkdir_parents_chown(root_dir, mode, (uid_t)-1, gid) < 0 &&
+       mailbox_list_get_dir_permissions(list, NULL, &mode, &gid, &origin);
+       if (mkdir_parents_chgrp(root_dir, mode, gid, origin) < 0 &&
            errno != EEXIST) {
                *error_r = mail_error_create_eacces_msg("mkdir", root_dir);
                return -1;
index 3b93fff4c2049052a07c7ee3d2c12234a2cc1ba7..d7aae18bb21eda237fb4286500c292d3164f2ccb 100644 (file)
@@ -92,6 +92,8 @@ struct mailbox_list {
        /* -1 if not set yet. use mailbox_list_get_permissions() to set them */
        mode_t file_create_mode, dir_create_mode;
        gid_t file_create_gid;
+       /* origin (e.g. path) where the file_create_gid was got from */
+       const char *file_create_gid_origin;
 
        char *error_string;
        enum mail_error error;
index b1d08317fa6c7cf50abea816ed52794c26e5f71f..5593d7f2e1d69d520c6d378146aa7eed4639845a 100644 (file)
@@ -327,7 +327,7 @@ void mailbox_list_get_closest_storage(struct mailbox_list *list,
 static void
 mailbox_list_get_permissions_full(struct mailbox_list *list, const char *name,
                                  mode_t *file_mode_r, mode_t *dir_mode_r,
-                                 gid_t *gid_r)
+                                 gid_t *gid_r, const char **gid_origin_r)
 {
        const char *path;
        struct stat st;
@@ -345,16 +345,19 @@ mailbox_list_get_permissions_full(struct mailbox_list *list, const char *name,
                        /* return defaults */
                        mailbox_list_get_permissions_full(list, NULL,
                                                          file_mode_r,
-                                                         dir_mode_r, gid_r);
+                                                         dir_mode_r, gid_r,
+                                                         gid_origin_r);
                        return;
                }
                /* return safe defaults */
                *file_mode_r = 0600;
                *dir_mode_r = 0700;
                *gid_r = (gid_t)-1;
+               *gid_origin_r = "defaults";
        } else {
                *file_mode_r = st.st_mode & 0666;
                *dir_mode_r = st.st_mode & 0777;
+               *gid_origin_r = path;
 
                if (!S_ISDIR(st.st_mode)) {
                        /* we're getting permissions from a file.
@@ -382,6 +385,8 @@ mailbox_list_get_permissions_full(struct mailbox_list *list, const char *name,
                list->file_create_mode = *file_mode_r;
                list->dir_create_mode = *dir_mode_r;
                list->file_create_gid = *gid_r;
+               list->file_create_gid_origin =
+                       p_strdup(list->pool, *gid_origin_r);
        }
 
        if (list->mail_set->mail_debug && name == NULL) {
@@ -393,34 +398,40 @@ mailbox_list_get_permissions_full(struct mailbox_list *list, const char *name,
        }
 }
 
-void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
-                                 mode_t *mode_r, gid_t *gid_r)
+void mailbox_list_get_permissions(struct mailbox_list *list,
+                                 const char *name,
+                                 mode_t *mode_r, gid_t *gid_r,
+                                 const char **gid_origin_r)
 {
        mode_t dir_mode;
 
        if (list->file_create_mode != (mode_t)-1 && name == NULL) {
                *mode_r = list->file_create_mode;
                *gid_r = list->file_create_gid;
+               *gid_origin_r = list->file_create_gid_origin;
                return;
        }
 
-       mailbox_list_get_permissions_full(list, name, mode_r, &dir_mode, gid_r);
+       mailbox_list_get_permissions_full(list, name, mode_r, &dir_mode, gid_r,
+                                         gid_origin_r);
 }
 
 void mailbox_list_get_dir_permissions(struct mailbox_list *list,
                                      const char *name,
-                                     mode_t *mode_r, gid_t *gid_r)
+                                     mode_t *mode_r, gid_t *gid_r,
+                                     const char **gid_origin_r)
 {
        mode_t file_mode;
 
        if (list->dir_create_mode != (mode_t)-1 && name == NULL) {
                *mode_r = list->dir_create_mode;
                *gid_r = list->file_create_gid;
+               *gid_origin_r = list->file_create_gid_origin;
                return;
        }
 
        mailbox_list_get_permissions_full(list, name, &file_mode,
-                                         mode_r, gid_r);
+                                         mode_r, gid_r, gid_origin_r);
 }
 
 bool mailbox_list_is_valid_pattern(struct mailbox_list *list,
index 5b2d5e85e9983427d113695629c4cec51b210bf3..c58fb1ad5c796c1cd8a0d40c1a28c07cd036a2fe 100644 (file)
@@ -145,13 +145,16 @@ void mailbox_list_get_closest_storage(struct mailbox_list *list,
 /* Returns the mode and GID that should be used when creating new files to
    the specified mailbox, or to mailbox list root if name is NULL. (gid_t)-1 is
    returned if it's not necessary to change the default gid. */
-void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
-                                 mode_t *mode_r, gid_t *gid_r);
+void mailbox_list_get_permissions(struct mailbox_list *list,
+                                 const char *name,
+                                 mode_t *mode_r, gid_t *gid_r,
+                                 const char **gid_origin_r);
 /* Like mailbox_list_get_permissions(), but add execute-bits for mode
    if either read or write bit is set (e.g. 0640 -> 0750). */
 void mailbox_list_get_dir_permissions(struct mailbox_list *list,
                                      const char *name,
-                                     mode_t *mode_r, gid_t *gid_r);
+                                     mode_t *mode_r, gid_t *gid_r,
+                                     const char **gid_origin_r);
 
 /* Returns TRUE if the name doesn't contain any invalid characters.
    The create name check can be more strict. */
index 49863b6f34a10f60f20cc2ff8cbad78c2fb1471e..78827423ac0a8c60c6cbc786df420e19f4e5b475 100644 (file)
@@ -88,8 +88,9 @@ eacces_error_get_full(const char *func, const char *path, bool creating)
        const struct group *group;
        string_t *errmsg;
        struct stat st, dir_st;
-       int ret = -1;
+       int orig_errno, ret = -1;
 
+       orig_errno = errno;
        errmsg = t_str_new(256);
        str_printfa(errmsg, "%s(%s) failed: Permission denied (euid=%s",
                    func, path, dec2str(geteuid()));
@@ -145,6 +146,7 @@ eacces_error_get_full(const char *func, const char *path, bool creating)
                        str_printfa(errmsg, " UNIX perms seem ok, ACL problem?");
        }
        str_append_c(errmsg, ')');
+       errno = orig_errno;
        return str_c(errmsg);
 }
 
@@ -157,3 +159,29 @@ const char *eacces_error_get_creating(const char *func, const char *path)
 {
        return eacces_error_get_full(func, path, TRUE);
 }
+
+const char *eperm_error_get_chgrp(const char *func, const char *path,
+                                 gid_t gid, const char *gid_origin)
+{
+       string_t *errmsg;
+       const struct group *group;
+       int orig_errno = errno;
+
+       errmsg = t_str_new(256);
+       
+       str_printfa(errmsg, "%s(%s, -1, %s", func, path, dec2str(gid));
+       group = getgrgid(gid);
+       if (group != NULL)
+               str_printfa(errmsg, "(%s)", group->gr_name);
+
+       str_printfa(errmsg, ") failed: Operation not permitted (egid=%s",
+                   dec2str(getegid()));
+       group = getgrgid(getegid());
+       if (group != NULL)
+               str_printfa(errmsg, "(%s)", group->gr_name);
+       if (gid_origin != NULL)
+               str_printfa(errmsg, ", group based on %s", gid_origin);
+       str_append_c(errmsg, ')');
+       errno = orig_errno;
+       return str_c(errmsg);
+}
index 20d9169ea56b874977307fbe44b11c30dea27be5..a9e39d44bfd45e811950d6df45d7e0e187959e06 100644 (file)
@@ -4,5 +4,10 @@
 /* Return a user-friendly error message for EACCES failures. */
 const char *eacces_error_get(const char *func, const char *path);
 const char *eacces_error_get_creating(const char *func, const char *path);
+/* Return a user-friendly error message for fchown() or chown() EPERM
+   failures when only the group is being changed. gid_origin specifies why
+   exactly this group is being used. */
+const char *eperm_error_get_chgrp(const char *func, const char *path,
+                                 gid_t gid, const char *gid_origin);
 
 #endif
index cb32daffea86f06ddbc153eb130ffe59f146e650..bc5ae705b1269cbab6f7276ccce0daea86cb8c6c 100644 (file)
@@ -6,6 +6,7 @@
 #include "hex-binary.h"
 #include "hostpid.h"
 #include "randgen.h"
+#include "eacces-error.h"
 #include "write-full.h"
 #include "safe-mkstemp.h"
 #include "nfs-workarounds.h"
@@ -765,10 +766,11 @@ int file_dotlock_open(const struct dotlock_settings *set, const char *path,
        return dotlock->fd;
 }
 
-int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path,
-                          enum dotlock_create_flags flags,
-                          mode_t mode, uid_t uid, gid_t gid,
-                          struct dotlock **dotlock_r)
+static int
+file_dotlock_open_mode_full(const struct dotlock_settings *set, const char *path,
+                           enum dotlock_create_flags flags,
+                           mode_t mode, uid_t uid, gid_t gid,
+                           const char *gid_origin, struct dotlock **dotlock_r)
 {
        struct dotlock *dotlock;
        mode_t old_mask;
@@ -780,9 +782,15 @@ int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path,
 
        if (fd != -1) {
                if (fchown(fd, uid, gid) < 0) {
-                       i_error("fchown(%s, %ld, %ld) failed: %m",
-                               file_dotlock_get_lock_path(dotlock),
-                               (long)uid, (long)gid);
+                       if (errno == EPERM && uid == (uid_t)-1) {
+                               i_error("%s", eperm_error_get_chgrp("fchown",
+                                       file_dotlock_get_lock_path(dotlock),
+                                       gid, gid_origin));
+                       } else {
+                               i_error("fchown(%s, %ld, %ld) failed: %m",
+                                       file_dotlock_get_lock_path(dotlock),
+                                       (long)uid, (long)gid);
+                       }
                        file_dotlock_delete(&dotlock);
                        return -1;
                }
@@ -791,6 +799,24 @@ int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path,
        return fd;
 }
 
+int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path,
+                          enum dotlock_create_flags flags,
+                          mode_t mode, uid_t uid, gid_t gid,
+                          struct dotlock **dotlock_r)
+{
+       return file_dotlock_open_mode_full(set, path, flags, mode, uid, gid,
+                                          NULL, dotlock_r);
+}
+
+int file_dotlock_open_group(const struct dotlock_settings *set, const char *path,
+                           enum dotlock_create_flags flags,
+                           mode_t mode, gid_t gid, const char *gid_origin,
+                           struct dotlock **dotlock_r)
+{
+       return file_dotlock_open_mode_full(set, path, flags, mode, (uid_t)-1,
+                                          gid, gid_origin, dotlock_r);
+}
+
 int file_dotlock_replace(struct dotlock **dotlock_p,
                         enum dotlock_replace_flags flags)
 {
index 767832ec9020e1b92fca0261fdead75e95581e3e..f7309c1acafce902c5a20a137eb92d8b8cb2600b 100644 (file)
@@ -73,6 +73,10 @@ int file_dotlock_open_mode(const struct dotlock_settings *set, const char *path,
                           enum dotlock_create_flags flags,
                           mode_t mode, uid_t uid, gid_t gid,
                           struct dotlock **dotlock_r);
+int file_dotlock_open_group(const struct dotlock_settings *set, const char *path,
+                           enum dotlock_create_flags flags,
+                           mode_t mode, gid_t gid, const char *gid_origin,
+                           struct dotlock **dotlock_r);
 /* Replaces the file dotlock protects with the dotlock file itself. */
 int file_dotlock_replace(struct dotlock **dotlock,
                         enum dotlock_replace_flags flags);
index 96262854443cd260b0b33fe1f67e445cc9739909..ef6bfa644b1ea5ba564d6b7f633cb19a7195baa6 100644 (file)
@@ -1,15 +1,22 @@
 /* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "str.h"
+#include "eacces-error.h"
 #include "mkdir-parents.h"
 
 #include <sys/stat.h>
 #include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
 
-int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+static int
+mkdir_chown_full(const char *path, mode_t mode, uid_t uid,
+                gid_t gid, const char *gid_origin)
 {
+       string_t *str;
        mode_t old_mask;
-       int ret;
+       int ret, orig_errno;
 
        old_mask = umask(0);
        ret = mkdir(path, mode);
@@ -26,20 +33,57 @@ int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
                return -1;
        }
        if (chown(path, uid, gid) < 0) {
-               i_error("chown(%s, %ld, %ld) failed: %m", path,
-                       uid == (uid_t)-1 ? -1L : (long)uid,
-                       gid == (gid_t)-1 ? -1L : (long)gid);
+               if (errno == EPERM && uid == (uid_t)-1) {
+                       i_error("%s", eperm_error_get_chgrp("chown", path, gid,
+                                                           gid_origin));
+                       return -1;
+               }
+               orig_errno = errno;
+
+               str = t_str_new(256);
+               str_printfa(str, "chown(%s, %ld", path,
+                           uid == (uid_t)-1 ? -1L : (long)uid);
+               if (uid != (uid_t)-1) {
+                       struct passwd *pw = getpwuid(uid);
+
+                       if (pw != NULL)
+                               str_printfa(str, "(%s)", pw->pw_name);
+
+               }
+               str_printfa(str, ", %ld",
+                           gid == (gid_t)-1 ? -1L : (long)gid);
+               if (gid != (gid_t)-1) {
+                       struct group *gr = getgrgid(uid);
+
+                       if (gr != NULL)
+                               str_printfa(str, "(%s)", gr->gr_name);
+               }
+               errno = orig_errno;
+               i_error("%s) failed: %m", str_c(str));
                return -1;
        }
        return 0;
 }
 
-int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       return mkdir_chown_full(path, mode, uid, gid, NULL);
+}
+
+int mkdir_chgrp(const char *path, mode_t mode,
+               gid_t gid, const char *gid_origin)
+{
+       return mkdir_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
+}
+
+static int
+mkdir_parents_chown_full(const char *path, mode_t mode, uid_t uid, gid_t gid,
+                        const char *gid_origin)
 {
        const char *p;
        int ret;
 
-       if (mkdir_chown(path, mode, uid, gid) < 0) {
+       if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0) {
                if (errno != ENOENT)
                        return -1;
 
@@ -49,19 +93,31 @@ int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
                        return -1; /* shouldn't happen */
 
                T_BEGIN {
-                       ret = mkdir_parents_chown(t_strdup_until(path, p),
-                                                 mode, uid, gid);
+                       ret = mkdir_parents_chown_full(t_strdup_until(path, p),
+                                                      mode, uid,
+                                                      gid, gid_origin);
                } T_END;
                if (ret < 0)
                        return -1;
 
                /* should work now */
-               if (mkdir_chown(path, mode, uid, gid) < 0)
+               if (mkdir_chown_full(path, mode, uid, gid, gid_origin) < 0)
                        return -1;
        }
        return 0;
 }
 
+int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid)
+{
+       return mkdir_parents_chown_full(path, mode, uid, gid, NULL);
+}
+
+int mkdir_parents_chgrp(const char *path, mode_t mode,
+                       gid_t gid, const char *gid_origin)
+{
+       return mkdir_parents_chown_full(path, mode, (uid_t)-1, gid, gid_origin);
+}
+
 int mkdir_parents(const char *path, mode_t mode)
 {
        return mkdir_parents_chown(path, mode, (uid_t)-1, (gid_t)-1);
index a7ac1d7b2ee50e4ca906ac7c902b01a5a958a546..b01d7e35c9c369858de9fa56aa0237b6152e601c 100644 (file)
@@ -7,10 +7,17 @@
 int mkdir_parents(const char *path, mode_t mode);
 
 /* Like mkdir_parents(), but use the given uid/gid for newly created
-   directories. */
+   directories. (uid_t)-1 or (gid_t)-1 can be used to indicate that it
+   doesn't need to be changed. */
 int mkdir_parents_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
+/* Like mkdir_parents_chown(), but change only group. If chown() fails with
+   EACCES, use gid_origin in the error message. */
+int mkdir_parents_chgrp(const char *path, mode_t mode,
+                       gid_t gid, const char *gid_origin);
 
 /* Like mkdir_parents_chown(), but don't actually create any parents. */
 int mkdir_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int mkdir_chgrp(const char *path, mode_t mode,
+               gid_t gid, const char *gid_origin);
 
 #endif
index be950df2beb57dae82595a0a4410e3cdd0464b05..d0a83ef1ffed71677b69e9c02d54dc826cc61d85 100644 (file)
@@ -5,13 +5,16 @@
 #include "hex-binary.h"
 #include "randgen.h"
 #include "hostpid.h"
+#include "eacces-error.h"
 #include "safe-mkstemp.h"
 
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
 
-int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
+static int
+safe_mkstemp_full(string_t *prefix, mode_t mode, uid_t uid, gid_t gid,
+                 const char *gid_origin)
 {
        size_t prefix_len;
        struct stat st;
@@ -45,22 +48,46 @@ int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
                        return -1;
                }
        }
-       if (uid != (uid_t)-1 || gid != (gid_t)-1) {
-               if (fchown(fd, uid, gid) < 0) {
+       if (uid == (uid_t)-1 && gid == (gid_t)-1)
+               return fd;
+
+       if (fchown(fd, uid, gid) < 0) {
+               if (errno == EPERM) {
+                       i_error("%s", eperm_error_get_chgrp("fchown",
+                                       str_c(prefix), gid, gid_origin));
+               } else {
                        i_error("fchown(%s, %ld, %ld) failed: %m",
                                str_c(prefix),
                                uid == (uid_t)-1 ? -1L : (long)uid,
                                gid == (gid_t)-1 ? -1L : (long)gid);
-                       (void)close(fd);
-                       (void)unlink(str_c(prefix));
-                       return -1;
                }
+               (void)close(fd);
+               (void)unlink(str_c(prefix));
+               return -1;
        }
        return fd;
 }
 
+int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
+{
+       return safe_mkstemp_full(prefix, mode, uid, gid, NULL);
+}
+
+int safe_mkstemp_group(string_t *prefix, mode_t mode,
+                      gid_t gid, const char *gid_origin)
+{
+       return safe_mkstemp_full(prefix, mode, (uid_t)-1, gid, gid_origin);
+}
+
 int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid)
 {
        str_printfa(prefix, "%s.%s.", my_hostname, my_pid);
        return safe_mkstemp(prefix, mode, uid, gid);
 }
+
+int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode,
+                              gid_t gid, const char *gid_origin)
+{
+       str_printfa(prefix, "%s.%s.", my_hostname, my_pid);
+       return safe_mkstemp_group(prefix, mode, gid, gid_origin);
+}
index 43585627f0df1069a1ee795db3fd870684b09de1..f04d5cfcbef803f43b8692453ebe21a443348ef9 100644 (file)
@@ -5,7 +5,11 @@
    created filename. uid and gid can be (uid_t)-1 and (gid_t)-1 to use the
    defaults. */
 int safe_mkstemp(string_t *prefix, mode_t mode, uid_t uid, gid_t gid);
+int safe_mkstemp_group(string_t *prefix, mode_t mode,
+                      gid_t gid, const char *gid_origin);
 /* Append host and PID to the prefix. */
 int safe_mkstemp_hostpid(string_t *prefix, mode_t mode, uid_t uid, gid_t gid);
+int safe_mkstemp_hostpid_group(string_t *prefix, mode_t mode,
+                              gid_t gid, const char *gid_origin);
 
 #endif
index 6ea3e86e7ca456d573ba192f8c97a8b34979d483..a7fa254f5349a76167ee1cb91f31524f83394f1f 100644 (file)
@@ -171,7 +171,7 @@ acl_backend_vfile_acllist_try_rebuild(struct acl_backend_vfile *backend)
        struct mail_namespace *ns;
        struct mailbox_list_iterate_context *iter;
        const struct mailbox_info *info;
-       const char *rootdir, *acllist_path;
+       const char *rootdir, *acllist_path, *origin;
        struct ostream *output;
        struct stat st;
        string_t *path;
@@ -198,8 +198,8 @@ acl_backend_vfile_acllist_try_rebuild(struct acl_backend_vfile *backend)
        /* Build it into a temporary file and rename() over. There's no need
           to use locking, because even if multiple processes are rebuilding
           the file at the same time the result should be the same. */
-       mailbox_list_get_permissions(list, NULL, &mode, &gid);
-       fd = safe_mkstemp(path, mode, (uid_t)-1, gid);
+       mailbox_list_get_permissions(list, NULL, &mode, &gid, &origin);
+       fd = safe_mkstemp_group(path, mode, gid, origin);
        if (fd == -1) {
                if (errno == EACCES) {
                        /* Ignore silently if we can't create it */
index 696ea26f60203cf03511cbc571d89b2889a88216..b15634b72ab95848fa13619fe5198947a434f801 100644 (file)
@@ -846,18 +846,18 @@ static int acl_backend_vfile_update_begin(struct acl_object_vfile *aclobj,
                                          struct dotlock **dotlock_r)
 {
        struct acl_object *_aclobj = &aclobj->aclobj;
+       const char *gid_origin;
        mode_t mode;
        gid_t gid;
        int fd;
 
        /* first lock the ACL file */
        mailbox_list_get_permissions(_aclobj->backend->list, _aclobj->name,
-                                    &mode, &gid);
-       fd = file_dotlock_open_mode(&dotlock_set, aclobj->local_path, 0,
-                                   mode, (uid_t)-1, gid, dotlock_r);
+                                    &mode, &gid, &gid_origin);
+       fd = file_dotlock_open_group(&dotlock_set, aclobj->local_path, 0,
+                                    mode, gid, gid_origin, dotlock_r);
        if (fd == -1) {
-               i_error("file_dotlock_open_mode(%s) failed: %m",
-                       aclobj->local_path);
+               i_error("file_dotlock_open(%s) failed: %m", aclobj->local_path);
                return -1;
        }
 
index dbd1cb8a6e077b44c36e201e723c3fa7874105fb..45d4e5150749d8b2617157babcd4553b0bfa15e9 100644 (file)
@@ -266,18 +266,18 @@ static int
 mailbox_move(struct mailbox_list *src_list, const char *src_name,
             struct mailbox_list *dest_list, const char **_dest_name)
 {
-       const char *dir, *dest_name = *_dest_name;
+       const char *dir, *origin, *dest_name = *_dest_name;
        enum mail_error error;
        mode_t mode;
        gid_t gid;
 
        /* make sure the destination root directory exists */
-       mailbox_list_get_dir_permissions(dest_list, NULL, &mode, &gid);
+       mailbox_list_get_dir_permissions(dest_list, NULL, &mode, &gid, &origin);
        dir = mailbox_list_get_path(dest_list, NULL, MAILBOX_LIST_PATH_TYPE_DIR);
-       if (mkdir_parents_chown(dir, mode, (uid_t)-1, gid) < 0 &&
+       if (mkdir_parents_chgrp(dir, mode, gid, origin) < 0 &&
            errno != EEXIST) {
                mailbox_list_set_critical(src_list,
-                       "mkdir_parents_chown(%s) failed: %m", dir);
+                       "mkdir_parents(%s) failed: %m", dir);
                return -1;
        }
 
index 6487550abfde9e8da1e419d3efdc30382fb89b7f..b76254785876079c097e7112f92fd06b0b8a5760 100644 (file)
@@ -222,7 +222,7 @@ static int maildirsize_write(struct maildir_quota_root *root, const char *path)
        struct mail_namespace *const *namespaces;
        unsigned int i, count;
        struct dotlock *dotlock;
-       const char *p, *dir;
+       const char *p, *dir, *gid_origin, *dir_gid_origin;
        string_t *str;
        mode_t mode, dir_mode;
        gid_t gid, dir_gid;
@@ -232,43 +232,41 @@ static int maildirsize_write(struct maildir_quota_root *root, const char *path)
 
        /* figure out what permissions we should use for maildirsize.
           use the inbox namespace's permissions if possible. */
-       mode = 0600; dir_mode = 0700;
+       mode = 0600; dir_mode = 0700; gid_origin = "default";
        gid = dir_gid = (gid_t)-1;
        namespaces = array_get(&root->root.quota->namespaces, &count);
        i_assert(count > 0);
        for (i = 0; i < count; i++) {
                if ((namespaces[i]->flags & NAMESPACE_FLAG_INBOX) != 0) {
                        mailbox_list_get_permissions(namespaces[i]->list,
-                                                    NULL, &mode, &gid);
+                                                    NULL, &mode, &gid,
+                                                    &gid_origin);
                        mailbox_list_get_dir_permissions(namespaces[i]->list,
                                                         NULL,
-                                                        &dir_mode, &dir_gid);
+                                                        &dir_mode, &dir_gid,
+                                                        &dir_gid_origin);
                        break;
                }
        }
 
        dotlock_settings.use_excl_lock = set->dotlock_use_excl;
        dotlock_settings.nfs_flush = set->mail_nfs_storage;
-       fd = file_dotlock_open_mode(&dotlock_settings, path,
-                                   DOTLOCK_CREATE_FLAG_NONBLOCK,
-                                   mode, (uid_t)-1, gid, &dotlock);
+       fd = file_dotlock_open_group(&dotlock_settings, path,
+                                    DOTLOCK_CREATE_FLAG_NONBLOCK,
+                                    mode, gid, gid_origin, &dotlock);
        if (fd == -1 && errno == ENOENT) {
                /* the control directory doesn't exist yet? create it */
                p = strrchr(path, '/');
                dir = t_strdup_until(path, p);
-               if (mkdir_parents(dir, dir_mode) < 0 && errno != EEXIST) {
+               if (mkdir_parents_chgrp(dir, dir_mode, dir_gid,
+                                       dir_gid_origin) < 0 &&
+                   errno != EEXIST) {
                        i_error("mkdir_parents(%s) failed: %m", dir);
                        return -1;
                }
-               if (dir_gid != (gid_t)-1) {
-                       if (chown(dir, (uid_t)-1, dir_gid) < 0) {
-                               i_error("chown(%s,-1,%ld) failed: %m",
-                                       dir, (long)dir_gid);
-                       }
-               }
-               fd = file_dotlock_open_mode(&dotlock_settings, path,
-                                           DOTLOCK_CREATE_FLAG_NONBLOCK,
-                                           mode, (uid_t)-1, gid, &dotlock);
+               fd = file_dotlock_open_group(&dotlock_settings, path,
+                                            DOTLOCK_CREATE_FLAG_NONBLOCK,
+                                            mode, gid, gid_origin, &dotlock);
        }
        if (fd == -1) {
                if (errno == EAGAIN) {