ovl: Add support for NFS as lower filesystem (Closes: #786925)
This commit is contained in:
parent
21364b6286
commit
517cfd5213
|
@ -345,6 +345,7 @@ linux (4.1.5-1) UNRELEASED; urgency=medium
|
|||
- README.Debian, README.source: Update references to svn
|
||||
* Bump ABI to 2
|
||||
* virtio-net: drop NETIF_F_FRAGLIST (CVE-2015-5156)
|
||||
* ovl: Add support for NFS as lower filesystem (Closes: #786925)
|
||||
|
||||
-- Ian Campbell <ijc@debian.org> Tue, 04 Aug 2015 19:31:45 +0100
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
From: Miklos Szeredi <mszeredi@suse.cz>
|
||||
Date: Mon, 22 Jun 2015 13:53:48 +0200
|
||||
Subject: ovl: don't traverse automount points
|
||||
Origin: https://git.kernel.org/linus/a6f15d9a756571babbb2b2cd4fdd1b64a5de232b
|
||||
Bug-Debian: https://bugs.debian.org/786925
|
||||
|
||||
NFS and other distributed filesystems may place automount points in the
|
||||
tree. Previoulsy overlayfs refused to mount such filesystems types (based
|
||||
on the existence of the .d_automount callback), even if the actual export
|
||||
didn't have any automount points.
|
||||
|
||||
It cannot be determined in advance whether the filesystem has automount
|
||||
points or not. The solution is to allow fs with .d_automount but refuse to
|
||||
traverse any automount points encountered.
|
||||
|
||||
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
|
||||
---
|
||||
fs/overlayfs/super.c | 8 ++++++--
|
||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
|
||||
index bf8537c..de9d2ee 100644
|
||||
--- a/fs/overlayfs/super.c
|
||||
+++ b/fs/overlayfs/super.c
|
||||
@@ -303,6 +303,10 @@ 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) {
|
||||
+ dput(dentry);
|
||||
+ /* Don't support traversing automounts */
|
||||
+ dentry = ERR_PTR(-EREMOTE);
|
||||
}
|
||||
return dentry;
|
||||
}
|
||||
@@ -700,12 +704,12 @@ static bool ovl_is_allowed_fs_type(struct dentry *root)
|
||||
|
||||
/*
|
||||
* We don't support:
|
||||
- * - automount filesystems
|
||||
+ * - autofs
|
||||
* - filesystems with revalidate (FIXME for lower layer)
|
||||
* - filesystems with case insensitive names
|
||||
*/
|
||||
if (dop &&
|
||||
- (dop->d_manage || dop->d_automount ||
|
||||
+ (dop->d_manage ||
|
||||
dop->d_revalidate || dop->d_weak_revalidate ||
|
||||
dop->d_compare || dop->d_hash)) {
|
||||
return false;
|
|
@ -0,0 +1,28 @@
|
|||
From: Al Viro <viro@zeniv.linux.org.uk>
|
||||
Date: Sun, 12 Jul 2015 10:39:45 -0400
|
||||
Subject: fix a braino in ovl_d_select_inode()
|
||||
Origin: https://git.kernel.org/linus/9391dd00d13c853ab4f2a85435288ae2202e0e43
|
||||
Bug-Debian: https://bugs.debian.org/786925
|
||||
|
||||
when opening a directory we want the overlayfs inode, not one from
|
||||
the topmost layer.
|
||||
|
||||
Reported-By: Andrey Jr. Melnikov <temnota.am@gmail.com>
|
||||
Tested-By: Andrey Jr. Melnikov <temnota.am@gmail.com>
|
||||
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||
---
|
||||
fs/overlayfs/inode.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/fs/overlayfs/inode.c
|
||||
+++ b/fs/overlayfs/inode.c
|
||||
@@ -344,6 +344,9 @@ static int ovl_dentry_open(struct dentry
|
||||
enum ovl_path_type type;
|
||||
bool want_write = false;
|
||||
|
||||
+ if (d_is_dir(dentry))
|
||||
+ return d_backing_inode(dentry);
|
||||
+
|
||||
type = ovl_path_real(dentry, &realpath);
|
||||
if (ovl_open_need_copy_up(file->f_flags, type, realpath.dentry)) {
|
||||
want_write = true;
|
|
@ -0,0 +1,227 @@
|
|||
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);
|
|
@ -97,3 +97,6 @@ features/all/grsecurity/grsecurity-kconfig.patch
|
|||
#features/all/grsecurity/grsecurity-kbuild.patch
|
||||
features/all/grsecurity/grkernsec_perf_harden.patch
|
||||
bugfix/all/virtio-net-drop-netif_f_fraglist.patch
|
||||
bugfix/all/-ovl-don-t-traverse-automount-points.patch
|
||||
bugfix/all/ovl-allow-distributed-fs-as-lower-layer.patch
|
||||
bugfix/all/fix-a-braino-in-ovl_d_select_inode.patch
|
||||
|
|
Loading…
Reference in New Issue