mirror of https://gitee.com/openkylin/linux.git
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs fixes from Miklos Szeredi: "Fix a regression in 4.14 and one in 4.13. The latter is a case when Docker is doing something it really shouldn't and gets away with it. We now print a warning instead of erroring out. There are also fixes to several error paths" * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: fix regression caused by exclusive upper/work dir protection ovl: fix missing unlock_rename() in ovl_do_copy_up() ovl: fix dentry leak in ovl_indexdir_cleanup() ovl: fix dput() of ERR_PTR in ovl_cleanup_index() ovl: fix error value printed in ovl_lookup_index() ovl: fix may_write_real() for overlayfs directories
This commit is contained in:
commit
8d4ef4e15e
|
@ -210,8 +210,11 @@ path as another overlay mount and it may use a lower layer path that is
|
|||
beneath or above the path of another overlay lower layer path.
|
||||
|
||||
Using an upper layer path and/or a workdir path that are already used by
|
||||
another overlay mount is not allowed and will fail with EBUSY. Using
|
||||
another overlay mount is not allowed and may fail with EBUSY. Using
|
||||
partially overlapping paths is not allowed but will not fail with EBUSY.
|
||||
If files are accessed from two overlayfs mounts which share or overlap the
|
||||
upper layer and/or workdir path the behavior of the overlay is undefined,
|
||||
though it will not result in a crash or deadlock.
|
||||
|
||||
Mounting an overlay using an upper layer path, where the upper layer path
|
||||
was previously used by another mounted overlay in combination with a
|
||||
|
|
|
@ -468,7 +468,9 @@ static inline int may_write_real(struct file *file)
|
|||
|
||||
/* File refers to upper, writable layer? */
|
||||
upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER);
|
||||
if (upperdentry && file_inode(file) == d_inode(upperdentry))
|
||||
if (upperdentry &&
|
||||
(file_inode(file) == d_inode(upperdentry) ||
|
||||
file_inode(file) == d_inode(dentry)))
|
||||
return 0;
|
||||
|
||||
/* Lower layer: can't write to real file, sorry... */
|
||||
|
|
|
@ -561,10 +561,8 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
|
|||
c->tmpfile = true;
|
||||
err = ovl_copy_up_locked(c);
|
||||
} else {
|
||||
err = -EIO;
|
||||
if (lock_rename(c->workdir, c->destdir) != NULL) {
|
||||
pr_err("overlayfs: failed to lock workdir+upperdir\n");
|
||||
} else {
|
||||
err = ovl_lock_rename_workdir(c->workdir, c->destdir);
|
||||
if (!err) {
|
||||
err = ovl_copy_up_locked(c);
|
||||
unlock_rename(c->workdir, c->destdir);
|
||||
}
|
||||
|
|
|
@ -216,26 +216,6 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ovl_lock_rename_workdir(struct dentry *workdir,
|
||||
struct dentry *upperdir)
|
||||
{
|
||||
/* Workdir should not be the same as upperdir */
|
||||
if (workdir == upperdir)
|
||||
goto err;
|
||||
|
||||
/* Workdir should not be subdir of upperdir and vice versa */
|
||||
if (lock_rename(workdir, upperdir) != NULL)
|
||||
goto err_unlock;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
unlock_rename(workdir, upperdir);
|
||||
err:
|
||||
pr_err("overlayfs: failed to lock workdir+upperdir\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
||||
struct list_head *list)
|
||||
{
|
||||
|
|
|
@ -506,6 +506,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
|
|||
|
||||
index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
|
||||
if (IS_ERR(index)) {
|
||||
err = PTR_ERR(index);
|
||||
pr_warn_ratelimited("overlayfs: failed inode index lookup (ino=%lu, key=%*s, err=%i);\n"
|
||||
"overlayfs: mount with '-o index=off' to disable inodes index.\n",
|
||||
d_inode(origin)->i_ino, name.len, name.name,
|
||||
|
|
|
@ -235,6 +235,7 @@ bool ovl_inuse_trylock(struct dentry *dentry);
|
|||
void ovl_inuse_unlock(struct dentry *dentry);
|
||||
int ovl_nlink_start(struct dentry *dentry, bool *locked);
|
||||
void ovl_nlink_end(struct dentry *dentry, bool locked);
|
||||
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
|
||||
|
||||
static inline bool ovl_is_impuredir(struct dentry *dentry)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,9 @@ struct ovl_fs {
|
|||
bool noxattr;
|
||||
/* sb common to all layers */
|
||||
struct super_block *same_sb;
|
||||
/* Did we take the inuse lock? */
|
||||
bool upperdir_locked;
|
||||
bool workdir_locked;
|
||||
};
|
||||
|
||||
/* private information held for every overlayfs dentry */
|
||||
|
|
|
@ -988,6 +988,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
|
|||
struct path *lowerstack, unsigned int numlower)
|
||||
{
|
||||
int err;
|
||||
struct dentry *index = NULL;
|
||||
struct inode *dir = dentry->d_inode;
|
||||
struct path path = { .mnt = mnt, .dentry = dentry };
|
||||
LIST_HEAD(list);
|
||||
|
@ -1007,8 +1008,6 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
|
|||
|
||||
inode_lock_nested(dir, I_MUTEX_PARENT);
|
||||
list_for_each_entry(p, &list, l_node) {
|
||||
struct dentry *index;
|
||||
|
||||
if (p->name[0] == '.') {
|
||||
if (p->len == 1)
|
||||
continue;
|
||||
|
@ -1018,6 +1017,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
|
|||
index = lookup_one_len(p->name, dentry, p->len);
|
||||
if (IS_ERR(index)) {
|
||||
err = PTR_ERR(index);
|
||||
index = NULL;
|
||||
break;
|
||||
}
|
||||
err = ovl_verify_index(index, lowerstack, numlower);
|
||||
|
@ -1029,7 +1029,9 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
|
|||
break;
|
||||
}
|
||||
dput(index);
|
||||
index = NULL;
|
||||
}
|
||||
dput(index);
|
||||
inode_unlock(dir);
|
||||
out:
|
||||
ovl_cache_free(&list);
|
||||
|
|
|
@ -211,9 +211,10 @@ static void ovl_put_super(struct super_block *sb)
|
|||
|
||||
dput(ufs->indexdir);
|
||||
dput(ufs->workdir);
|
||||
ovl_inuse_unlock(ufs->workbasedir);
|
||||
if (ufs->workdir_locked)
|
||||
ovl_inuse_unlock(ufs->workbasedir);
|
||||
dput(ufs->workbasedir);
|
||||
if (ufs->upper_mnt)
|
||||
if (ufs->upper_mnt && ufs->upperdir_locked)
|
||||
ovl_inuse_unlock(ufs->upper_mnt->mnt_root);
|
||||
mntput(ufs->upper_mnt);
|
||||
for (i = 0; i < ufs->numlower; i++)
|
||||
|
@ -881,9 +882,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||
goto out_put_upperpath;
|
||||
|
||||
err = -EBUSY;
|
||||
if (!ovl_inuse_trylock(upperpath.dentry)) {
|
||||
pr_err("overlayfs: upperdir is in-use by another mount\n");
|
||||
if (ovl_inuse_trylock(upperpath.dentry)) {
|
||||
ufs->upperdir_locked = true;
|
||||
} else if (ufs->config.index) {
|
||||
pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n");
|
||||
goto out_put_upperpath;
|
||||
} else {
|
||||
pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
|
||||
}
|
||||
|
||||
err = ovl_mount_dir(ufs->config.workdir, &workpath);
|
||||
|
@ -901,9 +906,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||
}
|
||||
|
||||
err = -EBUSY;
|
||||
if (!ovl_inuse_trylock(workpath.dentry)) {
|
||||
pr_err("overlayfs: workdir is in-use by another mount\n");
|
||||
if (ovl_inuse_trylock(workpath.dentry)) {
|
||||
ufs->workdir_locked = true;
|
||||
} else if (ufs->config.index) {
|
||||
pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n");
|
||||
goto out_put_workpath;
|
||||
} else {
|
||||
pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
|
||||
}
|
||||
|
||||
ufs->workbasedir = workpath.dentry;
|
||||
|
@ -1156,11 +1165,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||
out_free_lowertmp:
|
||||
kfree(lowertmp);
|
||||
out_unlock_workdentry:
|
||||
ovl_inuse_unlock(workpath.dentry);
|
||||
if (ufs->workdir_locked)
|
||||
ovl_inuse_unlock(workpath.dentry);
|
||||
out_put_workpath:
|
||||
path_put(&workpath);
|
||||
out_unlock_upperdentry:
|
||||
ovl_inuse_unlock(upperpath.dentry);
|
||||
if (ufs->upperdir_locked)
|
||||
ovl_inuse_unlock(upperpath.dentry);
|
||||
out_put_upperpath:
|
||||
path_put(&upperpath);
|
||||
out_free_config:
|
||||
|
|
|
@ -430,7 +430,7 @@ void ovl_inuse_unlock(struct dentry *dentry)
|
|||
}
|
||||
}
|
||||
|
||||
/* Called must hold OVL_I(inode)->oi_lock */
|
||||
/* Caller must hold OVL_I(inode)->lock */
|
||||
static void ovl_cleanup_index(struct dentry *dentry)
|
||||
{
|
||||
struct inode *dir = ovl_indexdir(dentry->d_sb)->d_inode;
|
||||
|
@ -469,6 +469,9 @@ static void ovl_cleanup_index(struct dentry *dentry)
|
|||
err = PTR_ERR(index);
|
||||
if (!IS_ERR(index))
|
||||
err = ovl_cleanup(dir, index);
|
||||
else
|
||||
index = NULL;
|
||||
|
||||
inode_unlock(dir);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
@ -557,3 +560,22 @@ void ovl_nlink_end(struct dentry *dentry, bool locked)
|
|||
mutex_unlock(&OVL_I(d_inode(dentry))->lock);
|
||||
}
|
||||
}
|
||||
|
||||
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir)
|
||||
{
|
||||
/* Workdir should not be the same as upperdir */
|
||||
if (workdir == upperdir)
|
||||
goto err;
|
||||
|
||||
/* Workdir should not be subdir of upperdir and vice versa */
|
||||
if (lock_rename(workdir, upperdir) != NULL)
|
||||
goto err_unlock;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
unlock_rename(workdir, upperdir);
|
||||
err:
|
||||
pr_err("overlayfs: failed to lock workdir+upperdir\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue