Commit 5d6b501f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ovl-fixes-5.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs fixes from Miklos Szeredi:
 "Here's one fix for a class of bugs triggered by syzcaller, and one
  that makes xfstests fail less"

* tag 'ovl-fixes-5.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: doc: add non-standard corner cases
  ovl: detect overlapping layers
  ovl: support the FS_IOC_FS[SG]ETXATTR ioctls
parents 21175857 5d3211b6
...@@ -336,8 +336,20 @@ the copied layers will fail the verification of the lower root file handle. ...@@ -336,8 +336,20 @@ the copied layers will fail the verification of the lower root file handle.
Non-standard behavior Non-standard behavior
--------------------- ---------------------
Overlayfs can now act as a POSIX compliant filesystem with the following Current version of overlayfs can act as a mostly POSIX compliant
features turned on: filesystem.
This is the list of cases that overlayfs doesn't currently handle:
a) POSIX mandates updating st_atime for reads. This is currently not
done in the case when the file resides on a lower layer.
b) If a file residing on a lower layer is opened for read-only and then
memory mapped with MAP_SHARED, then subsequent changes to the file are not
reflected in the memory mapping.
The following options allow overlayfs to act more like a standards
compliant filesystem:
1) "redirect_dir" 1) "redirect_dir"
......
...@@ -426,7 +426,8 @@ static unsigned int ovl_get_inode_flags(struct inode *inode) ...@@ -426,7 +426,8 @@ static unsigned int ovl_get_inode_flags(struct inode *inode)
return ovl_iflags; return ovl_iflags;
} }
static long ovl_ioctl_set_flags(struct file *file, unsigned long arg) static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd,
unsigned long arg)
{ {
long ret; long ret;
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
...@@ -456,7 +457,7 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned long arg) ...@@ -456,7 +457,7 @@ static long ovl_ioctl_set_flags(struct file *file, unsigned long arg)
if (ret) if (ret)
goto unlock; goto unlock;
ret = ovl_real_ioctl(file, FS_IOC_SETFLAGS, arg); ret = ovl_real_ioctl(file, cmd, arg);
ovl_copyflags(ovl_inode_real(inode), inode); ovl_copyflags(ovl_inode_real(inode), inode);
unlock: unlock:
...@@ -474,11 +475,13 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -474,11 +475,13 @@ static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
switch (cmd) { switch (cmd) {
case FS_IOC_GETFLAGS: case FS_IOC_GETFLAGS:
case FS_IOC_FSGETXATTR:
ret = ovl_real_ioctl(file, cmd, arg); ret = ovl_real_ioctl(file, cmd, arg);
break; break;
case FS_IOC_SETFLAGS: case FS_IOC_SETFLAGS:
ret = ovl_ioctl_set_flags(file, arg); case FS_IOC_FSSETXATTR:
ret = ovl_ioctl_set_flags(file, cmd, arg);
break; break;
default: default:
......
...@@ -777,6 +777,54 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, ...@@ -777,6 +777,54 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
return inode; return inode;
} }
bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir)
{
struct inode *key = d_inode(dir);
struct inode *trap;
bool res;
trap = ilookup5(sb, (unsigned long) key, ovl_inode_test, key);
if (!trap)
return false;
res = IS_DEADDIR(trap) && !ovl_inode_upper(trap) &&
!ovl_inode_lower(trap);
iput(trap);
return res;
}
/*
* Create an inode cache entry for layer root dir, that will intentionally
* fail ovl_verify_inode(), so any lookup that will find some layer root
* will fail.
*/
struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir)
{
struct inode *key = d_inode(dir);
struct inode *trap;
if (!d_is_dir(dir))
return ERR_PTR(-ENOTDIR);
trap = iget5_locked(sb, (unsigned long) key, ovl_inode_test,
ovl_inode_set, key);
if (!trap)
return ERR_PTR(-ENOMEM);
if (!(trap->i_state & I_NEW)) {
/* Conflicting layer roots? */
iput(trap);
return ERR_PTR(-ELOOP);
}
trap->i_mode = S_IFDIR;
trap->i_flags = S_DEAD;
unlock_new_inode(trap);
return trap;
}
/* /*
* Does overlay inode need to be hashed by lower inode? * Does overlay inode need to be hashed by lower inode?
*/ */
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "overlayfs.h" #include "overlayfs.h"
struct ovl_lookup_data { struct ovl_lookup_data {
struct super_block *sb;
struct qstr name; struct qstr name;
bool is_dir; bool is_dir;
bool opaque; bool opaque;
...@@ -244,6 +245,12 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -244,6 +245,12 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
if (!d->metacopy || d->last) if (!d->metacopy || d->last)
goto out; goto out;
} else { } else {
if (ovl_lookup_trap_inode(d->sb, this)) {
/* Caught in a trap of overlapping layers */
err = -ELOOP;
goto out_err;
}
if (last_element) if (last_element)
d->is_dir = true; d->is_dir = true;
if (d->last) if (d->last)
...@@ -819,6 +826,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -819,6 +826,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
int err; int err;
bool metacopy = false; bool metacopy = false;
struct ovl_lookup_data d = { struct ovl_lookup_data d = {
.sb = dentry->d_sb,
.name = dentry->d_name, .name = dentry->d_name,
.is_dir = false, .is_dir = false,
.opaque = false, .opaque = false,
......
...@@ -270,6 +270,7 @@ void ovl_clear_flag(unsigned long flag, struct inode *inode); ...@@ -270,6 +270,7 @@ void ovl_clear_flag(unsigned long flag, struct inode *inode);
bool ovl_test_flag(unsigned long flag, struct inode *inode); bool ovl_test_flag(unsigned long flag, struct inode *inode);
bool ovl_inuse_trylock(struct dentry *dentry); bool ovl_inuse_trylock(struct dentry *dentry);
void ovl_inuse_unlock(struct dentry *dentry); void ovl_inuse_unlock(struct dentry *dentry);
bool ovl_is_inuse(struct dentry *dentry);
bool ovl_need_index(struct dentry *dentry); bool ovl_need_index(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry); int ovl_nlink_start(struct dentry *dentry);
void ovl_nlink_end(struct dentry *dentry); void ovl_nlink_end(struct dentry *dentry);
...@@ -376,6 +377,8 @@ struct ovl_inode_params { ...@@ -376,6 +377,8 @@ struct ovl_inode_params {
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev); struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev);
struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real, struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
bool is_upper); bool is_upper);
bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir);
struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir);
struct inode *ovl_get_inode(struct super_block *sb, struct inode *ovl_get_inode(struct super_block *sb,
struct ovl_inode_params *oip); struct ovl_inode_params *oip);
static inline void ovl_copyattr(struct inode *from, struct inode *to) static inline void ovl_copyattr(struct inode *from, struct inode *to)
......
...@@ -29,6 +29,8 @@ struct ovl_sb { ...@@ -29,6 +29,8 @@ struct ovl_sb {
struct ovl_layer { struct ovl_layer {
struct vfsmount *mnt; struct vfsmount *mnt;
/* Trap in ovl inode cache */
struct inode *trap;
struct ovl_sb *fs; struct ovl_sb *fs;
/* Index of this layer in fs root (upper idx == 0) */ /* Index of this layer in fs root (upper idx == 0) */
int idx; int idx;
...@@ -65,6 +67,10 @@ struct ovl_fs { ...@@ -65,6 +67,10 @@ struct ovl_fs {
/* Did we take the inuse lock? */ /* Did we take the inuse lock? */
bool upperdir_locked; bool upperdir_locked;
bool workdir_locked; bool workdir_locked;
/* Traps in ovl inode cache */
struct inode *upperdir_trap;
struct inode *workdir_trap;
struct inode *indexdir_trap;
/* Inode numbers in all layers do not use the high xino_bits */ /* Inode numbers in all layers do not use the high xino_bits */
unsigned int xino_bits; unsigned int xino_bits;
}; };
......
...@@ -215,6 +215,9 @@ static void ovl_free_fs(struct ovl_fs *ofs) ...@@ -215,6 +215,9 @@ static void ovl_free_fs(struct ovl_fs *ofs)
{ {
unsigned i; unsigned i;
iput(ofs->indexdir_trap);
iput(ofs->workdir_trap);
iput(ofs->upperdir_trap);
dput(ofs->indexdir); dput(ofs->indexdir);
dput(ofs->workdir); dput(ofs->workdir);
if (ofs->workdir_locked) if (ofs->workdir_locked)
...@@ -223,8 +226,10 @@ static void ovl_free_fs(struct ovl_fs *ofs) ...@@ -223,8 +226,10 @@ static void ovl_free_fs(struct ovl_fs *ofs)
if (ofs->upperdir_locked) if (ofs->upperdir_locked)
ovl_inuse_unlock(ofs->upper_mnt->mnt_root); ovl_inuse_unlock(ofs->upper_mnt->mnt_root);
mntput(ofs->upper_mnt); mntput(ofs->upper_mnt);
for (i = 0; i < ofs->numlower; i++) for (i = 0; i < ofs->numlower; i++) {
iput(ofs->lower_layers[i].trap);
mntput(ofs->lower_layers[i].mnt); mntput(ofs->lower_layers[i].mnt);
}
for (i = 0; i < ofs->numlowerfs; i++) for (i = 0; i < ofs->numlowerfs; i++)
free_anon_bdev(ofs->lower_fs[i].pseudo_dev); free_anon_bdev(ofs->lower_fs[i].pseudo_dev);
kfree(ofs->lower_layers); kfree(ofs->lower_layers);
...@@ -983,7 +988,26 @@ static const struct xattr_handler *ovl_xattr_handlers[] = { ...@@ -983,7 +988,26 @@ static const struct xattr_handler *ovl_xattr_handlers[] = {
NULL NULL
}; };
static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath) static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
struct inode **ptrap, const char *name)
{
struct inode *trap;
int err;
trap = ovl_get_trap_inode(sb, dir);
err = PTR_ERR(trap);
if (IS_ERR(trap)) {
if (err == -ELOOP)
pr_err("overlayfs: conflicting %s path\n", name);
return err;
}
*ptrap = trap;
return 0;
}
static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
struct path *upperpath)
{ {
struct vfsmount *upper_mnt; struct vfsmount *upper_mnt;
int err; int err;
...@@ -1003,6 +1027,11 @@ static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath) ...@@ -1003,6 +1027,11 @@ static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath)
if (err) if (err)
goto out; goto out;
err = ovl_setup_trap(sb, upperpath->dentry, &ofs->upperdir_trap,
"upperdir");
if (err)
goto out;
upper_mnt = clone_private_mount(upperpath); upper_mnt = clone_private_mount(upperpath);
err = PTR_ERR(upper_mnt); err = PTR_ERR(upper_mnt);
if (IS_ERR(upper_mnt)) { if (IS_ERR(upper_mnt)) {
...@@ -1029,7 +1058,8 @@ static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath) ...@@ -1029,7 +1058,8 @@ static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath)
return err; return err;
} }
static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
struct path *workpath)
{ {
struct vfsmount *mnt = ofs->upper_mnt; struct vfsmount *mnt = ofs->upper_mnt;
struct dentry *temp; struct dentry *temp;
...@@ -1044,6 +1074,10 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ...@@ -1044,6 +1074,10 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
if (!ofs->workdir) if (!ofs->workdir)
goto out; goto out;
err = ovl_setup_trap(sb, ofs->workdir, &ofs->workdir_trap, "workdir");
if (err)
goto out;
/* /*
* Upper should support d_type, else whiteouts are visible. Given * Upper should support d_type, else whiteouts are visible. Given
* workdir and upper are on same fs, we can do iterate_dir() on * workdir and upper are on same fs, we can do iterate_dir() on
...@@ -1104,7 +1138,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath) ...@@ -1104,7 +1138,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
return err; return err;
} }
static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath) static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs,
struct path *upperpath)
{ {
int err; int err;
struct path workpath = { }; struct path workpath = { };
...@@ -1135,19 +1170,16 @@ static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath) ...@@ -1135,19 +1170,16 @@ static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath)
pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n"); pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
} }
err = ovl_make_workdir(ofs, &workpath); err = ovl_make_workdir(sb, ofs, &workpath);
if (err)
goto out;
err = 0;
out: out:
path_put(&workpath); path_put(&workpath);
return err; return err;
} }
static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
struct path *upperpath) struct ovl_entry *oe, struct path *upperpath)
{ {
struct vfsmount *mnt = ofs->upper_mnt; struct vfsmount *mnt = ofs->upper_mnt;
int err; int err;
...@@ -1166,6 +1198,11 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe, ...@@ -1166,6 +1198,11 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true); ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
if (ofs->indexdir) { if (ofs->indexdir) {
err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap,
"indexdir");
if (err)
goto out;
/* /*
* Verify upper root is exclusively associated with index dir. * Verify upper root is exclusively associated with index dir.
* Older kernels stored upper fh in "trusted.overlay.origin" * Older kernels stored upper fh in "trusted.overlay.origin"
...@@ -1253,8 +1290,8 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) ...@@ -1253,8 +1290,8 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
return ofs->numlowerfs; return ofs->numlowerfs;
} }
static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, static int ovl_get_lower_layers(struct super_block *sb, struct ovl_fs *ofs,
unsigned int numlower) struct path *stack, unsigned int numlower)
{ {
int err; int err;
unsigned int i; unsigned int i;
...@@ -1272,16 +1309,28 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, ...@@ -1272,16 +1309,28 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
for (i = 0; i < numlower; i++) { for (i = 0; i < numlower; i++) {
struct vfsmount *mnt; struct vfsmount *mnt;
struct inode *trap;
int fsid; int fsid;
err = fsid = ovl_get_fsid(ofs, &stack[i]); err = fsid = ovl_get_fsid(ofs, &stack[i]);
if (err < 0) if (err < 0)
goto out; goto out;
err = -EBUSY;
if (ovl_is_inuse(stack[i].dentry)) {
pr_err("overlayfs: lowerdir is in-use as upperdir/workdir\n");
goto out;
}
err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir");
if (err)
goto out;
mnt = clone_private_mount(&stack[i]); mnt = clone_private_mount(&stack[i]);
err = PTR_ERR(mnt); err = PTR_ERR(mnt);
if (IS_ERR(mnt)) { if (IS_ERR(mnt)) {
pr_err("overlayfs: failed to clone lowerpath\n"); pr_err("overlayfs: failed to clone lowerpath\n");
iput(trap);
goto out; goto out;
} }
...@@ -1291,6 +1340,7 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack, ...@@ -1291,6 +1340,7 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
*/ */
mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME; mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
ofs->lower_layers[ofs->numlower].trap = trap;
ofs->lower_layers[ofs->numlower].mnt = mnt; ofs->lower_layers[ofs->numlower].mnt = mnt;
ofs->lower_layers[ofs->numlower].idx = i + 1; ofs->lower_layers[ofs->numlower].idx = i + 1;
ofs->lower_layers[ofs->numlower].fsid = fsid; ofs->lower_layers[ofs->numlower].fsid = fsid;
...@@ -1385,7 +1435,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, ...@@ -1385,7 +1435,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
goto out_err; goto out_err;
} }
err = ovl_get_lower_layers(ofs, stack, numlower); err = ovl_get_lower_layers(sb, ofs, stack, numlower);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1417,6 +1467,85 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, ...@@ -1417,6 +1467,85 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
goto out; goto out;
} }
/*
* Check if this layer root is a descendant of:
* - another layer of this overlayfs instance
* - upper/work dir of any overlayfs instance
* - a disconnected dentry (detached root)
*/
static int ovl_check_layer(struct super_block *sb, struct dentry *dentry,
const char *name)
{
struct dentry *next, *parent;
bool is_root = false;
int err = 0;
if (!dentry || dentry == dentry->d_sb->s_root)
return 0;
next = dget(dentry);
/* Walk back ancestors to fs root (inclusive) looking for traps */
do {
parent = dget_parent(next);
is_root = (parent == next);
if (ovl_is_inuse(parent)) {
err = -EBUSY;
pr_err("overlayfs: %s path overlapping in-use upperdir/workdir\n",
name);
} else if (ovl_lookup_trap_inode(sb, parent)) {
err = -ELOOP;
pr_err("overlayfs: overlapping %s path\n", name);
}
dput(next);
next = parent;
} while (!err && !is_root);
/* Did we really walk to fs root or found a detached root? */
if (!err && next != dentry->d_sb->s_root) {
err = -ESTALE;
pr_err("overlayfs: disconnected %s path\n", name);
}
dput(next);
return err;
}
/*
* Check if any of the layers or work dirs overlap.
*/
static int ovl_check_overlapping_layers(struct super_block *sb,
struct ovl_fs *ofs)
{
int i, err;
if (ofs->upper_mnt) {
err = ovl_check_layer(sb, ofs->upper_mnt->mnt_root, "upperdir");
if (err)
return err;
/*
* Checking workbasedir avoids hitting ovl_is_inuse(parent) of
* this instance and covers overlapping work and index dirs,
* unless work or index dir have been moved since created inside
* workbasedir. In that case, we already have their traps in
* inode cache and we will catch that case on lookup.
*/
err = ovl_check_layer(sb, ofs->workbasedir, "workdir");
if (err)
return err;
}
for (i = 0; i < ofs->numlower; i++) {
err = ovl_check_layer(sb, ofs->lower_layers[i].mnt->mnt_root,
"lowerdir");
if (err)
return err;
}
return 0;
}
static int ovl_fill_super(struct super_block *sb, void *data, int silent) static int ovl_fill_super(struct super_block *sb, void *data, int silent)
{ {
struct path upperpath = { }; struct path upperpath = { };
...@@ -1456,17 +1585,20 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1456,17 +1585,20 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
if (ofs->config.xino != OVL_XINO_OFF) if (ofs->config.xino != OVL_XINO_OFF)
ofs->xino_bits = BITS_PER_LONG - 32; ofs->xino_bits = BITS_PER_LONG - 32;
/* alloc/destroy_inode needed for setting up traps in inode cache */
sb->s_op = &ovl_super_operations;
if (ofs->config.upperdir) { if (ofs->config.upperdir) {
if (!ofs->config.workdir) { if (!ofs->config.workdir) {
pr_err("overlayfs: missing 'workdir'\n"); pr_err("overlayfs: missing 'workdir'\n");
goto out_err; goto out_err;
} }
err = ovl_get_upper(ofs, &upperpath); err = ovl_get_upper(sb, ofs, &upperpath);
if (err) if (err)
goto out_err; goto out_err;
err = ovl_get_workdir(ofs, &upperpath); err = ovl_get_workdir(sb, ofs, &upperpath);
if (err) if (err)
goto out_err; goto out_err;
...@@ -1487,7 +1619,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1487,7 +1619,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
sb->s_flags |= SB_RDONLY; sb->s_flags |= SB_RDONLY;
if (!(ovl_force_readonly(ofs)) && ofs->config.index) { if (!(ovl_force_readonly(ofs)) && ofs->config.index) {
err = ovl_get_indexdir(ofs, oe, &upperpath); err = ovl_get_indexdir(sb, ofs, oe, &upperpath);
if (err) if (err)
goto out_free_oe; goto out_free_oe;
...@@ -1500,6 +1632,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1500,6 +1632,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
} }
err = ovl_check_overlapping_layers(sb, ofs);
if (err)
goto out_free_oe;
/* Show index=off in /proc/mounts for forced r/o mount */ /* Show index=off in /proc/mounts for forced r/o mount */
if (!ofs->indexdir) { if (!ofs->indexdir) {
ofs->config.index = false; ofs->config.index = false;
...@@ -1521,7 +1657,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1521,7 +1657,6 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
sb->s_magic = OVERLAYFS_SUPER_MAGIC; sb->s_magic = OVERLAYFS_SUPER_MAGIC;
sb->s_op = &ovl_super_operations;
sb->s_xattr = ovl_xattr_handlers; sb->s_xattr = ovl_xattr_handlers;
sb->s_fs_info = ofs; sb->s_fs_info = ofs;
sb->s_flags |= SB_POSIXACL; sb->s_flags |= SB_POSIXACL;
......
...@@ -652,6 +652,18 @@ void ovl_inuse_unlock(struct dentry *dentry) ...@@ -652,6 +652,18 @@ void ovl_inuse_unlock(struct dentry *dentry)
} }
} }
bool ovl_is_inuse(struct dentry *dentry)
{
struct inode *inode = d_inode(dentry);
bool inuse;
spin_lock(&inode->i_lock);
inuse = (inode->i_state & I_OVL_INUSE);
spin_unlock(&inode->i_lock);
return inuse;
}
/* /*
* Does this overlay dentry need to be indexed on copy up? * Does this overlay dentry need to be indexed on copy up?
*/ */
......
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