diff options
Diffstat (limited to 'target/linux/generic/pending-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch')
-rw-r--r-- | target/linux/generic/pending-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/target/linux/generic/pending-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch b/target/linux/generic/pending-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch new file mode 100644 index 0000000000..208bc61ec5 --- /dev/null +++ b/target/linux/generic/pending-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch @@ -0,0 +1,336 @@ +From 3fe6e52f062643676eb4518d68cee3bc1272091b Mon Sep 17 00:00:00 2001 +From: Antonio Murdaca <amurdaca@redhat.com> +Date: Thu, 7 Apr 2016 15:48:25 +0200 +Subject: [PATCH] ovl: override creds with the ones from the superblock mounter + +In user namespace the whiteout creation fails with -EPERM because the +current process isn't capable(CAP_SYS_ADMIN) when setting xattr. + +A simple reproducer: + +$ mkdir upper lower work merged lower/dir +$ sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged +$ unshare -m -p -f -U -r bash + +Now as root in the user namespace: + +\# touch merged/dir/{1,2,3} # this will force a copy up of lower/dir +\# rm -fR merged/* + +This ends up failing with -EPERM after the files in dir has been +correctly deleted: + +unlinkat(4, "2", 0) = 0 +unlinkat(4, "1", 0) = 0 +unlinkat(4, "3", 0) = 0 +close(4) = 0 +unlinkat(AT_FDCWD, "merged/dir", AT_REMOVEDIR) = -1 EPERM (Operation not +permitted) + +Interestingly, if you don't place files in merged/dir you can remove it, +meaning if upper/dir does not exist, creating the char device file works +properly in that same location. + +This patch uses ovl_sb_creator_cred() to get the cred struct from the +superblock mounter and override the old cred with these new ones so that +the whiteout creation is possible because overlay is wrong in assuming that +the creds it will get with prepare_creds will be in the initial user +namespace. The old cap_raise game is removed in favor of just overriding +the old cred struct. + +This patch also drops from ovl_copy_up_one() the following two lines: + +override_cred->fsuid = stat->uid; +override_cred->fsgid = stat->gid; + +This is because the correct uid and gid are taken directly with the stat +struct and correctly set with ovl_set_attr(). + +Signed-off-by: Antonio Murdaca <runcom@redhat.com> +Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> +--- + fs/overlayfs/copy_up.c | 26 +------------------ + fs/overlayfs/dir.c | 67 ++++-------------------------------------------- + fs/overlayfs/overlayfs.h | 1 + + fs/overlayfs/readdir.c | 14 +++------- + fs/overlayfs/super.c | 18 ++++++++++++- + 5 files changed, 27 insertions(+), 99 deletions(-) + +--- a/fs/overlayfs/copy_up.c ++++ b/fs/overlayfs/copy_up.c +@@ -317,7 +317,6 @@ int ovl_copy_up_one(struct dentry *paren + struct dentry *upperdir; + struct dentry *upperdentry; + const struct cred *old_cred; +- struct cred *override_cred; + char *link = NULL; + + if (WARN_ON(!workdir)) +@@ -336,28 +335,7 @@ int ovl_copy_up_one(struct dentry *paren + return PTR_ERR(link); + } + +- err = -ENOMEM; +- override_cred = prepare_creds(); +- if (!override_cred) +- goto out_free_link; +- +- override_cred->fsuid = stat->uid; +- override_cred->fsgid = stat->gid; +- /* +- * CAP_SYS_ADMIN for copying up extended attributes +- * CAP_DAC_OVERRIDE for create +- * CAP_FOWNER for chmod, timestamp update +- * CAP_FSETID for chmod +- * CAP_CHOWN for chown +- * CAP_MKNOD for mknod +- */ +- cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); +- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); +- cap_raise(override_cred->cap_effective, CAP_FOWNER); +- cap_raise(override_cred->cap_effective, CAP_FSETID); +- cap_raise(override_cred->cap_effective, CAP_CHOWN); +- cap_raise(override_cred->cap_effective, CAP_MKNOD); +- old_cred = override_creds(override_cred); ++ old_cred = ovl_override_creds(dentry->d_sb); + + err = -EIO; + if (lock_rename(workdir, upperdir) != NULL) { +@@ -380,9 +358,7 @@ int ovl_copy_up_one(struct dentry *paren + out_unlock: + unlock_rename(workdir, upperdir); + revert_creds(old_cred); +- put_cred(override_cred); + +-out_free_link: + if (link) + free_page((unsigned long) link); + +--- a/fs/overlayfs/dir.c ++++ b/fs/overlayfs/dir.c +@@ -408,28 +408,13 @@ static int ovl_create_or_link(struct den + err = ovl_create_upper(dentry, inode, &stat, link, hardlink); + } else { + const struct cred *old_cred; +- struct cred *override_cred; + +- err = -ENOMEM; +- override_cred = prepare_creds(); +- if (!override_cred) +- goto out_iput; +- +- /* +- * CAP_SYS_ADMIN for setting opaque xattr +- * CAP_DAC_OVERRIDE for create in workdir, rename +- * CAP_FOWNER for removing whiteout from sticky dir +- */ +- cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); +- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); +- cap_raise(override_cred->cap_effective, CAP_FOWNER); +- old_cred = override_creds(override_cred); ++ old_cred = ovl_override_creds(dentry->d_sb); + + err = ovl_create_over_whiteout(dentry, inode, &stat, link, + hardlink); + + revert_creds(old_cred); +- put_cred(override_cred); + } + + if (!err) +@@ -659,32 +644,11 @@ static int ovl_do_remove(struct dentry * + if (OVL_TYPE_PURE_UPPER(type)) { + err = ovl_remove_upper(dentry, is_dir); + } else { +- const struct cred *old_cred; +- struct cred *override_cred; +- +- err = -ENOMEM; +- override_cred = prepare_creds(); +- if (!override_cred) +- goto out_drop_write; +- +- /* +- * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir +- * CAP_DAC_OVERRIDE for create in workdir, rename +- * CAP_FOWNER for removing whiteout from sticky dir +- * CAP_FSETID for chmod of opaque dir +- * CAP_CHOWN for chown of opaque dir +- */ +- cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); +- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); +- cap_raise(override_cred->cap_effective, CAP_FOWNER); +- cap_raise(override_cred->cap_effective, CAP_FSETID); +- cap_raise(override_cred->cap_effective, CAP_CHOWN); +- old_cred = override_creds(override_cred); ++ const struct cred *old_cred = ovl_override_creds(dentry->d_sb); + + err = ovl_remove_and_whiteout(dentry, is_dir); + + revert_creds(old_cred); +- put_cred(override_cred); + } + out_drop_write: + ovl_drop_write(dentry); +@@ -723,7 +687,6 @@ static int ovl_rename2(struct inode *old + bool new_is_dir = false; + struct dentry *opaquedir = NULL; + const struct cred *old_cred = NULL; +- struct cred *override_cred = NULL; + + err = -EINVAL; + if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) +@@ -792,26 +755,8 @@ static int ovl_rename2(struct inode *old + old_opaque = !OVL_TYPE_PURE_UPPER(old_type); + new_opaque = !OVL_TYPE_PURE_UPPER(new_type); + +- if (old_opaque || new_opaque) { +- err = -ENOMEM; +- override_cred = prepare_creds(); +- if (!override_cred) +- goto out_drop_write; +- +- /* +- * CAP_SYS_ADMIN for setting xattr on whiteout, opaque dir +- * CAP_DAC_OVERRIDE for create in workdir +- * CAP_FOWNER for removing whiteout from sticky dir +- * CAP_FSETID for chmod of opaque dir +- * CAP_CHOWN for chown of opaque dir +- */ +- cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); +- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); +- cap_raise(override_cred->cap_effective, CAP_FOWNER); +- cap_raise(override_cred->cap_effective, CAP_FSETID); +- cap_raise(override_cred->cap_effective, CAP_CHOWN); +- old_cred = override_creds(override_cred); +- } ++ if (old_opaque || new_opaque) ++ old_cred = ovl_override_creds(old->d_sb); + + if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) { + opaquedir = ovl_check_empty_and_clear(new); +@@ -942,10 +887,8 @@ out_dput_old: + out_unlock: + unlock_rename(new_upperdir, old_upperdir); + out_revert_creds: +- if (old_opaque || new_opaque) { ++ if (old_opaque || new_opaque) + revert_creds(old_cred); +- put_cred(override_cred); +- } + out_drop_write: + ovl_drop_write(old); + out: +--- a/fs/overlayfs/overlayfs.h ++++ b/fs/overlayfs/overlayfs.h +@@ -150,6 +150,7 @@ void ovl_drop_write(struct dentry *dentr + bool ovl_dentry_is_opaque(struct dentry *dentry); + void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); + bool ovl_is_whiteout(struct dentry *dentry); ++const struct cred *ovl_override_creds(struct super_block *sb); + void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry); + struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags); +--- a/fs/overlayfs/readdir.c ++++ b/fs/overlayfs/readdir.c +@@ -36,6 +36,7 @@ struct ovl_dir_cache { + + struct ovl_readdir_data { + struct dir_context ctx; ++ struct dentry *dentry; + bool is_lowest; + struct rb_root root; + struct list_head *list; +@@ -205,17 +206,8 @@ static int ovl_check_whiteouts(struct de + struct ovl_cache_entry *p; + struct dentry *dentry; + const struct cred *old_cred; +- struct cred *override_cred; +- +- override_cred = prepare_creds(); +- if (!override_cred) +- return -ENOMEM; + +- /* +- * CAP_DAC_OVERRIDE for lookup +- */ +- cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); +- old_cred = override_creds(override_cred); ++ old_cred = ovl_override_creds(rdd->dentry->d_sb); + + err = mutex_lock_killable(&dir->d_inode->i_mutex); + if (!err) { +@@ -231,7 +223,6 @@ static int ovl_check_whiteouts(struct de + mutex_unlock(&dir->d_inode->i_mutex); + } + revert_creds(old_cred); +- put_cred(override_cred); + + return err; + } +@@ -287,6 +278,7 @@ static int ovl_dir_read_merged(struct de + struct path realpath; + struct ovl_readdir_data rdd = { + .ctx.actor = ovl_fill_merge, ++ .dentry = dentry, + .list = list, + .root = RB_ROOT, + .is_lowest = false, +--- a/fs/overlayfs/super.c ++++ b/fs/overlayfs/super.c +@@ -42,6 +42,8 @@ struct ovl_fs { + long lower_namelen; + /* pathnames of lower and upper dirs, for show_options */ + struct ovl_config config; ++ /* creds of process who forced instantiation of super block */ ++ const struct cred *creator_cred; + }; + + struct ovl_dir_cache; +@@ -246,6 +248,13 @@ bool ovl_is_whiteout(struct dentry *dent + return inode && IS_WHITEOUT(inode); + } + ++const struct cred *ovl_override_creds(struct super_block *sb) ++{ ++ struct ovl_fs *ofs = sb->s_fs_info; ++ ++ return override_creds(ofs->creator_cred); ++} ++ + static bool ovl_is_opaquedir(struct dentry *dentry) + { + int res; +@@ -587,6 +596,7 @@ static void ovl_put_super(struct super_b + kfree(ufs->config.lowerdir); + kfree(ufs->config.upperdir); + kfree(ufs->config.workdir); ++ put_cred(ufs->creator_cred); + kfree(ufs); + } + +@@ -1087,10 +1097,14 @@ static int ovl_fill_super(struct super_b + else + sb->s_d_op = &ovl_dentry_operations; + ++ ufs->creator_cred = prepare_creds(); ++ if (!ufs->creator_cred) ++ goto out_put_lower_mnt; ++ + err = -ENOMEM; + oe = ovl_alloc_entry(numlower); + if (!oe) +- goto out_put_lower_mnt; ++ goto out_put_cred; + + root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe)); + if (!root_dentry) +@@ -1123,6 +1137,8 @@ static int ovl_fill_super(struct super_b + + out_free_oe: + kfree(oe); ++out_put_cred: ++ put_cred(ufs->creator_cred); + out_put_lower_mnt: + for (i = 0; i < ufs->numlower; i++) + mntput(ufs->lower_mnt[i]); |