summaryrefslogtreecommitdiffstats
path: root/target/linux/generic/patches-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/patches-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch')
-rw-r--r--target/linux/generic/patches-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch336
1 files changed, 336 insertions, 0 deletions
diff --git a/target/linux/generic/patches-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch b/target/linux/generic/patches-4.4/051-0002-ovl-override-creds-with-the-ones-from-the-superblock.patch
new file mode 100644
index 0000000000..8a7d00aeb0
--- /dev/null
+++ b/target/linux/generic/patches-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
+@@ -303,7 +303,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))
+@@ -322,28 +321,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) {
+@@ -366,9 +344,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
+@@ -405,28 +405,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)
+@@ -656,32 +641,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);
+@@ -720,7 +684,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))
+@@ -789,26 +752,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);
+@@ -939,10 +884,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);
+ }
+
+@@ -1068,10 +1078,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)
+@@ -1104,6 +1118,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]);