ovl: Add support for NFS as lower filesystem (Closes: #786925)

This commit is contained in:
Ben Hutchings 2015-08-12 12:20:56 +02:00
parent 21364b6286
commit 517cfd5213
5 changed files with 309 additions and 0 deletions

1
debian/changelog vendored
View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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