Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs fixes from Miklos Szeredi: "This fixes a crash with SELinux and several other old and new bugs" * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: check for bad and whiteout index on lookup ovl: do not cleanup directory and whiteout index entries ovl: fix xattr get and set with selinux ovl: remove unneeded check for IS_ERR() ovl: fix origin verification of index dir ovl: mark parent impure on ovl_link() ovl: fix random return value on mount
This commit is contained in:
commit
99313414dd
@ -481,17 +481,30 @@ out_cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
|
static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
|
||||||
struct cattr *attr, struct dentry *hardlink)
|
struct cattr *attr, struct dentry *hardlink,
|
||||||
|
bool origin)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
const struct cred *old_cred;
|
const struct cred *old_cred;
|
||||||
struct cred *override_cred;
|
struct cred *override_cred;
|
||||||
|
struct dentry *parent = dentry->d_parent;
|
||||||
|
|
||||||
err = ovl_copy_up(dentry->d_parent);
|
err = ovl_copy_up(parent);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
old_cred = ovl_override_creds(dentry->d_sb);
|
old_cred = ovl_override_creds(dentry->d_sb);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When linking a file with copy up origin into a new parent, mark the
|
||||||
|
* new parent dir "impure".
|
||||||
|
*/
|
||||||
|
if (origin) {
|
||||||
|
err = ovl_set_impure(parent, ovl_dentry_upper(parent));
|
||||||
|
if (err)
|
||||||
|
goto out_revert_creds;
|
||||||
|
}
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
override_cred = prepare_creds();
|
override_cred = prepare_creds();
|
||||||
if (override_cred) {
|
if (override_cred) {
|
||||||
@ -550,7 +563,7 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
|
|||||||
inode_init_owner(inode, dentry->d_parent->d_inode, mode);
|
inode_init_owner(inode, dentry->d_parent->d_inode, mode);
|
||||||
attr.mode = inode->i_mode;
|
attr.mode = inode->i_mode;
|
||||||
|
|
||||||
err = ovl_create_or_link(dentry, inode, &attr, NULL);
|
err = ovl_create_or_link(dentry, inode, &attr, NULL, false);
|
||||||
if (err)
|
if (err)
|
||||||
iput(inode);
|
iput(inode);
|
||||||
|
|
||||||
@ -609,7 +622,8 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
|
|||||||
inode = d_inode(old);
|
inode = d_inode(old);
|
||||||
ihold(inode);
|
ihold(inode);
|
||||||
|
|
||||||
err = ovl_create_or_link(new, inode, NULL, ovl_dentry_upper(old));
|
err = ovl_create_or_link(new, inode, NULL, ovl_dentry_upper(old),
|
||||||
|
ovl_type_origin(old));
|
||||||
if (err)
|
if (err)
|
||||||
iput(inode);
|
iput(inode);
|
||||||
|
|
||||||
|
@ -202,37 +202,38 @@ bool ovl_is_private_xattr(const char *name)
|
|||||||
sizeof(OVL_XATTR_PREFIX) - 1) == 0;
|
sizeof(OVL_XATTR_PREFIX) - 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value,
|
int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
|
||||||
size_t size, int flags)
|
const void *value, size_t size, int flags)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct path realpath;
|
struct dentry *upperdentry = ovl_i_dentry_upper(inode);
|
||||||
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
|
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
|
||||||
const struct cred *old_cred;
|
const struct cred *old_cred;
|
||||||
|
|
||||||
err = ovl_want_write(dentry);
|
err = ovl_want_write(dentry);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!value && !OVL_TYPE_UPPER(type)) {
|
if (!value && !upperdentry) {
|
||||||
err = vfs_getxattr(realpath.dentry, name, NULL, 0);
|
err = vfs_getxattr(realdentry, name, NULL, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out_drop_write;
|
goto out_drop_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ovl_copy_up(dentry);
|
if (!upperdentry) {
|
||||||
if (err)
|
err = ovl_copy_up(dentry);
|
||||||
goto out_drop_write;
|
if (err)
|
||||||
|
goto out_drop_write;
|
||||||
|
|
||||||
if (!OVL_TYPE_UPPER(type))
|
realdentry = ovl_dentry_upper(dentry);
|
||||||
ovl_path_upper(dentry, &realpath);
|
}
|
||||||
|
|
||||||
old_cred = ovl_override_creds(dentry->d_sb);
|
old_cred = ovl_override_creds(dentry->d_sb);
|
||||||
if (value)
|
if (value)
|
||||||
err = vfs_setxattr(realpath.dentry, name, value, size, flags);
|
err = vfs_setxattr(realdentry, name, value, size, flags);
|
||||||
else {
|
else {
|
||||||
WARN_ON(flags != XATTR_REPLACE);
|
WARN_ON(flags != XATTR_REPLACE);
|
||||||
err = vfs_removexattr(realpath.dentry, name);
|
err = vfs_removexattr(realdentry, name);
|
||||||
}
|
}
|
||||||
revert_creds(old_cred);
|
revert_creds(old_cred);
|
||||||
|
|
||||||
@ -242,12 +243,13 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ovl_xattr_get(struct dentry *dentry, const char *name,
|
int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
|
||||||
void *value, size_t size)
|
void *value, size_t size)
|
||||||
{
|
{
|
||||||
struct dentry *realdentry = ovl_dentry_real(dentry);
|
|
||||||
ssize_t res;
|
ssize_t res;
|
||||||
const struct cred *old_cred;
|
const struct cred *old_cred;
|
||||||
|
struct dentry *realdentry =
|
||||||
|
ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry);
|
||||||
|
|
||||||
old_cred = ovl_override_creds(dentry->d_sb);
|
old_cred = ovl_override_creds(dentry->d_sb);
|
||||||
res = vfs_getxattr(realdentry, name, value, size);
|
res = vfs_getxattr(realdentry, name, value, size);
|
||||||
|
@ -397,8 +397,19 @@ int ovl_verify_index(struct dentry *index, struct path *lowerstack,
|
|||||||
if (!d_inode(index))
|
if (!d_inode(index))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err = -EISDIR;
|
/*
|
||||||
if (d_is_dir(index))
|
* Directory index entries are going to be used for looking up
|
||||||
|
* redirected upper dirs by lower dir fh when decoding an overlay
|
||||||
|
* file handle of a merge dir. Whiteout index entries are going to be
|
||||||
|
* used as an indication that an exported overlay file handle should
|
||||||
|
* be treated as stale (i.e. after unlink of the overlay inode).
|
||||||
|
* We don't know the verification rules for directory and whiteout
|
||||||
|
* index entries, because they have not been implemented yet, so return
|
||||||
|
* EROFS if those entries are found to avoid corrupting an index that
|
||||||
|
* was created by a newer kernel.
|
||||||
|
*/
|
||||||
|
err = -EROFS;
|
||||||
|
if (d_is_dir(index) || ovl_is_whiteout(index))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
@ -436,8 +447,8 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
pr_warn_ratelimited("overlayfs: failed to verify index (%pd2, err=%i)\n",
|
pr_warn_ratelimited("overlayfs: failed to verify index (%pd2, ftype=%x, err=%i)\n",
|
||||||
index, err);
|
index, d_inode(index)->i_mode & S_IFMT, err);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,6 +513,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inode = d_inode(index);
|
||||||
if (d_is_negative(index)) {
|
if (d_is_negative(index)) {
|
||||||
if (upper && d_inode(origin)->i_nlink > 1) {
|
if (upper && d_inode(origin)->i_nlink > 1) {
|
||||||
pr_warn_ratelimited("overlayfs: hard link with origin but no index (ino=%lu).\n",
|
pr_warn_ratelimited("overlayfs: hard link with origin but no index (ino=%lu).\n",
|
||||||
@ -511,11 +523,22 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
|
|||||||
|
|
||||||
dput(index);
|
dput(index);
|
||||||
index = NULL;
|
index = NULL;
|
||||||
} else if (upper && d_inode(index) != d_inode(upper)) {
|
} else if (upper && d_inode(upper) != inode) {
|
||||||
inode = d_inode(index);
|
pr_warn_ratelimited("overlayfs: wrong index found (index=%pd2, ino=%lu, upper ino=%lu).\n",
|
||||||
pr_warn_ratelimited("overlayfs: wrong index found (index ino: %lu, upper ino: %lu).\n",
|
index, inode->i_ino, d_inode(upper)->i_ino);
|
||||||
d_inode(index)->i_ino,
|
goto fail;
|
||||||
d_inode(upper)->i_ino);
|
} else if (ovl_dentry_weird(index) || ovl_is_whiteout(index) ||
|
||||||
|
((inode->i_mode ^ d_inode(origin)->i_mode) & S_IFMT)) {
|
||||||
|
/*
|
||||||
|
* Index should always be of the same file type as origin
|
||||||
|
* except for the case of a whiteout index. A whiteout
|
||||||
|
* index should only exist if all lower aliases have been
|
||||||
|
* unlinked, which means that finding a lower origin on lookup
|
||||||
|
* whose index is a whiteout should be treated as an error.
|
||||||
|
*/
|
||||||
|
pr_warn_ratelimited("overlayfs: bad index found (index=%pd2, ftype=%x, origin ftype=%x).\n",
|
||||||
|
index, d_inode(index)->i_mode & S_IFMT,
|
||||||
|
d_inode(origin)->i_mode & S_IFMT);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,8 @@ enum ovl_flag {
|
|||||||
/* Is the real inode encoded in fid an upper inode? */
|
/* Is the real inode encoded in fid an upper inode? */
|
||||||
#define OVL_FH_FLAG_PATH_UPPER (1 << 2)
|
#define OVL_FH_FLAG_PATH_UPPER (1 << 2)
|
||||||
|
|
||||||
#define OVL_FH_FLAG_ALL (OVL_FH_FLAG_BIG_ENDIAN | OVL_FH_FLAG_ANY_ENDIAN)
|
#define OVL_FH_FLAG_ALL (OVL_FH_FLAG_BIG_ENDIAN | OVL_FH_FLAG_ANY_ENDIAN | \
|
||||||
|
OVL_FH_FLAG_PATH_UPPER)
|
||||||
|
|
||||||
#if defined(__LITTLE_ENDIAN)
|
#if defined(__LITTLE_ENDIAN)
|
||||||
#define OVL_FH_FLAG_CPU_ENDIAN 0
|
#define OVL_FH_FLAG_CPU_ENDIAN 0
|
||||||
@ -199,6 +200,7 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
|
|||||||
struct dentry *ovl_dentry_upper(struct dentry *dentry);
|
struct dentry *ovl_dentry_upper(struct dentry *dentry);
|
||||||
struct dentry *ovl_dentry_lower(struct dentry *dentry);
|
struct dentry *ovl_dentry_lower(struct dentry *dentry);
|
||||||
struct dentry *ovl_dentry_real(struct dentry *dentry);
|
struct dentry *ovl_dentry_real(struct dentry *dentry);
|
||||||
|
struct dentry *ovl_i_dentry_upper(struct inode *inode);
|
||||||
struct inode *ovl_inode_upper(struct inode *inode);
|
struct inode *ovl_inode_upper(struct inode *inode);
|
||||||
struct inode *ovl_inode_lower(struct inode *inode);
|
struct inode *ovl_inode_lower(struct inode *inode);
|
||||||
struct inode *ovl_inode_real(struct inode *inode);
|
struct inode *ovl_inode_real(struct inode *inode);
|
||||||
@ -270,9 +272,9 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr);
|
|||||||
int ovl_getattr(const struct path *path, struct kstat *stat,
|
int ovl_getattr(const struct path *path, struct kstat *stat,
|
||||||
u32 request_mask, unsigned int flags);
|
u32 request_mask, unsigned int flags);
|
||||||
int ovl_permission(struct inode *inode, int mask);
|
int ovl_permission(struct inode *inode, int mask);
|
||||||
int ovl_xattr_set(struct dentry *dentry, const char *name, const void *value,
|
int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
|
||||||
size_t size, int flags);
|
const void *value, size_t size, int flags);
|
||||||
int ovl_xattr_get(struct dentry *dentry, const char *name,
|
int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
|
||||||
void *value, size_t size);
|
void *value, size_t size);
|
||||||
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
|
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
|
||||||
struct posix_acl *ovl_get_acl(struct inode *inode, int type);
|
struct posix_acl *ovl_get_acl(struct inode *inode, int type);
|
||||||
|
@ -703,7 +703,10 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
|
|||||||
err = PTR_ERR(index);
|
err = PTR_ERR(index);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ovl_verify_index(index, lowerstack, numlower)) {
|
err = ovl_verify_index(index, lowerstack, numlower);
|
||||||
|
if (err) {
|
||||||
|
if (err == -EROFS)
|
||||||
|
break;
|
||||||
err = ovl_cleanup(dir, index);
|
err = ovl_cleanup(dir, index);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
@ -692,7 +692,7 @@ ovl_posix_acl_xattr_get(const struct xattr_handler *handler,
|
|||||||
struct dentry *dentry, struct inode *inode,
|
struct dentry *dentry, struct inode *inode,
|
||||||
const char *name, void *buffer, size_t size)
|
const char *name, void *buffer, size_t size)
|
||||||
{
|
{
|
||||||
return ovl_xattr_get(dentry, handler->name, buffer, size);
|
return ovl_xattr_get(dentry, inode, handler->name, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __maybe_unused
|
static int __maybe_unused
|
||||||
@ -742,7 +742,7 @@ ovl_posix_acl_xattr_set(const struct xattr_handler *handler,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ovl_xattr_set(dentry, handler->name, value, size, flags);
|
err = ovl_xattr_set(dentry, inode, handler->name, value, size, flags);
|
||||||
if (!err)
|
if (!err)
|
||||||
ovl_copyattr(ovl_inode_real(inode), inode);
|
ovl_copyattr(ovl_inode_real(inode), inode);
|
||||||
|
|
||||||
@ -772,7 +772,7 @@ static int ovl_other_xattr_get(const struct xattr_handler *handler,
|
|||||||
struct dentry *dentry, struct inode *inode,
|
struct dentry *dentry, struct inode *inode,
|
||||||
const char *name, void *buffer, size_t size)
|
const char *name, void *buffer, size_t size)
|
||||||
{
|
{
|
||||||
return ovl_xattr_get(dentry, name, buffer, size);
|
return ovl_xattr_get(dentry, inode, name, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ovl_other_xattr_set(const struct xattr_handler *handler,
|
static int ovl_other_xattr_set(const struct xattr_handler *handler,
|
||||||
@ -780,7 +780,7 @@ static int ovl_other_xattr_set(const struct xattr_handler *handler,
|
|||||||
const char *name, const void *value,
|
const char *name, const void *value,
|
||||||
size_t size, int flags)
|
size_t size, int flags)
|
||||||
{
|
{
|
||||||
return ovl_xattr_set(dentry, name, value, size, flags);
|
return ovl_xattr_set(dentry, inode, name, value, size, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct xattr_handler __maybe_unused
|
static const struct xattr_handler __maybe_unused
|
||||||
@ -1058,10 +1058,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
|
|
||||||
ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry,
|
ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry,
|
||||||
OVL_INDEXDIR_NAME, true);
|
OVL_INDEXDIR_NAME, true);
|
||||||
err = PTR_ERR(ufs->indexdir);
|
|
||||||
if (IS_ERR(ufs->indexdir))
|
|
||||||
goto out_put_lower_mnt;
|
|
||||||
|
|
||||||
if (ufs->indexdir) {
|
if (ufs->indexdir) {
|
||||||
/* Verify upper root is index dir origin */
|
/* Verify upper root is index dir origin */
|
||||||
err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt,
|
err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt,
|
||||||
@ -1090,6 +1086,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
else
|
else
|
||||||
sb->s_d_op = &ovl_dentry_operations;
|
sb->s_d_op = &ovl_dentry_operations;
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
ufs->creator_cred = cred = prepare_creds();
|
ufs->creator_cred = cred = prepare_creds();
|
||||||
if (!cred)
|
if (!cred)
|
||||||
goto out_put_indexdir;
|
goto out_put_indexdir;
|
||||||
|
@ -157,9 +157,14 @@ struct dentry *ovl_dentry_real(struct dentry *dentry)
|
|||||||
return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry);
|
return ovl_dentry_upper(dentry) ?: ovl_dentry_lower(dentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct dentry *ovl_i_dentry_upper(struct inode *inode)
|
||||||
|
{
|
||||||
|
return ovl_upperdentry_dereference(OVL_I(inode));
|
||||||
|
}
|
||||||
|
|
||||||
struct inode *ovl_inode_upper(struct inode *inode)
|
struct inode *ovl_inode_upper(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dentry *upperdentry = ovl_upperdentry_dereference(OVL_I(inode));
|
struct dentry *upperdentry = ovl_i_dentry_upper(inode);
|
||||||
|
|
||||||
return upperdentry ? d_inode(upperdentry) : NULL;
|
return upperdentry ? d_inode(upperdentry) : NULL;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user