summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2016-07-29 10:05:23 (GMT)
committerMiklos Szeredi <mszeredi@redhat.com>2016-07-29 10:05:23 (GMT)
commitbb0d2b8ad29630b580ac903f989e704e23462357 (patch)
tree906ff6b0ba636472c66510c5d1776c40bc6e7755
parent9c630ebefeeee4363ffd29f2f9b18eddafc6479c (diff)
downloadlinux-bb0d2b8ad29630b580ac903f989e704e23462357.tar.xz
ovl: fix sgid on directory
When creating directory in workdir, the group/sgid inheritance from the parent dir was omitted completely. Fix this by calling inode_init_owner() on overlay inode and using the resulting uid/gid/mode to create the file. Unfortunately the sgid bit can be stripped off due to umask, so need to reset the mode in this case in workdir before moving the directory in place. Reported-by: Eryu Guan <eguan@redhat.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/overlayfs/dir.c31
1 files changed, 27 insertions, 4 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 7195306..8beeed3 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -357,6 +357,21 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (err)
goto out_dput2;
+ /*
+ * mode could have been mutilated due to umask (e.g. sgid directory)
+ */
+ if (!S_ISLNK(stat->mode) && newdentry->d_inode->i_mode != stat->mode) {
+ struct iattr attr = {
+ .ia_valid = ATTR_MODE,
+ .ia_mode = stat->mode,
+ };
+ inode_lock(newdentry->d_inode);
+ err = notify_change(newdentry, &attr, NULL);
+ inode_unlock(newdentry->d_inode);
+ if (err)
+ goto out_cleanup;
+ }
+
if (S_ISDIR(stat->mode)) {
err = ovl_set_opaque(newdentry);
if (err)
@@ -397,7 +412,6 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
const struct cred *old_cred;
struct cred *override_cred;
struct kstat stat = {
- .mode = mode,
.rdev = rdev,
};
@@ -410,12 +424,15 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
if (err)
goto out_iput;
+ inode_init_owner(inode, dentry->d_parent->d_inode, mode);
+ stat.mode = inode->i_mode;
+
old_cred = ovl_override_creds(dentry->d_sb);
err = -ENOMEM;
override_cred = prepare_creds();
if (override_cred) {
- override_cred->fsuid = old_cred->fsuid;
- override_cred->fsgid = old_cred->fsgid;
+ override_cred->fsuid = inode->i_uid;
+ override_cred->fsgid = inode->i_gid;
put_cred(override_creds(override_cred));
put_cred(override_cred);
@@ -427,8 +444,14 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
link, hardlink);
}
revert_creds(old_cred);
- if (!err)
+ if (!err) {
+ struct inode *realinode = d_inode(ovl_dentry_upper(dentry));
+
+ WARN_ON(inode->i_mode != realinode->i_mode);
+ WARN_ON(!uid_eq(inode->i_uid, realinode->i_uid));
+ WARN_ON(!gid_eq(inode->i_gid, realinode->i_gid));
inode = NULL;
+ }
out_iput:
iput(inode);
out: