Merge branch 'overlayfs-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs updates from Miklos Szeredi: "This relaxes the requirements on the lower layer filesystem: now ones that implement .d_revalidate, such as NFS, can be used. Upper layer filesystems still has the "no .d_revalidate" requirement. Also a bad interaction with jffs2 locking has been fixed" * 'overlayfs-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: lookup whiteouts outside iterate_dir() ovl: allow distributed fs as lower layer ovl: don't traverse automount points
This commit is contained in:
commit
320cd413fa
@ -23,6 +23,7 @@ struct ovl_cache_entry {
|
|||||||
u64 ino;
|
u64 ino;
|
||||||
struct list_head l_node;
|
struct list_head l_node;
|
||||||
struct rb_node node;
|
struct rb_node node;
|
||||||
|
struct ovl_cache_entry *next_maybe_whiteout;
|
||||||
bool is_whiteout;
|
bool is_whiteout;
|
||||||
char name[];
|
char name[];
|
||||||
};
|
};
|
||||||
@ -39,7 +40,7 @@ struct ovl_readdir_data {
|
|||||||
struct rb_root root;
|
struct rb_root root;
|
||||||
struct list_head *list;
|
struct list_head *list;
|
||||||
struct list_head middle;
|
struct list_head middle;
|
||||||
struct dentry *dir;
|
struct ovl_cache_entry *first_maybe_whiteout;
|
||||||
int count;
|
int count;
|
||||||
int err;
|
int err;
|
||||||
};
|
};
|
||||||
@ -79,7 +80,7 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
|
static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
|
||||||
const char *name, int len,
|
const char *name, int len,
|
||||||
u64 ino, unsigned int d_type)
|
u64 ino, unsigned int d_type)
|
||||||
{
|
{
|
||||||
@ -98,29 +99,8 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
|
|||||||
p->is_whiteout = false;
|
p->is_whiteout = false;
|
||||||
|
|
||||||
if (d_type == DT_CHR) {
|
if (d_type == DT_CHR) {
|
||||||
struct dentry *dentry;
|
p->next_maybe_whiteout = rdd->first_maybe_whiteout;
|
||||||
const struct cred *old_cred;
|
rdd->first_maybe_whiteout = p;
|
||||||
struct cred *override_cred;
|
|
||||||
|
|
||||||
override_cred = prepare_creds();
|
|
||||||
if (!override_cred) {
|
|
||||||
kfree(p);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CAP_DAC_OVERRIDE for lookup
|
|
||||||
*/
|
|
||||||
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
|
||||||
old_cred = override_creds(override_cred);
|
|
||||||
|
|
||||||
dentry = lookup_one_len(name, dir, len);
|
|
||||||
if (!IS_ERR(dentry)) {
|
|
||||||
p->is_whiteout = ovl_is_whiteout(dentry);
|
|
||||||
dput(dentry);
|
|
||||||
}
|
|
||||||
revert_creds(old_cred);
|
|
||||||
put_cred(override_cred);
|
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@ -148,7 +128,7 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
|
p = ovl_cache_entry_new(rdd, name, len, ino, d_type);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -169,7 +149,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
|
|||||||
if (p) {
|
if (p) {
|
||||||
list_move_tail(&p->l_node, &rdd->middle);
|
list_move_tail(&p->l_node, &rdd->middle);
|
||||||
} else {
|
} else {
|
||||||
p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
|
p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type);
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
rdd->err = -ENOMEM;
|
rdd->err = -ENOMEM;
|
||||||
else
|
else
|
||||||
@ -219,6 +199,43 @@ static int ovl_fill_merge(struct dir_context *ctx, const char *name,
|
|||||||
return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
|
return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct ovl_cache_entry *p;
|
||||||
|
struct dentry *dentry;
|
||||||
|
const struct cred *old_cred;
|
||||||
|
struct cred *override_cred;
|
||||||
|
|
||||||
|
override_cred = prepare_creds();
|
||||||
|
if (!override_cred)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CAP_DAC_OVERRIDE for lookup
|
||||||
|
*/
|
||||||
|
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
|
||||||
|
old_cred = override_creds(override_cred);
|
||||||
|
|
||||||
|
err = mutex_lock_killable(&dir->d_inode->i_mutex);
|
||||||
|
if (!err) {
|
||||||
|
while (rdd->first_maybe_whiteout) {
|
||||||
|
p = rdd->first_maybe_whiteout;
|
||||||
|
rdd->first_maybe_whiteout = p->next_maybe_whiteout;
|
||||||
|
dentry = lookup_one_len(p->name, dir, p->len);
|
||||||
|
if (!IS_ERR(dentry)) {
|
||||||
|
p->is_whiteout = ovl_is_whiteout(dentry);
|
||||||
|
dput(dentry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&dir->d_inode->i_mutex);
|
||||||
|
}
|
||||||
|
revert_creds(old_cred);
|
||||||
|
put_cred(override_cred);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int ovl_dir_read(struct path *realpath,
|
static inline int ovl_dir_read(struct path *realpath,
|
||||||
struct ovl_readdir_data *rdd)
|
struct ovl_readdir_data *rdd)
|
||||||
{
|
{
|
||||||
@ -229,7 +246,7 @@ static inline int ovl_dir_read(struct path *realpath,
|
|||||||
if (IS_ERR(realfile))
|
if (IS_ERR(realfile))
|
||||||
return PTR_ERR(realfile);
|
return PTR_ERR(realfile);
|
||||||
|
|
||||||
rdd->dir = realpath->dentry;
|
rdd->first_maybe_whiteout = NULL;
|
||||||
rdd->ctx.pos = 0;
|
rdd->ctx.pos = 0;
|
||||||
do {
|
do {
|
||||||
rdd->count = 0;
|
rdd->count = 0;
|
||||||
@ -238,6 +255,10 @@ static inline int ovl_dir_read(struct path *realpath,
|
|||||||
if (err >= 0)
|
if (err >= 0)
|
||||||
err = rdd->err;
|
err = rdd->err;
|
||||||
} while (!err && rdd->count);
|
} while (!err && rdd->count);
|
||||||
|
|
||||||
|
if (!err && rdd->first_maybe_whiteout)
|
||||||
|
err = ovl_check_whiteouts(realpath->dentry, rdd);
|
||||||
|
|
||||||
fput(realfile);
|
fput(realfile);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -273,10 +273,57 @@ static void ovl_dentry_release(struct dentry *dentry)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct ovl_entry *oe = dentry->d_fsdata;
|
||||||
|
unsigned int i;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < oe->numlower; i++) {
|
||||||
|
struct dentry *d = oe->lowerstack[i].dentry;
|
||||||
|
|
||||||
|
if (d->d_flags & DCACHE_OP_REVALIDATE) {
|
||||||
|
ret = d->d_op->d_revalidate(d, flags);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
if (!ret) {
|
||||||
|
if (!(flags & LOOKUP_RCU))
|
||||||
|
d_invalidate(d);
|
||||||
|
return -ESTALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct ovl_entry *oe = dentry->d_fsdata;
|
||||||
|
unsigned int i;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < oe->numlower; i++) {
|
||||||
|
struct dentry *d = oe->lowerstack[i].dentry;
|
||||||
|
|
||||||
|
if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE) {
|
||||||
|
ret = d->d_op->d_weak_revalidate(d, flags);
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct dentry_operations ovl_dentry_operations = {
|
static const struct dentry_operations ovl_dentry_operations = {
|
||||||
.d_release = ovl_dentry_release,
|
.d_release = ovl_dentry_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct dentry_operations ovl_reval_dentry_operations = {
|
||||||
|
.d_release = ovl_dentry_release,
|
||||||
|
.d_revalidate = ovl_dentry_revalidate,
|
||||||
|
.d_weak_revalidate = ovl_dentry_weak_revalidate,
|
||||||
|
};
|
||||||
|
|
||||||
static struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
|
static struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
|
||||||
{
|
{
|
||||||
size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);
|
size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);
|
||||||
@ -288,6 +335,20 @@ static struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
|
|||||||
return oe;
|
return oe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ovl_dentry_remote(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
return dentry->d_flags &
|
||||||
|
(DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ovl_dentry_weird(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT |
|
||||||
|
DCACHE_MANAGE_TRANSIT |
|
||||||
|
DCACHE_OP_HASH |
|
||||||
|
DCACHE_OP_COMPARE);
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct dentry *ovl_lookup_real(struct dentry *dir,
|
static inline struct dentry *ovl_lookup_real(struct dentry *dir,
|
||||||
struct qstr *name)
|
struct qstr *name)
|
||||||
{
|
{
|
||||||
@ -303,6 +364,10 @@ static inline struct dentry *ovl_lookup_real(struct dentry *dir,
|
|||||||
} else if (!dentry->d_inode) {
|
} else if (!dentry->d_inode) {
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
dentry = NULL;
|
dentry = NULL;
|
||||||
|
} else if (ovl_dentry_weird(dentry)) {
|
||||||
|
dput(dentry);
|
||||||
|
/* Don't support traversing automounts and other weirdness */
|
||||||
|
dentry = ERR_PTR(-EREMOTE);
|
||||||
}
|
}
|
||||||
return dentry;
|
return dentry;
|
||||||
}
|
}
|
||||||
@ -350,6 +415,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (this) {
|
if (this) {
|
||||||
|
if (unlikely(ovl_dentry_remote(this))) {
|
||||||
|
dput(this);
|
||||||
|
err = -EREMOTE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
if (ovl_is_whiteout(this)) {
|
if (ovl_is_whiteout(this)) {
|
||||||
dput(this);
|
dput(this);
|
||||||
this = NULL;
|
this = NULL;
|
||||||
@ -694,25 +764,6 @@ static void ovl_unescape(char *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ovl_is_allowed_fs_type(struct dentry *root)
|
|
||||||
{
|
|
||||||
const struct dentry_operations *dop = root->d_op;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We don't support:
|
|
||||||
* - automount filesystems
|
|
||||||
* - filesystems with revalidate (FIXME for lower layer)
|
|
||||||
* - filesystems with case insensitive names
|
|
||||||
*/
|
|
||||||
if (dop &&
|
|
||||||
(dop->d_manage || dop->d_automount ||
|
|
||||||
dop->d_revalidate || dop->d_weak_revalidate ||
|
|
||||||
dop->d_compare || dop->d_hash)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ovl_mount_dir_noesc(const char *name, struct path *path)
|
static int ovl_mount_dir_noesc(const char *name, struct path *path)
|
||||||
{
|
{
|
||||||
int err = -EINVAL;
|
int err = -EINVAL;
|
||||||
@ -727,7 +778,7 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
if (!ovl_is_allowed_fs_type(path->dentry)) {
|
if (ovl_dentry_weird(path->dentry)) {
|
||||||
pr_err("overlayfs: filesystem on '%s' not supported\n", name);
|
pr_err("overlayfs: filesystem on '%s' not supported\n", name);
|
||||||
goto out_put;
|
goto out_put;
|
||||||
}
|
}
|
||||||
@ -751,13 +802,21 @@ static int ovl_mount_dir(const char *name, struct path *path)
|
|||||||
if (tmp) {
|
if (tmp) {
|
||||||
ovl_unescape(tmp);
|
ovl_unescape(tmp);
|
||||||
err = ovl_mount_dir_noesc(tmp, path);
|
err = ovl_mount_dir_noesc(tmp, path);
|
||||||
|
|
||||||
|
if (!err)
|
||||||
|
if (ovl_dentry_remote(path->dentry)) {
|
||||||
|
pr_err("overlayfs: filesystem on '%s' not supported as upperdir\n",
|
||||||
|
tmp);
|
||||||
|
path_put(path);
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
kfree(tmp);
|
kfree(tmp);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
|
static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
|
||||||
int *stack_depth)
|
int *stack_depth, bool *remote)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct kstatfs statfs;
|
struct kstatfs statfs;
|
||||||
@ -774,6 +833,9 @@ static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
|
|||||||
*namelen = max(*namelen, statfs.f_namelen);
|
*namelen = max(*namelen, statfs.f_namelen);
|
||||||
*stack_depth = max(*stack_depth, path->mnt->mnt_sb->s_stack_depth);
|
*stack_depth = max(*stack_depth, path->mnt->mnt_sb->s_stack_depth);
|
||||||
|
|
||||||
|
if (ovl_dentry_remote(path->dentry))
|
||||||
|
*remote = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_put:
|
out_put:
|
||||||
@ -827,6 +889,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
unsigned int numlower;
|
unsigned int numlower;
|
||||||
unsigned int stacklen = 0;
|
unsigned int stacklen = 0;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
bool remote = false;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
@ -900,7 +963,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
lower = lowertmp;
|
lower = lowertmp;
|
||||||
for (numlower = 0; numlower < stacklen; numlower++) {
|
for (numlower = 0; numlower < stacklen; numlower++) {
|
||||||
err = ovl_lower_dir(lower, &stack[numlower],
|
err = ovl_lower_dir(lower, &stack[numlower],
|
||||||
&ufs->lower_namelen, &sb->s_stack_depth);
|
&ufs->lower_namelen, &sb->s_stack_depth,
|
||||||
|
&remote);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_put_lowerpath;
|
goto out_put_lowerpath;
|
||||||
|
|
||||||
@ -958,6 +1022,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
if (!ufs->upper_mnt)
|
if (!ufs->upper_mnt)
|
||||||
sb->s_flags |= MS_RDONLY;
|
sb->s_flags |= MS_RDONLY;
|
||||||
|
|
||||||
|
if (remote)
|
||||||
|
sb->s_d_op = &ovl_reval_dentry_operations;
|
||||||
|
else
|
||||||
sb->s_d_op = &ovl_dentry_operations;
|
sb->s_d_op = &ovl_dentry_operations;
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
Loading…
Reference in New Issue
Block a user