Commit 8d4ef4e1 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 1249b571 85fdee1e
...@@ -210,8 +210,11 @@ path as another overlay mount and it may use a lower layer path that is ...@@ -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. 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 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. 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 Mounting an overlay using an upper layer path, where the upper layer path
was previously used by another mounted overlay in combination with a was previously used by another mounted overlay in combination with a
......
...@@ -468,7 +468,9 @@ static inline int may_write_real(struct file *file) ...@@ -468,7 +468,9 @@ static inline int may_write_real(struct file *file)
/* File refers to upper, writable layer? */ /* File refers to upper, writable layer? */
upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER); 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; return 0;
/* Lower layer: can't write to real file, sorry... */ /* 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) ...@@ -561,10 +561,8 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
c->tmpfile = true; c->tmpfile = true;
err = ovl_copy_up_locked(c); err = ovl_copy_up_locked(c);
} else { } else {
err = -EIO; err = ovl_lock_rename_workdir(c->workdir, c->destdir);
if (lock_rename(c->workdir, c->destdir) != NULL) { if (!err) {
pr_err("overlayfs: failed to lock workdir+upperdir\n");
} else {
err = ovl_copy_up_locked(c); err = ovl_copy_up_locked(c);
unlock_rename(c->workdir, c->destdir); unlock_rename(c->workdir, c->destdir);
} }
......
...@@ -216,26 +216,6 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode, ...@@ -216,26 +216,6 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
return err; 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, static struct dentry *ovl_clear_empty(struct dentry *dentry,
struct list_head *list) struct list_head *list)
{ {
......
...@@ -506,6 +506,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry, ...@@ -506,6 +506,7 @@ static struct dentry *ovl_lookup_index(struct dentry *dentry,
index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len); index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
if (IS_ERR(index)) { if (IS_ERR(index)) {
err = PTR_ERR(index);
pr_warn_ratelimited("overlayfs: failed inode index lookup (ino=%lu, key=%*s, err=%i);\n" 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", "overlayfs: mount with '-o index=off' to disable inodes index.\n",
d_inode(origin)->i_ino, name.len, name.name, d_inode(origin)->i_ino, name.len, name.name,
......
...@@ -235,6 +235,7 @@ bool ovl_inuse_trylock(struct dentry *dentry); ...@@ -235,6 +235,7 @@ bool ovl_inuse_trylock(struct dentry *dentry);
void ovl_inuse_unlock(struct dentry *dentry); void ovl_inuse_unlock(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry, bool *locked); int ovl_nlink_start(struct dentry *dentry, bool *locked);
void ovl_nlink_end(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) static inline bool ovl_is_impuredir(struct dentry *dentry)
{ {
......
...@@ -37,6 +37,9 @@ struct ovl_fs { ...@@ -37,6 +37,9 @@ struct ovl_fs {
bool noxattr; bool noxattr;
/* sb common to all layers */ /* sb common to all layers */
struct super_block *same_sb; struct super_block *same_sb;
/* Did we take the inuse lock? */
bool upperdir_locked;
bool workdir_locked;
}; };
/* private information held for every overlayfs dentry */ /* private information held for every overlayfs dentry */
......
...@@ -988,6 +988,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, ...@@ -988,6 +988,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
struct path *lowerstack, unsigned int numlower) struct path *lowerstack, unsigned int numlower)
{ {
int err; int err;
struct dentry *index = NULL;
struct inode *dir = dentry->d_inode; struct inode *dir = dentry->d_inode;
struct path path = { .mnt = mnt, .dentry = dentry }; struct path path = { .mnt = mnt, .dentry = dentry };
LIST_HEAD(list); LIST_HEAD(list);
...@@ -1007,8 +1008,6 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, ...@@ -1007,8 +1008,6 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
inode_lock_nested(dir, I_MUTEX_PARENT); inode_lock_nested(dir, I_MUTEX_PARENT);
list_for_each_entry(p, &list, l_node) { list_for_each_entry(p, &list, l_node) {
struct dentry *index;
if (p->name[0] == '.') { if (p->name[0] == '.') {
if (p->len == 1) if (p->len == 1)
continue; continue;
...@@ -1018,6 +1017,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, ...@@ -1018,6 +1017,7 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
index = lookup_one_len(p->name, dentry, p->len); index = lookup_one_len(p->name, dentry, p->len);
if (IS_ERR(index)) { if (IS_ERR(index)) {
err = PTR_ERR(index); err = PTR_ERR(index);
index = NULL;
break; break;
} }
err = ovl_verify_index(index, lowerstack, numlower); err = ovl_verify_index(index, lowerstack, numlower);
...@@ -1029,7 +1029,9 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, ...@@ -1029,7 +1029,9 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
break; break;
} }
dput(index); dput(index);
index = NULL;
} }
dput(index);
inode_unlock(dir); inode_unlock(dir);
out: out:
ovl_cache_free(&list); ovl_cache_free(&list);
......
...@@ -211,9 +211,10 @@ static void ovl_put_super(struct super_block *sb) ...@@ -211,9 +211,10 @@ static void ovl_put_super(struct super_block *sb)
dput(ufs->indexdir); dput(ufs->indexdir);
dput(ufs->workdir); dput(ufs->workdir);
ovl_inuse_unlock(ufs->workbasedir); if (ufs->workdir_locked)
ovl_inuse_unlock(ufs->workbasedir);
dput(ufs->workbasedir); dput(ufs->workbasedir);
if (ufs->upper_mnt) if (ufs->upper_mnt && ufs->upperdir_locked)
ovl_inuse_unlock(ufs->upper_mnt->mnt_root); ovl_inuse_unlock(ufs->upper_mnt->mnt_root);
mntput(ufs->upper_mnt); mntput(ufs->upper_mnt);
for (i = 0; i < ufs->numlower; i++) for (i = 0; i < ufs->numlower; i++)
...@@ -881,9 +882,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -881,9 +882,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
goto out_put_upperpath; goto out_put_upperpath;
err = -EBUSY; err = -EBUSY;
if (!ovl_inuse_trylock(upperpath.dentry)) { if (ovl_inuse_trylock(upperpath.dentry)) {
pr_err("overlayfs: upperdir is in-use by another mount\n"); 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; 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); 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) ...@@ -901,9 +906,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
} }
err = -EBUSY; err = -EBUSY;
if (!ovl_inuse_trylock(workpath.dentry)) { if (ovl_inuse_trylock(workpath.dentry)) {
pr_err("overlayfs: workdir is in-use by another mount\n"); 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; 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; ufs->workbasedir = workpath.dentry;
...@@ -1156,11 +1165,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1156,11 +1165,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
out_free_lowertmp: out_free_lowertmp:
kfree(lowertmp); kfree(lowertmp);
out_unlock_workdentry: out_unlock_workdentry:
ovl_inuse_unlock(workpath.dentry); if (ufs->workdir_locked)
ovl_inuse_unlock(workpath.dentry);
out_put_workpath: out_put_workpath:
path_put(&workpath); path_put(&workpath);
out_unlock_upperdentry: out_unlock_upperdentry:
ovl_inuse_unlock(upperpath.dentry); if (ufs->upperdir_locked)
ovl_inuse_unlock(upperpath.dentry);
out_put_upperpath: out_put_upperpath:
path_put(&upperpath); path_put(&upperpath);
out_free_config: out_free_config:
......
...@@ -430,7 +430,7 @@ void ovl_inuse_unlock(struct dentry *dentry) ...@@ -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) static void ovl_cleanup_index(struct dentry *dentry)
{ {
struct inode *dir = ovl_indexdir(dentry->d_sb)->d_inode; struct inode *dir = ovl_indexdir(dentry->d_sb)->d_inode;
...@@ -469,6 +469,9 @@ static void ovl_cleanup_index(struct dentry *dentry) ...@@ -469,6 +469,9 @@ static void ovl_cleanup_index(struct dentry *dentry)
err = PTR_ERR(index); err = PTR_ERR(index);
if (!IS_ERR(index)) if (!IS_ERR(index))
err = ovl_cleanup(dir, index); err = ovl_cleanup(dir, index);
else
index = NULL;
inode_unlock(dir); inode_unlock(dir);
if (err) if (err)
goto fail; goto fail;
...@@ -557,3 +560,22 @@ void ovl_nlink_end(struct dentry *dentry, bool locked) ...@@ -557,3 +560,22 @@ void ovl_nlink_end(struct dentry *dentry, bool locked)
mutex_unlock(&OVL_I(d_inode(dentry))->lock); 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment