Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs fixes from Miklos Szeredi: "This contains a fix for a potential crash/corruption issue and another where the suid/sgid bits weren't cleared on write" * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: verify upper dentry in ovl_remove_and_whiteout() ovl: Copy up underlying inode's ->i_mode to overlay inode ovl: handle ATTR_KILL*
This commit is contained in:
@@ -505,6 +505,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
|
|||||||
struct dentry *upper;
|
struct dentry *upper;
|
||||||
struct dentry *opaquedir = NULL;
|
struct dentry *opaquedir = NULL;
|
||||||
int err;
|
int err;
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
if (WARN_ON(!workdir))
|
if (WARN_ON(!workdir))
|
||||||
return -EROFS;
|
return -EROFS;
|
||||||
@@ -534,46 +535,39 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
|
|||||||
if (err)
|
if (err)
|
||||||
goto out_dput;
|
goto out_dput;
|
||||||
|
|
||||||
whiteout = ovl_whiteout(workdir, dentry);
|
|
||||||
err = PTR_ERR(whiteout);
|
|
||||||
if (IS_ERR(whiteout))
|
|
||||||
goto out_unlock;
|
|
||||||
|
|
||||||
upper = ovl_dentry_upper(dentry);
|
|
||||||
if (!upper) {
|
|
||||||
upper = lookup_one_len(dentry->d_name.name, upperdir,
|
upper = lookup_one_len(dentry->d_name.name, upperdir,
|
||||||
dentry->d_name.len);
|
dentry->d_name.len);
|
||||||
err = PTR_ERR(upper);
|
err = PTR_ERR(upper);
|
||||||
if (IS_ERR(upper))
|
if (IS_ERR(upper))
|
||||||
goto kill_whiteout;
|
goto out_unlock;
|
||||||
|
|
||||||
err = ovl_do_rename(wdir, whiteout, udir, upper, 0);
|
|
||||||
dput(upper);
|
|
||||||
if (err)
|
|
||||||
goto kill_whiteout;
|
|
||||||
} else {
|
|
||||||
int flags = 0;
|
|
||||||
|
|
||||||
if (opaquedir)
|
|
||||||
upper = opaquedir;
|
|
||||||
err = -ESTALE;
|
err = -ESTALE;
|
||||||
if (upper->d_parent != upperdir)
|
if ((opaquedir && upper != opaquedir) ||
|
||||||
goto kill_whiteout;
|
(!opaquedir && ovl_dentry_upper(dentry) &&
|
||||||
|
upper != ovl_dentry_upper(dentry))) {
|
||||||
|
goto out_dput_upper;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_dir)
|
whiteout = ovl_whiteout(workdir, dentry);
|
||||||
flags |= RENAME_EXCHANGE;
|
err = PTR_ERR(whiteout);
|
||||||
|
if (IS_ERR(whiteout))
|
||||||
|
goto out_dput_upper;
|
||||||
|
|
||||||
|
if (d_is_dir(upper))
|
||||||
|
flags = RENAME_EXCHANGE;
|
||||||
|
|
||||||
err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
|
err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
|
||||||
if (err)
|
if (err)
|
||||||
goto kill_whiteout;
|
goto kill_whiteout;
|
||||||
|
if (flags)
|
||||||
if (is_dir)
|
|
||||||
ovl_cleanup(wdir, upper);
|
ovl_cleanup(wdir, upper);
|
||||||
}
|
|
||||||
ovl_dentry_version_inc(dentry->d_parent);
|
ovl_dentry_version_inc(dentry->d_parent);
|
||||||
out_d_drop:
|
out_d_drop:
|
||||||
d_drop(dentry);
|
d_drop(dentry);
|
||||||
dput(whiteout);
|
dput(whiteout);
|
||||||
|
out_dput_upper:
|
||||||
|
dput(upper);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
unlock_rename(workdir, upperdir);
|
unlock_rename(workdir, upperdir);
|
||||||
out_dput:
|
out_dput:
|
||||||
|
@@ -80,6 +80,9 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
|
|||||||
goto out_drop_write;
|
goto out_drop_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
|
||||||
|
attr->ia_valid &= ~ATTR_MODE;
|
||||||
|
|
||||||
inode_lock(upperdentry->d_inode);
|
inode_lock(upperdentry->d_inode);
|
||||||
err = notify_change(upperdentry, attr, NULL);
|
err = notify_change(upperdentry, attr, NULL);
|
||||||
if (!err)
|
if (!err)
|
||||||
@@ -410,12 +413,11 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
|
|||||||
if (!inode)
|
if (!inode)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
mode &= S_IFMT;
|
|
||||||
|
|
||||||
inode->i_ino = get_next_ino();
|
inode->i_ino = get_next_ino();
|
||||||
inode->i_mode = mode;
|
inode->i_mode = mode;
|
||||||
inode->i_flags |= S_NOATIME | S_NOCMTIME;
|
inode->i_flags |= S_NOATIME | S_NOCMTIME;
|
||||||
|
|
||||||
|
mode &= S_IFMT;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case S_IFDIR:
|
case S_IFDIR:
|
||||||
inode->i_private = oe;
|
inode->i_private = oe;
|
||||||
|
@@ -187,6 +187,7 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
|
|||||||
{
|
{
|
||||||
to->i_uid = from->i_uid;
|
to->i_uid = from->i_uid;
|
||||||
to->i_gid = from->i_gid;
|
to->i_gid = from->i_gid;
|
||||||
|
to->i_mode = from->i_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dir.c */
|
/* dir.c */
|
||||||
|
Reference in New Issue
Block a user