On 2015-08-11, Vagrant Cascadian <vagr...@debian.org> wrote:
> Control: found 4.1.3-1
>
> On 2015-05-26, Vagrant Cascadian wrote:
>> LTSP has been using aufs to provide a writeable tmpfs layer on top of
>> NFS since wheezy.
>>
>> With the switch to the overlay filesystem (after adjusting LTSP's
>> initramfs-tools hooks to support overlay), it appears to fail due to an
>> incompatibility with NFS. 
>
> I haven't confirmed, but this may be fixed in mainline, not sure how
> difficult it would be to backport to 4.1.x:

The attached 3 patches from mainline (one of them refreshed to apply to
4.1.3) allow it to mount with NFS as a lowerdir, and it mostly works,
although with some odd behavior when writing to files:

  # echo ltsp95 > /etc/hostname
  bash: /etc/hostname: Permission denied
  # touch /etc/hostname
  # echo ltsp95 > /etc/hostname
  # cat /etc/hostname
  ltsp95

Something seems a bit odd with the copyup behavior...

live well,
  vagrant

From a6f15d9a756571babbb2b2cd4fdd1b64a5de232b Mon Sep 17 00:00:00 2001
From: Miklos Szeredi <mszer...@suse.cz>
Date: Mon, 22 Jun 2015 13:53:48 +0200
Subject: [PATCH 1/2] ovl: don't traverse automount points

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 <mszer...@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;
-- 
2.1.4

From 7c03b5d45b8eebf0111125053d8fe887cc262ba6 Mon Sep 17 00:00:00 2001
From: Miklos Szeredi <mszer...@suse.cz>
Date: Mon, 22 Jun 2015 13:53:48 +0200
Subject: [PATCH 2/2] ovl: allow distributed fs as lower layer

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 <mszer...@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);
-- 
2.1.4

From 9391dd00d13c853ab4f2a85435288ae2202e0e43 Mon Sep 17 00:00:00 2001
From: Al Viro <v...@zeniv.linux.org.uk>
Date: Sun, 12 Jul 2015 10:39:45 -0400
Subject: [PATCH] fix a braino in ovl_d_select_inode()

when opening a directory we want the overlayfs inode, not one from
the topmost layer.

Reported-By: Andrey Jr. Melnikov <temnota...@gmail.com>
Tested-By: Andrey Jr. Melnikov <temnota...@gmail.com>
Signed-off-by: Al Viro <v...@zeniv.linux.org.uk>
---
 fs/overlayfs/inode.c | 3 +++
 1 file changed, 3 insertions(+)

Index: linux-4.1.3/fs/overlayfs/inode.c
===================================================================
--- linux-4.1.3.orig/fs/overlayfs/inode.c
+++ linux-4.1.3/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;

Attachment: signature.asc
Description: PGP signature

Reply via email to