Commit 320cd413 authored by Linus Torvalds's avatar Linus Torvalds

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
parents a7ba4bf5 cdb67279
...@@ -23,6 +23,7 @@ struct ovl_cache_entry { ...@@ -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 { ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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,7 +1022,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -958,7 +1022,10 @@ 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;
sb->s_d_op = &ovl_dentry_operations; if (remote)
sb->s_d_op = &ovl_reval_dentry_operations;
else
sb->s_d_op = &ovl_dentry_operations;
err = -ENOMEM; err = -ENOMEM;
oe = ovl_alloc_entry(numlower); oe = ovl_alloc_entry(numlower);
......
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