Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs update from Miklos Szeredi: "The meat of this is a change to use the mounter's credentials for operations that require elevated privileges (such as whiteout creation). This fixes behavior under user namespaces as well as being a nice cleanup" * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: Do d_type check only if work dir creation was successful ovl: update documentation ovl: override creds with the ones from the superblock mounter
This commit is contained in:
commit
0121a32201
|
@ -194,15 +194,6 @@ If a file with multiple hard links is copied up, then this will
|
|||
"break" the link. Changes will not be propagated to other names
|
||||
referring to the same inode.
|
||||
|
||||
Symlinks in /proc/PID/ and /proc/PID/fd which point to a non-directory
|
||||
object in overlayfs will not contain valid absolute paths, only
|
||||
relative paths leading up to the filesystem's root. This will be
|
||||
fixed in the future.
|
||||
|
||||
Some operations are not atomic, for example a crash during copy_up or
|
||||
rename will leave the filesystem in an inconsistent state. This will
|
||||
be addressed in the future.
|
||||
|
||||
Changes to underlying filesystems
|
||||
---------------------------------
|
||||
|
||||
|
|
|
@ -336,7 +336,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
|||
struct dentry *upperdir;
|
||||
struct dentry *upperdentry;
|
||||
const struct cred *old_cred;
|
||||
struct cred *override_cred;
|
||||
char *link = NULL;
|
||||
|
||||
if (WARN_ON(!workdir))
|
||||
|
@ -357,28 +356,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
|||
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) {
|
||||
|
@ -401,9 +379,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
|||
out_unlock:
|
||||
unlock_rename(workdir, upperdir);
|
||||
revert_creds(old_cred);
|
||||
put_cred(override_cred);
|
||||
|
||||
out_free_link:
|
||||
if (link)
|
||||
free_page((unsigned long) link);
|
||||
|
||||
|
|
|
@ -405,28 +405,13 @@ static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
|
|||
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)
|
||||
|
@ -662,32 +647,11 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
|||
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);
|
||||
|
@ -725,7 +689,6 @@ static int ovl_rename2(struct inode *olddir, struct dentry *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))
|
||||
|
@ -794,26 +757,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *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);
|
||||
|
@ -943,10 +888,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *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:
|
||||
|
|
|
@ -153,6 +153,7 @@ void ovl_drop_write(struct dentry *dentry);
|
|||
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);
|
||||
|
|
|
@ -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;
|
||||
|
@ -206,17 +207,8 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
|
|||
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);
|
||||
|
||||
inode_lock(dir->d_inode);
|
||||
err = 0;
|
||||
|
@ -234,7 +226,6 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
|
|||
inode_unlock(dir->d_inode);
|
||||
}
|
||||
revert_creds(old_cred);
|
||||
put_cred(override_cred);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -290,6 +281,7 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
|
|||
struct path realpath;
|
||||
struct ovl_readdir_data rdd = {
|
||||
.ctx.actor = ovl_fill_merge,
|
||||
.dentry = dentry,
|
||||
.list = list,
|
||||
.root = RB_ROOT,
|
||||
.is_lowest = false,
|
||||
|
|
|
@ -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;
|
||||
|
@ -265,6 +267,13 @@ bool ovl_is_whiteout(struct dentry *dentry)
|
|||
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;
|
||||
|
@ -603,6 +612,7 @@ static void ovl_put_super(struct super_block *sb)
|
|||
kfree(ufs->config.lowerdir);
|
||||
kfree(ufs->config.upperdir);
|
||||
kfree(ufs->config.workdir);
|
||||
put_cred(ufs->creator_cred);
|
||||
kfree(ufs);
|
||||
}
|
||||
|
||||
|
@ -1064,16 +1074,19 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||
/*
|
||||
* Upper should support d_type, else whiteouts are visible.
|
||||
* Given workdir and upper are on same fs, we can do
|
||||
* iterate_dir() on workdir.
|
||||
* iterate_dir() on workdir. This check requires successful
|
||||
* creation of workdir in previous step.
|
||||
*/
|
||||
err = ovl_check_d_type_supported(&workpath);
|
||||
if (err < 0)
|
||||
goto out_put_workdir;
|
||||
if (ufs->workdir) {
|
||||
err = ovl_check_d_type_supported(&workpath);
|
||||
if (err < 0)
|
||||
goto out_put_workdir;
|
||||
|
||||
if (!err) {
|
||||
pr_err("overlayfs: upper fs needs to support d_type.\n");
|
||||
err = -EINVAL;
|
||||
goto out_put_workdir;
|
||||
if (!err) {
|
||||
pr_err("overlayfs: upper fs needs to support d_type.\n");
|
||||
err = -EINVAL;
|
||||
goto out_put_workdir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1108,10 +1121,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||
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)
|
||||
|
@ -1144,6 +1161,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||
|
||||
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]);
|
||||
|
|
Loading…
Reference in New Issue