You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
10 KiB
336 lines
10 KiB
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]);
|
|
|