228 lines
6.3 KiB
Diff
228 lines
6.3 KiB
Diff
From: Miklos Szeredi <mszeredi@suse.cz>
|
|
Date: Mon, 22 Jun 2015 13:53:48 +0200
|
|
Subject: ovl: allow distributed fs as lower layer
|
|
Origin: https://git.kernel.org/linus/7c03b5d45b8eebf0111125053d8fe887cc262ba6
|
|
Bug-Debian: https://bugs.debian.org/786925
|
|
|
|
Allow filesystems with .d_revalidate as lower layer(s), but not as upper
|
|
layer.
|
|
|
|
For local filesystems the rule was that modifications on the layers
|
|
directly while being part of the overlay results in undefined behavior.
|
|
|
|
This can easily be extended to distributed filesystems: we assume the tree
|
|
used as lower layer is static, which means ->d_revalidate() should always
|
|
return "1". If that is not the case, return -ESTALE, don't try to work
|
|
around the modification.
|
|
|
|
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
|
|
---
|
|
fs/overlayfs/super.c | 113 +++++++++++++++++++++++++++++++++++++++------------
|
|
1 file changed, 88 insertions(+), 25 deletions(-)
|
|
|
|
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
|
|
index de9d2ee..8a08c58 100644
|
|
--- a/fs/overlayfs/super.c
|
|
+++ b/fs/overlayfs/super.c
|
|
@@ -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 = {
|
|
.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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
+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,
|
|
struct qstr *name)
|
|
{
|
|
@@ -303,9 +364,9 @@ static inline struct dentry *ovl_lookup_real(struct dentry *dir,
|
|
} else if (!dentry->d_inode) {
|
|
dput(dentry);
|
|
dentry = NULL;
|
|
- } else if (dentry->d_flags & DCACHE_MANAGED_DENTRY) {
|
|
+ } else if (ovl_dentry_weird(dentry)) {
|
|
dput(dentry);
|
|
- /* Don't support traversing automounts */
|
|
+ /* Don't support traversing automounts and other weirdness */
|
|
dentry = ERR_PTR(-EREMOTE);
|
|
}
|
|
return dentry;
|
|
@@ -354,6 +415,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
|
goto out;
|
|
|
|
if (this) {
|
|
+ if (unlikely(ovl_dentry_remote(this))) {
|
|
+ dput(this);
|
|
+ err = -EREMOTE;
|
|
+ goto out;
|
|
+ }
|
|
if (ovl_is_whiteout(this)) {
|
|
dput(this);
|
|
this = NULL;
|
|
@@ -698,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:
|
|
- * - autofs
|
|
- * - filesystems with revalidate (FIXME for lower layer)
|
|
- * - filesystems with case insensitive names
|
|
- */
|
|
- if (dop &&
|
|
- (dop->d_manage ||
|
|
- 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)
|
|
{
|
|
int err = -EINVAL;
|
|
@@ -731,7 +778,7 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path)
|
|
goto out;
|
|
}
|
|
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);
|
|
goto out_put;
|
|
}
|
|
@@ -755,13 +802,21 @@ static int ovl_mount_dir(const char *name, struct path *path)
|
|
if (tmp) {
|
|
ovl_unescape(tmp);
|
|
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);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
|
|
- int *stack_depth)
|
|
+ int *stack_depth, bool *remote)
|
|
{
|
|
int err;
|
|
struct kstatfs statfs;
|
|
@@ -778,6 +833,9 @@ static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
|
|
*namelen = max(*namelen, statfs.f_namelen);
|
|
*stack_depth = max(*stack_depth, path->mnt->mnt_sb->s_stack_depth);
|
|
|
|
+ if (ovl_dentry_remote(path->dentry))
|
|
+ *remote = true;
|
|
+
|
|
return 0;
|
|
|
|
out_put:
|
|
@@ -831,6 +889,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|
unsigned int numlower;
|
|
unsigned int stacklen = 0;
|
|
unsigned int i;
|
|
+ bool remote = false;
|
|
int err;
|
|
|
|
err = -ENOMEM;
|
|
@@ -904,7 +963,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|
lower = lowertmp;
|
|
for (numlower = 0; numlower < stacklen; 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)
|
|
goto out_put_lowerpath;
|
|
|
|
@@ -962,7 +1022,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|
if (!ufs->upper_mnt)
|
|
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;
|
|
oe = ovl_alloc_entry(numlower);
|