linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 44/74] union-mount: Temporarily disable some syscalls
  2011-03-23  1:58 [PATCH 00/74] Union mounts version something or other Valerie Aurora
@ 2011-03-23  1:59 ` Valerie Aurora
  0 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  1:59 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

After some of the following patches in this series, a few system calls
will crash the kernel if called on union-mounted file systems.
Temporarily disable rename(), unlink(), and rmdir() on unioned file
systems until they are correctly implemented by later patches.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   17 +++++++++++++++++
 1 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index ce54ed4..3c00ce6 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -35,6 +35,7 @@
 #include <asm/uaccess.h>
 
 #include "internal.h"
+#include "union.h"
 
 /* [Feb-1997 T. Schoebel-Theuer]
  * Fundamental changes in the pathname lookup mechanisms (namei)
@@ -2375,6 +2376,11 @@ static long do_rmdir(int dfd, const char __user *pathname)
 	if (error)
 		return error;
 
+	/* rmdir() on union mounts not implemented yet */
+	error = -EINVAL;
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		goto exit1;
+
 	switch(nd.last_type) {
 	case LAST_DOTDOT:
 		error = -ENOTEMPTY;
@@ -2471,6 +2477,11 @@ static long do_unlinkat(int dfd, const char __user *pathname)
 	if (nd.last_type != LAST_NORM)
 		goto exit1;
 
+	/* unlink() on union mounts not implemented yet */
+	error = -EINVAL;
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		goto exit1;
+
 	nd.flags &= ~LOOKUP_PARENT;
 
 	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
@@ -2861,6 +2872,12 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 	if (oldnd.path.mnt != newnd.path.mnt)
 		goto exit2;
 
+	/* rename() on union mounts not implemented yet */
+	error = -EXDEV;
+	if (IS_DIR_UNIONED(oldnd.path.dentry) ||
+	    IS_DIR_UNIONED(newnd.path.dentry))
+		goto exit2;
+
 	old_dir = oldnd.path.dentry;
 	error = -EBUSY;
 	if (oldnd.last_type != LAST_NORM)
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 44/74] union-mount: Temporarily disable some syscalls
@ 2011-03-23  2:04 Valerie Aurora
  2011-03-23  2:04 ` [PATCH 45/74] union-mount: Basic infrastructure of __lookup_union() Valerie Aurora
                   ` (29 more replies)
  0 siblings, 30 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

After some of the following patches in this series, a few system calls
will crash the kernel if called on union-mounted file systems.
Temporarily disable rename(), unlink(), and rmdir() on unioned file
systems until they are correctly implemented by later patches.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   17 +++++++++++++++++
 1 files changed, 17 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index ce54ed4..3c00ce6 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -35,6 +35,7 @@
 #include <asm/uaccess.h>
 
 #include "internal.h"
+#include "union.h"
 
 /* [Feb-1997 T. Schoebel-Theuer]
  * Fundamental changes in the pathname lookup mechanisms (namei)
@@ -2375,6 +2376,11 @@ static long do_rmdir(int dfd, const char __user *pathname)
 	if (error)
 		return error;
 
+	/* rmdir() on union mounts not implemented yet */
+	error = -EINVAL;
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		goto exit1;
+
 	switch(nd.last_type) {
 	case LAST_DOTDOT:
 		error = -ENOTEMPTY;
@@ -2471,6 +2477,11 @@ static long do_unlinkat(int dfd, const char __user *pathname)
 	if (nd.last_type != LAST_NORM)
 		goto exit1;
 
+	/* unlink() on union mounts not implemented yet */
+	error = -EINVAL;
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		goto exit1;
+
 	nd.flags &= ~LOOKUP_PARENT;
 
 	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
@@ -2861,6 +2872,12 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 	if (oldnd.path.mnt != newnd.path.mnt)
 		goto exit2;
 
+	/* rename() on union mounts not implemented yet */
+	error = -EXDEV;
+	if (IS_DIR_UNIONED(oldnd.path.dentry) ||
+	    IS_DIR_UNIONED(newnd.path.dentry))
+		goto exit2;
+
 	old_dir = oldnd.path.dentry;
 	error = -EBUSY;
 	if (oldnd.last_type != LAST_NORM)
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 45/74] union-mount: Basic infrastructure of __lookup_union()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 46/74] union-mount: Process negative dentries in __lookup_union() Valerie Aurora
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Create a very simple version of union lookup.  This patch only looks
up the target in each layer of the union but does not process it in
any way.  Patches to do whiteouts, etc. follow.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 79 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 3c00ce6..2f7354e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -719,6 +719,85 @@ static __always_inline void follow_dotdot(struct nameidata *nd)
 	follow_mount(&nd->path);
 }
 
+static struct dentry *__lookup_hash(struct qstr *name, struct dentry *base,
+				    struct nameidata *nd);
+
+/*
+ * __lookup_union - Lookup and build union stack
+ *
+ * @nd - nameidata for the parent of @topmost
+ * @name - name of target
+ * @topmost - path of the target on the topmost file system
+ *
+ * Do the "union" part of lookup for @topmost - that is, look it up in
+ * the lower layers of its parent directory's union stack.  If
+ * @topmost is a directory, build its union stack.  @topmost is the
+ * path of the target in the topmost layer of the union file system.
+ * It is either a directory or a negative (non-whiteout) dentry.
+ *
+ * This function may stomp nd->path with the path of the parent
+ * directory of the lower layers, so the caller must save nd->path and
+ * restore it afterwards.
+ */
+
+static int __lookup_union(struct nameidata *nd, struct qstr *name,
+			  struct path *topmost)
+{
+	struct path lower, parent = nd->path;
+	struct path *path;
+	unsigned int i, layers = parent.dentry->d_sb->s_union_count;
+	int err = 0;
+
+	if (!topmost->dentry->d_inode) {
+		if (d_is_whiteout(topmost->dentry))
+			return 0;
+		if (IS_OPAQUE(parent.dentry->d_inode) &&
+		    !d_is_fallthru(topmost->dentry))
+			return 0;
+	}
+
+	/* If it's positive and not a dir, no lookup needed */
+	if (topmost->dentry->d_inode &&
+	    !S_ISDIR(topmost->dentry->d_inode->i_mode))
+		return 0;
+
+	/*
+	 * Note: This loop iterates through the union stack of the
+	 * parent of the target, not the target itself.  This function
+	 * builds the union stack of the target (if any).  The union
+	 * stack of the root directory is built at mount.
+	 */
+	for (i = 0; i < layers; i++) {
+		/*
+		 * Get the parent directory for this layer and lookup
+		 * the target in it.
+		 */
+		path = union_find_dir(parent.dentry, i);
+		if (!path->mnt)
+			continue;
+
+		nd->path = *path;
+		lower.mnt = mntget(nd->path.mnt);
+		mutex_lock(&nd->path.dentry->d_inode->i_mutex);
+		lower.dentry = __lookup_hash(name, nd->path.dentry, nd);
+		mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
+
+		if (IS_ERR(lower.dentry)) {
+			mntput(lower.mnt);
+			err = PTR_ERR(lower.dentry);
+			goto out_err;
+		}
+		/* XXX - do nothing, lookup rule processing in later patches */
+		path_put(&lower);
+	}
+	return 0;
+
+out_err:
+	d_free_unions(topmost->dentry);
+	path_put(&lower);
+	return err;
+}
+
 /*
  * Allocate a dentry with name and parent, and perform a parent
  * directory ->lookup on it. Returns the new dentry, or ERR_PTR
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 46/74] union-mount: Process negative dentries in __lookup_union()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
  2011-03-23  2:04 ` [PATCH 45/74] union-mount: Basic infrastructure of __lookup_union() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 47/74] union-mount: Return files found in lower layers " Valerie Aurora
                   ` (27 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Whiteouts end a union lookup.  So do opaque directories, unless
specific fallthru entry exists for this name.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   23 ++++++++++++++++++++++-
 1 files changed, 22 insertions(+), 1 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 2f7354e..5c62042 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -787,11 +787,32 @@ static int __lookup_union(struct nameidata *nd, struct qstr *name,
 			err = PTR_ERR(lower.dentry);
 			goto out_err;
 		}
-		/* XXX - do nothing, lookup rule processing in later patches */
+
+		/*
+		 * A negative dentry can mean several things.  A plain
+		 * negative dentry is ignored and lookup continues to
+		 * the next layer.  But a whiteout or a non-fallthru
+		 * in an opaque dir covers everything below it.
+		 */
+		if (!lower.dentry->d_inode) {
+			if (d_is_whiteout(lower.dentry))
+				goto out_lookup_done;
+			if (IS_OPAQUE(nd->path.dentry->d_inode) &&
+			    !d_is_fallthru(lower.dentry))
+				goto out_lookup_done;
+			path_put(&lower);
+			continue;
+		}
+
+		/* XXX - do nothing, more in later patches */
 		path_put(&lower);
 	}
 	return 0;
 
+out_lookup_done:
+	path_put(&lower);
+	return 0;
+
 out_err:
 	d_free_unions(topmost->dentry);
 	path_put(&lower);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 47/74] union-mount: Return files found in lower layers in __lookup_union()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
  2011-03-23  2:04 ` [PATCH 45/74] union-mount: Basic infrastructure of __lookup_union() Valerie Aurora
  2011-03-23  2:04 ` [PATCH 46/74] union-mount: Process negative dentries in __lookup_union() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 48/74] union-mount: Build union stack " Valerie Aurora
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

If we find a file during union lookup, don't look in any lower layers
and replace the topmost path with the file's path.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   23 +++++++++++++++++++++++
 1 files changed, 23 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 5c62042..3532ea9 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -804,11 +804,34 @@ static int __lookup_union(struct nameidata *nd, struct qstr *name,
 			continue;
 		}
 
+		/*
+		 * Files block everything below them.  Special case:
+		 * If we find a file below a directory (which makes no
+		 * sense), just ignore the file and return the
+		 * directory above it.
+		 */
+		if (!S_ISDIR(lower.dentry->d_inode->i_mode)) {
+			if (topmost->dentry->d_inode &&
+			    S_ISDIR(topmost->dentry->d_inode->i_mode))
+				goto out_lookup_done;
+			goto out_found_file;
+		}
+
 		/* XXX - do nothing, more in later patches */
 		path_put(&lower);
 	}
 	return 0;
 
+out_found_file:
+	/*
+	 * Swap out the positive lower dentry with the negative upper
+	 * dentry for this file.  Note that the matching mntput() is done
+	 * in link_path_walk().
+	 */
+	dput(topmost->dentry);
+	*topmost = lower;
+	return 0;
+
 out_lookup_done:
 	path_put(&lower);
 	return 0;
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 48/74] union-mount: Build union stack in __lookup_union()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (2 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 47/74] union-mount: Return files found in lower layers " Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 49/74] union-mount: Follow mount " Valerie Aurora
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Build the union stack for directories as we look them up.  Create the
topmost directory if it doesn't exist.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   18 ++++++++++++++++--
 1 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 3532ea9..4170f92 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -817,8 +817,22 @@ static int __lookup_union(struct nameidata *nd, struct qstr *name,
 			goto out_found_file;
 		}
 
-		/* XXX - do nothing, more in later patches */
-		path_put(&lower);
+		/*
+		 * Now we know the target is a directory.  Create a
+		 * matching topmost directory if one doesn't already
+		 * exist, and add this layer's directory to the union
+		 * stack for the topmost directory.
+		 */
+		if (!topmost->dentry->d_inode) {
+			err = union_create_topmost_dir(&parent, name, topmost,
+						       &lower);
+			if (err)
+				goto out_err;
+		}
+
+		err = union_add_dir(topmost, &lower, i);
+		if (err)
+			goto out_err;
 	}
 	return 0;
 
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 49/74] union-mount: Follow mount in __lookup_union()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (3 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 48/74] union-mount: Build union stack " Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 50/74] union-mount: Add lookup_union() Valerie Aurora
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

In order for read-only layers of a union to have submounts, we have to
follow mounts on directories in union lookup.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 4170f92..789015a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -817,6 +817,8 @@ static int __lookup_union(struct nameidata *nd, struct qstr *name,
 			goto out_found_file;
 		}
 
+		follow_mount(&lower);
+
 		/*
 		 * Now we know the target is a directory.  Create a
 		 * matching topmost directory if one doesn't already
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 50/74] union-mount: Add lookup_union()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (4 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 49/74] union-mount: Follow mount " Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 51/74] union-mount: Add do_lookup_union() wrapper for __lookup_union() Valerie Aurora
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

lookup_union() checks if union lookup is actually necessary for this
dentry, and marks the dentry to show that the union lookup has been
performed.

__lookup_union() may overwrite the parent's path in the nameidata
struct for the entry being looked up.  This is because it reuses the
same nameidata to do lookups in each of the lower layer directories.
lookup_union() saves and restores the original parent's path in the
nameidata.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 50 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 789015a..8257537 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -859,6 +859,56 @@ out_err:
 }
 
 /*
+ * lookup_union - Lookup and/or build union stack if needed
+ *
+ * @nd - nameidata for the parent of @topmost
+ * @name - name of target
+ * @topmost - path of the target on the topmost file system
+ *
+ * Check if we need to do a union lookup on this target.  Mark dentry
+ * to show lookup union has been performed.
+ *
+ * We borrow the nameidata struct from the topmost layer to do the
+ * revalidation on lower dentries, replacing the topmost parent
+ * directory's path with that of the matching parent dir in each lower
+ * layer.  This wrapper for __lookup_union() saves the topmost layer's
+ * path and restores it when we are done.
+ *
+ * Caller must hold parent i_mutex.
+ */
+static int lookup_union(struct nameidata *nd, struct qstr *name,
+			struct path *topmost)
+{
+	struct path saved_path;
+	int err = 0;
+
+	BUG_ON(!IS_MNT_UNION(nd->path.mnt) && !IS_MNT_UNION(topmost->mnt));
+	BUG_ON(!mutex_is_locked(&nd->path.dentry->d_inode->i_mutex));
+
+	/*
+	 * Initial test done outside of parent i_mutex lock, recheck
+	 * it.  We only set this flag inside parent i_mutex so it's
+	 * safe to check it here (only need d_lock when setting to
+	 * avoid squashing other flags).
+	 */
+	if (topmost->dentry->d_flags & DCACHE_UNION_LOOKUP_DONE)
+		return 0;
+
+	saved_path = nd->path;
+
+	err = __lookup_union(nd, name, topmost);
+
+	nd->path = saved_path;
+
+	/* XXX move into dcache.h */
+	spin_lock(&topmost->dentry->d_lock);
+	topmost->dentry->d_flags |= DCACHE_UNION_LOOKUP_DONE;
+	spin_unlock(&topmost->dentry->d_lock);
+
+	return err;
+}
+
+/*
  * Allocate a dentry with name and parent, and perform a parent
  * directory ->lookup on it. Returns the new dentry, or ERR_PTR
  * on error. parent->d_inode->i_mutex must be held. d_lookup must
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 51/74] union-mount: Add do_lookup_union() wrapper for __lookup_union()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (5 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 50/74] union-mount: Add lookup_union() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 52/74] union-mount: Call union lookup functions in lookup path Valerie Aurora
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

do_lookup_union() locks the parent directory and follows the mount
after lookup.  It is appropriate for calling from do_lookup().

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 8257537..3fcb42c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -909,6 +909,28 @@ static int lookup_union(struct nameidata *nd, struct qstr *name,
 }
 
 /*
+ * do_union_lookup - union mount-aware part of do_lookup
+ *
+ * do_lookup()-style wrapper for lookup_union().  Follows mounts.
+ */
+
+static int do_lookup_union(struct nameidata *nd, struct qstr *name,
+			   struct path *topmost)
+{
+	struct dentry *parent = nd->path.dentry;
+	struct inode *dir = parent->d_inode;
+	int err;
+
+	mutex_lock(&dir->i_mutex);
+	err = lookup_union(nd, name, topmost);
+	mutex_unlock(&dir->i_mutex);
+
+	__follow_mount(topmost);
+
+	return err;
+}
+
+/*
  * Allocate a dentry with name and parent, and perform a parent
  * directory ->lookup on it. Returns the new dentry, or ERR_PTR
  * on error. parent->d_inode->i_mutex must be held. d_lookup must
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 52/74] union-mount: Call union lookup functions in lookup path
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (6 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 51/74] union-mount: Add do_lookup_union() wrapper for __lookup_union() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 53/74] union-mount: Create whiteout on unlink() Valerie Aurora
                   ` (21 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Union mounts hook into the lookup path in two places: do_lookup() and
lookup_hash().

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 3fcb42c..531a0e0 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -995,6 +995,11 @@ done:
 	path->mnt = mnt;
 	path->dentry = dentry;
 	__follow_mount(path);
+	if (needs_lookup_union(&nd->path, path)) {
+		int err = do_lookup_union(nd, name, path);
+		if (err < 0)
+			return err;
+	}
 	return 0;
 
 need_lookup:
@@ -1417,8 +1422,13 @@ static int lookup_hash(struct nameidata *nd, struct qstr *name,
 		err = PTR_ERR(path->dentry);
 		path->dentry = NULL;
 		path->mnt = NULL;
+		return err;
 	}
+
+	if (needs_lookup_union(&nd->path, path))
+		err = lookup_union(nd, name, path);
 	return err;
+
 }
 
 static int __lookup_one_len(const char *name, struct qstr *this,
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 53/74] union-mount: Create whiteout on unlink()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (7 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 52/74] union-mount: Call union lookup functions in lookup path Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 54/74] union-mount: Create whiteout on rmdir() Valerie Aurora
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Whiteout an unlinked directory entry in a union mounted file system.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |    9 ++++-----
 1 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 531a0e0..83f9c62 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2698,11 +2698,6 @@ static long do_unlinkat(int dfd, const char __user *pathname)
 	if (nd.last_type != LAST_NORM)
 		goto exit1;
 
-	/* unlink() on union mounts not implemented yet */
-	error = -EINVAL;
-	if (IS_DIR_UNIONED(nd.path.dentry))
-		goto exit1;
-
 	nd.flags &= ~LOOKUP_PARENT;
 
 	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
@@ -2720,6 +2715,10 @@ static long do_unlinkat(int dfd, const char __user *pathname)
 		error = security_path_unlink(&nd.path, path.dentry);
 		if (error)
 			goto exit3;
+		if (IS_DIR_UNIONED(nd.path.dentry)) {
+			error = do_whiteout(&nd, &path, 0);
+			goto exit3;
+		}
 		error = vfs_unlink(nd.path.dentry->d_inode, path.dentry);
 exit3:
 		mnt_drop_write(nd.path.mnt);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 54/74] union-mount: Create whiteout on rmdir()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (8 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 53/74] union-mount: Create whiteout on unlink() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 55/74] union-mount: Set opaque flag on new directories in unioned file systems Valerie Aurora
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Whiteout a deleted directory in a union mounted file system.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |    9 ++++-----
 1 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 83f9c62..9c6803a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2597,11 +2597,6 @@ static long do_rmdir(int dfd, const char __user *pathname)
 	if (error)
 		return error;
 
-	/* rmdir() on union mounts not implemented yet */
-	error = -EINVAL;
-	if (IS_DIR_UNIONED(nd.path.dentry))
-		goto exit1;
-
 	switch(nd.last_type) {
 	case LAST_DOTDOT:
 		error = -ENOTEMPTY;
@@ -2626,6 +2621,10 @@ static long do_rmdir(int dfd, const char __user *pathname)
 	error = security_path_rmdir(&nd.path, path.dentry);
 	if (error)
 		goto exit4;
+	if (IS_DIR_UNIONED(nd.path.dentry)) {
+		error = do_whiteout(&nd, &path, 1);
+		goto exit4;
+	}
 	error = vfs_rmdir(nd.path.dentry->d_inode, path.dentry);
 exit4:
 	mnt_drop_write(nd.path.mnt);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 55/74] union-mount: Set opaque flag on new directories in unioned file systems
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (9 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 54/74] union-mount: Create whiteout on rmdir() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 56/74] union-mount: Copy up directory entries on first readdir() Valerie Aurora
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Jan Blunck, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

If we mkdir() a directory on the top layer of a union, we don't want
entries from a matching directory on the lower layer to "show through"
suddenly.  To prevent this, we set the opaque flag on a directory in a
union mount if there is no matching directory on the lower layers.

Signed-off-by: Jan Blunck <jblunck@suse.de>
Signed-off-by: Valerie Aurora <vaurora@redhat.com>
Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   13 +++++++++++--
 1 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 9c6803a..47fe25a 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2323,8 +2323,17 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 		return error;
 
 	error = dir->i_op->mkdir(dir, dentry, mode);
-	if (!error)
-		fsnotify_mkdir(dir, dentry);
+	if (error)
+		return error;
+
+	/* XXX racy - crash now and dir isn't opaque */
+	if (IS_DIR_UNIONED(dentry->d_parent)) {
+		dentry->d_inode->i_flags |= S_OPAQUE;
+		mark_inode_dirty(dentry->d_inode);
+	}
+
+	fsnotify_mkdir(dir, dentry);
+
 	return error;
 }
 
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 56/74] union-mount: Copy up directory entries on first readdir()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (10 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 55/74] union-mount: Set opaque flag on new directories in unioned file systems Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 57/74] union-mount: Add generic_readdir_fallthru() helper Valerie Aurora
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

readdir() in union mounts is implemented by copying up all visible
directory entries from the lower level directories to the topmost
directory.  Directory entries that refer to lower level file system
objects are marked as "fallthru" in the topmost directory.

Thanks to Felix Fietkau <nbd@openwrt.org> for a bug fix.

Signed-off-by: Valerie Aurora <vaurora@redhat.com>
Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/readdir.c |    9 +++
 fs/union.c   |  189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/union.h   |    2 +
 3 files changed, 200 insertions(+), 0 deletions(-)

diff --git a/fs/readdir.c b/fs/readdir.c
index de703d6..a87ae02 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -20,6 +20,8 @@
 
 #include <asm/uaccess.h>
 
+#include "union.h"
+
 int vfs_readdir(struct file *file, filldir_t filler, void *buf)
 {
 	struct inode *inode = file->f_path.dentry->d_inode;
@@ -37,9 +39,16 @@ int vfs_readdir(struct file *file, filldir_t filler, void *buf)
 
 	res = -ENOENT;
 	if (!IS_DEADDIR(inode)) {
+		if (IS_DIR_UNIONED(file->f_path.dentry) && !IS_OPAQUE(inode)) {
+			res = union_copyup_dir(&file->f_path);
+			if (res)
+				goto out_unlock;
+		}
+
 		res = file->f_op->readdir(file, buf, filler);
 		file_accessed(file);
 	}
+out_unlock:
 	mutex_unlock(&inode->i_mutex);
 out:
 	return res;
diff --git a/fs/union.c b/fs/union.c
index 446116f..62bad6a 100644
--- a/fs/union.c
+++ b/fs/union.c
@@ -22,6 +22,8 @@
 #include <linux/fs_struct.h>
 #include <linux/slab.h>
 #include <linux/namei.h>
+#include <linux/file.h>
+#include <linux/security.h>
 
 #include "union.h"
 
@@ -146,3 +148,190 @@ out:
 
 	return error;
 }
+
+struct union_filldir_info {
+	struct dentry *topmost_dentry;
+	int error;
+};
+
+/**
+ * union_copyup_dir_one - copy up a single directory entry
+ *
+ * Individual directory entry copyup function for union_copyup_dir.
+ * We get the entries from higher level layers first.
+ */
+
+static int union_copyup_dir_one(void *buf, const char *name, int namlen,
+				loff_t offset, u64 ino, unsigned int d_type)
+{
+	struct union_filldir_info *ufi = (struct union_filldir_info *) buf;
+	struct dentry *topmost_dentry = ufi->topmost_dentry;
+	struct dentry *dentry;
+	int err = 0;
+
+	switch (namlen) {
+	case 2:
+		if (name[1] != '.')
+			break;
+	case 1:
+		if (name[0] != '.')
+			break;
+		return 0;
+	}
+
+	/* Lookup this entry in the topmost directory */
+	dentry = lookup_one_len(name, topmost_dentry, namlen);
+
+	if (IS_ERR(dentry)) {
+		printk(KERN_WARNING "%s: error looking up %s\n", __func__,
+		       dentry->d_name.name);
+		err = PTR_ERR(dentry);
+		goto out;
+	}
+
+	/* XXX do we need to revalidate on readdir anyway? think NFS */
+	if (dentry->d_op && dentry->d_op->d_revalidate)
+		goto fallthru;
+	/*
+	 * If the entry already exists, one of the following is true:
+	 * it was already copied up (due to an earlier lookup), an
+	 * entry with the same name already exists on the topmost file
+	 * system, it is a whiteout, or it is a fallthru.  In each
+	 * case, the top level entry masks any entries from lower file
+	 * systems, so don't copy up this entry.
+	 */
+	if (dentry->d_inode || d_is_whiteout(dentry) || d_is_fallthru(dentry))
+		goto out_dput;
+
+	/*
+	 * If the entry doesn't exist, create a fallthru entry in the
+	 * topmost file system.  All possible directory types are
+	 * used, so each file system must implement its own way of
+	 * storing a fallthru entry.
+	 */
+fallthru:
+	err = topmost_dentry->d_inode->i_op->fallthru(topmost_dentry->d_inode,
+						      dentry);
+
+	/* It's okay if it exists, ultimate responsibility rests with ->fallthru() */
+	if (err == -EEXIST)
+		err = 0;
+out_dput:
+	dput(dentry);
+out:
+	if (err)
+		ufi->error = err;
+	return err;
+}
+
+/**
+ * union_copyup_dir - copy up low-level directory entries to topmost dir
+ *
+ * readdir() is difficult to support on union file systems for two
+ * reasons: We must eliminate duplicates and apply whiteouts, and we
+ * must return something in f_pos that lets us restart in the same
+ * place when we return.  Our solution is to, on first readdir() of
+ * the directory, copy up all visible entries from the low-level file
+ * systems and mark the entries that refer to low-level file system
+ * objects as "fallthru" entries.
+ *
+ * Locking strategy: We hold the topmost dir's i_mutex on entry.  We
+ * grab the i_mutex on lower directories one by one.  So the locking
+ * order is:
+ *
+ * Writable/topmost layers > Read-only/lower layers
+ *
+ * So there is no problem with lock ordering for union stacks with
+ * multiple lower layers.  E.g.:
+ *
+ * (topmost) A->B->C (bottom)
+ * (topmost) D->C->B (bottom)
+ *
+ */
+
+int union_copyup_dir(struct path *topmost_path)
+{
+	struct union_filldir_info ufi;
+	struct dentry *topmost_dentry = topmost_path->dentry;
+	unsigned int i, layers = topmost_dentry->d_sb->s_union_count;
+	int error = 0;
+
+	BUG_ON(IS_OPAQUE(topmost_dentry->d_inode));
+
+	if (!topmost_dentry->d_inode->i_op || !topmost_dentry->d_inode->i_op->fallthru)
+		return -EOPNOTSUPP;
+
+	error = mnt_want_write(topmost_path->mnt);
+	if (error)
+		return error;
+
+	for (i = 0; i < layers; i++) {
+		struct file * ftmp;
+		struct inode * inode;
+		struct path *path;
+
+		path = union_find_dir(topmost_dentry, i);
+		if (!path->mnt)
+			continue;
+		/* dentry_open() doesn't get a path reference itself */
+		path_get(path);
+		ftmp = dentry_open(path->dentry, path->mnt,
+				   O_RDONLY | O_DIRECTORY | O_NOATIME,
+				   current_cred());
+		if (IS_ERR(ftmp)) {
+			printk (KERN_ERR "unable to open dir %s for "
+				"directory copyup: %ld\n",
+				path->dentry->d_name.name, PTR_ERR(ftmp));
+			path_put(path);
+			error = PTR_ERR(ftmp);
+			break;
+		}
+
+		inode = path->dentry->d_inode;
+		mutex_lock(&inode->i_mutex);
+
+		error = -ENOENT;
+		if (IS_DEADDIR(inode))
+			goto out_fput;
+		/*
+		 * Read the whole directory, calling our directory
+		 * entry copyup function on each entry.
+		 */
+		ufi.topmost_dentry = topmost_dentry;
+		ufi.error = 0;
+		error = ftmp->f_op->readdir(ftmp, &ufi, union_copyup_dir_one);
+out_fput:
+		mutex_unlock(&inode->i_mutex);
+		fput(ftmp);
+
+		if (ufi.error)
+			error = ufi.error;
+		if (error)
+			break;
+
+		/* XXX Should process directories below an opaque
+		 * directory in case there are fallthrus in it */
+		if (IS_OPAQUE(path->dentry->d_inode))
+			break;
+	}
+	/*
+	 * Mark this dir opaque to show that we have already copied up
+	 * the lower entries.  Be sure to do this AFTER the directory
+	 * entries have been copied up so that if we crash in the
+	 * middle of copyup, we will try to copyup the dir next time
+	 * we read it.
+	 *
+	 * XXX - Could leave directory non-opaque, and force
+	 * reread/copyup of directory each time it is read in from
+	 * disk.  That would make it easy to update lower file systems
+	 * (when not union mounted) and have the changes show up when
+	 * union mounted again.
+	 */
+	if (!error) {
+		topmost_dentry->d_inode->i_flags |= S_OPAQUE;
+		mark_inode_dirty(topmost_dentry->d_inode);
+	}
+
+	mnt_drop_write(topmost_path->mnt);
+	return error;
+}
diff --git a/fs/union.h b/fs/union.h
index 9efb177..75c8d8b 100644
--- a/fs/union.h
+++ b/fs/union.h
@@ -56,6 +56,7 @@ extern void d_free_unions(struct dentry *);
 extern int union_add_dir(struct path *, struct path *, unsigned int);
 extern int union_create_topmost_dir(struct path *, struct qstr *, struct path *,
 				    struct path *);
+extern int union_copyup_dir(struct path *);
 
 static inline int needs_lookup_union(struct path *parent_path, struct path *path)
 {
@@ -88,6 +89,7 @@ static inline struct path *union_find_dir(struct dentry *dentry,
 #define union_find_dir(x, y)		({ BUG(); (NULL); })
 #define union_create_topmost_dir(w, x, y, z)	({ BUG(); (0); })
 #define needs_lookup_union(x, y)	({ (0); })
+#define union_copyup_dir(x)		({ BUG(); (0); })
 
 #endif	/* CONFIG_UNION_MOUNT */
 #endif	/* __KERNEL__ */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 57/74] union-mount: Add generic_readdir_fallthru() helper
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (11 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 56/74] union-mount: Copy up directory entries on first readdir() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 58/74] fallthru: ext2 support for lookup of d_type/d_ino in fallthrus Valerie Aurora
                   ` (16 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

In readdir(), client file systems need to lookup the target of a
fallthru in a lower layer for three reasons: (1) fill in d_ino, (2)
fill in d_type, (2) make sure there is something to fall through to
(and if not, don't return this dentry).  Create a generic helper
function.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/union.c         |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/union.h         |    3 ++
 include/linux/fs.h |   15 ++++++++++++++
 3 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/fs/union.c b/fs/union.c
index 62bad6a..2fdc095 100644
--- a/fs/union.c
+++ b/fs/union.c
@@ -335,3 +335,58 @@ out_fput:
 	mnt_drop_write(topmost_path->mnt);
 	return error;
 }
+
+/* Relationship between i_mode and the DT_xxx types */
+static inline unsigned char dt_type(struct inode *inode)
+{
+	return (inode->i_mode >> 12) & 15;
+}
+
+/**
+ * generic_readdir_fallthru - Helper to lookup target of a fallthru
+ *
+ * @topmost_dentry: dentry for the topmost dentry of the dir being read
+ * @name: name of fallthru dirent
+ * @namelen: length of @name
+ * @ino: return inode number of target, if found
+ * @d_type: return directory type of target, if found
+ *
+ * In readdir(), client file systems need to lookup the target of a
+ * fallthru in a lower layer for three reasons: (1) fill in d_ino, (2)
+ * fill in d_type, (2) make sure there is something to fall through to
+ * (and if not, don't return this dentry).  Upon detecting a fallthru
+ * dentry in readdir(), the client file system should call this function.
+ *
+ * Returns 0 on success and -ENOENT if no matching directory entry was
+ * found (which can happen when the topmost file system is unmounted
+ * and remounted over a different file system than).  Any other errors
+ * are unexpected.
+ */
+
+int
+generic_readdir_fallthru(struct dentry *topmost_dentry, const char *name,
+			 int namlen, ino_t *ino, unsigned char *d_type)
+{
+	struct path *parent;
+	struct dentry *dentry;
+	unsigned int i, layers = topmost_dentry->d_sb->s_union_count;
+
+	BUG_ON(!mutex_is_locked(&topmost_dentry->d_inode->i_mutex));
+
+	for (i = 0; i < layers; i++) {
+		parent = union_find_dir(topmost_dentry, i);
+		mutex_lock(&parent->dentry->d_inode->i_mutex);
+		dentry = lookup_one_len(name, parent->dentry, namlen);
+		mutex_unlock(&parent->dentry->d_inode->i_mutex);
+		if (IS_ERR(dentry))
+			return PTR_ERR(dentry);
+		if (dentry->d_inode) {
+			*ino = dentry->d_inode->i_ino;
+			*d_type = dt_type(dentry->d_inode);
+			dput(dentry);
+			return 0;
+		}
+		dput(dentry);
+	}
+	return -ENOENT;
+}
diff --git a/fs/union.h b/fs/union.h
index 75c8d8b..4620cc1 100644
--- a/fs/union.h
+++ b/fs/union.h
@@ -57,6 +57,8 @@ extern int union_add_dir(struct path *, struct path *, unsigned int);
 extern int union_create_topmost_dir(struct path *, struct qstr *, struct path *,
 				    struct path *);
 extern int union_copyup_dir(struct path *);
+extern int generic_readdir_fallthru(struct dentry *topmost_dentry, const char *name,
+				    int namlen, ino_t *ino, unsigned char *d_type);
 
 static inline int needs_lookup_union(struct path *parent_path, struct path *path)
 {
@@ -90,6 +92,7 @@ static inline struct path *union_find_dir(struct dentry *dentry,
 #define union_create_topmost_dir(w, x, y, z)	({ BUG(); (0); })
 #define needs_lookup_union(x, y)	({ (0); })
 #define union_copyup_dir(x)		({ BUG(); (0); })
+#define generic_readdir_fallthru(w, x, y, z)	({ BUG(); (0); })
 
 #endif	/* CONFIG_UNION_MOUNT */
 #endif	/* __KERNEL__ */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 258f99b..3dfb0e2 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2403,6 +2403,21 @@ extern ssize_t simple_write_to_buffer(void *to, size_t available, loff_t *ppos,
 
 extern int generic_file_fsync(struct file *, int);
 
+#ifdef CONFIG_UNION_MOUNT
+extern int generic_readdir_fallthru(struct dentry *topmost_dentry, const char *name,
+				    int namlen, ino_t *ino, unsigned char *d_type);
+#else
+static inline int generic_readdir_fallthru(struct dentry *topmost_dentry, const char *name,
+					   int namlen, ino_t *ino, unsigned char *d_type)
+{
+	/*
+	 * Found a fallthru on a kernel without union support.
+	 * There's nothing to fall through to, so return -ENOENT.
+	 */
+	return -ENOENT;
+}
+#endif
+
 #ifdef CONFIG_MIGRATION
 extern int buffer_migrate_page(struct address_space *,
 				struct page *, struct page *);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 58/74] fallthru: ext2 support for lookup of d_type/d_ino in fallthrus
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (12 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 57/74] union-mount: Add generic_readdir_fallthru() helper Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 59/74] fallthru: tmpfs " Valerie Aurora
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Now that we have full union lookup support, lookup the true d_type and
d_ino of a fallthru.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/ext2/dir.c |   20 +++++++++++++-------
 1 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index daff471..145d9eb 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -352,15 +352,21 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
 				}
 			} else if (de->file_type == EXT2_FT_FALLTHRU) {
 				int over;
+				unsigned char d_type = DT_UNKNOWN;
+				ino_t ino;
+				int err;
 
 				offset = (char *)de - kaddr;
-				/* XXX placeholder until generic_readdir_fallthru() arrives */
-				over = filldir(dirent, de->name, de->name_len,
-					       (n<<PAGE_CACHE_SHIFT) | offset,
-					       1, DT_UNKNOWN); /* XXX */
-				if (over) {
-					ext2_put_page(page);
-					return 0;
+				err = generic_readdir_fallthru(filp->f_path.dentry, de->name,
+							       de->name_len, &ino, &d_type);
+				if (!err) {
+					over = filldir(dirent, de->name, de->name_len,
+						       (n<<PAGE_CACHE_SHIFT) | offset,
+						       ino, d_type);
+					if (over) {
+						ext2_put_page(page);
+						return 0;
+					}
 				}
 			}
 			filp->f_pos += ext2_rec_len_from_disk(de->rec_len);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 59/74] fallthru: tmpfs support for lookup of d_type/d_ino in fallthrus
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (13 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 58/74] fallthru: ext2 support for lookup of d_type/d_ino in fallthrus Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 60/74] fallthru: jffs2 " Valerie Aurora
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux
  Cc: viro, Valerie Aurora, Hugh Dickins, linux-mm, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Now that we have full union lookup support, lookup the true d_type and
d_ino of a fallthru.

Cc: Hugh Dickins <hughd@google.com>
Cc: linux-mm@kvack.org
Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/libfs.c |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/fs/libfs.c b/fs/libfs.c
index a73423d..8453c75 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -132,6 +132,7 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
 	ino_t ino;
 	char d_type;
 	int i = filp->f_pos;
+	int err = 0;
 
 	switch (i) {
 		case 0:
@@ -161,9 +162,13 @@ int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
 
 				spin_unlock(&dcache_lock);
 				if (d_is_fallthru(next)) {
-					/* XXX placeholder until generic_readdir_fallthru() arrives */
-					ino = 1;
-					d_type = DT_UNKNOWN;
+					/* On tmpfs, should only fail with ENOMEM, EIO, etc. */
+					err = generic_readdir_fallthru(filp->f_path.dentry,
+								       next->d_name.name,
+								       next->d_name.len,
+								       &ino, &d_type);
+					if (err)
+						return err;
 				} else {
 					ino = next->d_inode->i_ino;
 					d_type = dt_type(next->d_inode);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 60/74] fallthru: jffs2 support for lookup of d_type/d_ino in fallthrus
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (14 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 59/74] fallthru: tmpfs " Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 61/74] VFS: Split inode_permission() and create path_permission() Valerie Aurora
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Now that we have full union lookup support, lookup the true d_type and
d_ino of a fallthru.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/jffs2/dir.c |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index dc0e01e..bd270dc 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -174,9 +174,14 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
 			continue;
 		}
 		if (fd->type == JFFS2_DT_FALLTHRU) {
-			/* XXX placeholder until generic_readdir_fallthru() arrives */
-			ino = 1;
-			d_type = DT_UNKNOWN;
+			int err;
+			err = generic_readdir_fallthru(filp->f_path.dentry, fd->name, strlen(fd->name),
+						       &ino, &d_type);
+			if (err) {
+				D2(printk(KERN_DEBUG "Skipping fallthru dirent \"%s\"\n", fd->name));
+				offset++;
+				continue;
+			}
 		} else if (!fd->ino && (fd->type != DT_WHT)) {
 			D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name));
 			offset++;
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 61/74] VFS: Split inode_permission() and create path_permission()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (15 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 60/74] fallthru: jffs2 " Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 62/74] VFS: Create user_path_nd() to lookup both parent and target Valerie Aurora
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Split inode_permission() into inode and file-system-dependent parts.
Create path_permission() to check permission based on the path to the
inode.  This is for union mounts, in which an inode can be located on
a read-only lower layer file system but is still writable, since we
will copy it up to the writable top layer file system.  So in that
case, we want to ignore MS_RDONLY on the lower layer.  To make this
decision, we must know the path (vfsmount, dentry) of both the target
and its parent.

XXX - so ugly!

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c         |   92 ++++++++++++++++++++++++++++++++++++++++++++--------
 include/linux/fs.h |    1 +
 2 files changed, 79 insertions(+), 14 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 47fe25a..bf8d857 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -241,29 +241,20 @@ int generic_permission(struct inode *inode, int mask,
 }
 
 /**
- * inode_permission  -  check for access rights to a given inode
+ * __inode_permission  -  check for access rights to a given inode
  * @inode:	inode to check permission on
  * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
  *
  * Used to check for read/write/execute permissions on an inode.
- * We use "fsuid" for this, letting us set arbitrary permissions
- * for filesystem access without changing the "normal" uids which
- * are used for other things.
+ *
+ * This does not check for a read-only file system.  You probably want
+ * inode_permission().
  */
-int inode_permission(struct inode *inode, int mask)
+static int __inode_permission(struct inode *inode, int mask)
 {
 	int retval;
 
 	if (mask & MAY_WRITE) {
-		umode_t mode = inode->i_mode;
-
-		/*
-		 * Nobody gets write access to a read-only fs.
-		 */
-		if (IS_RDONLY(inode) &&
-		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
-			return -EROFS;
-
 		/*
 		 * Nobody gets write access to an immutable file.
 		 */
@@ -287,6 +278,79 @@ int inode_permission(struct inode *inode, int mask)
 }
 
 /**
+ * sb_permission  -  check superblock-level permissions
+ * @sb: superblock of inode to check permission on
+ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ *
+ * Separate out file-system wide checks from inode-specific permission
+ * checks.  In particular, union mounts want to check the read-only
+ * status of the top-level file system, not the lower.
+ */
+int sb_permission(struct super_block *sb, struct inode *inode, int mask)
+{
+	if (mask & MAY_WRITE) {
+		umode_t mode = inode->i_mode;
+
+		/*
+		 * Nobody gets write access to a read-only fs.
+		 */
+		if ((sb->s_flags & MS_RDONLY) &&
+		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+			return -EROFS;
+	}
+	return 0;
+}
+
+/**
+ * inode_permission  -  check for access rights to a given inode
+ * @inode:	inode to check permission on
+ * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ *
+ * Used to check for read/write/execute permissions on an inode.
+ * We use "fsuid" for this, letting us set arbitrary permissions
+ * for filesystem access without changing the "normal" uids which
+ * are used for other things.
+ */
+int inode_permission(struct inode *inode, int mask)
+{
+	int retval;
+
+	retval = sb_permission(inode->i_sb, inode, mask);
+	if (retval)
+		return retval;
+	return __inode_permission(inode, mask);
+}
+
+/**
+ * path_permission - check for inode access rights depending on path
+ * @path: path of inode to check
+ * @parent_path: path of inode's parent
+ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+ *
+ * Like inode_permission, but used to check for permission when the
+ * file may potentially be copied up between union layers.
+ */
+
+int path_permission(struct path *path, struct path *parent_path, int mask)
+{
+	struct vfsmount *mnt;
+	int retval;
+
+	/* Catch some reversal of args */
+	BUG_ON(!S_ISDIR(parent_path->dentry->d_inode->i_mode));
+
+	if (IS_MNT_UNION(parent_path->mnt))
+		mnt = parent_path->mnt;
+	else
+		mnt = path->mnt;
+
+	retval = sb_permission(mnt->mnt_sb, path->dentry->d_inode, mask);
+	if (retval)
+		return retval;
+	return __inode_permission(path->dentry->d_inode, mask);
+}
+
+/**
  * file_permission  -  check for additional access rights to a given file
  * @file:	file to check access rights for
  * @mask:	right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3dfb0e2..fe16edf 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2140,6 +2140,7 @@ extern sector_t bmap(struct inode *, sector_t);
 #endif
 extern int notify_change(struct dentry *, struct iattr *);
 extern int inode_permission(struct inode *, int);
+extern int path_permission(struct path *, struct path *, int);
 extern int generic_permission(struct inode *, int,
 		int (*check_acl)(struct inode *, int));
 
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 62/74] VFS: Create user_path_nd() to lookup both parent and target
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (16 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 61/74] VFS: Split inode_permission() and create path_permission() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 63/74] union-mount: In-kernel file copyup routines Valerie Aurora
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Proof-of-concept implementation of user_path_nd().  Lookup both the
parent and the target of a user-supplied filename, to supply later to
union copyup routines.

XXX - Inefficient, racy, gets the parent of the symlink instead of the
parent of the target.  Al Viro would like to see something more like
this:

user_path_mumble() looks up and returns:

parent nameidata
positive topmost dentry of target
negative dentry of target from the topmost layer (if it doesn't exist on top)

Both the positive lower dentry and negative topmost dentry are passed
to the following code, like do_chown().  The tests for permissions and
such-like are performed on the positive lower dentry.  When it comes
time to actually modify the target, we call union_copyup() with both
positive and negative dentries (and the parent nameidata).

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c            |   31 +++++++++++++++++++++++++++++++
 include/linux/namei.h |    2 ++
 2 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index bf8d857..5eec62c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1581,6 +1581,37 @@ static int user_path_parent(int dfd, const char __user *path,
 	return error;
 }
 
+int user_path_nd(int dfd, const char __user *filename,
+			 unsigned flags, struct nameidata *parent_nd,
+			 struct path *child, char **tmp)
+{
+	struct nameidata child_nd;
+	char *s = getname(filename);
+	int error;
+
+	if (IS_ERR(s))
+		return PTR_ERR(s);
+
+	/* Lookup parent */
+	error = do_path_lookup(dfd, s, LOOKUP_PARENT, parent_nd);
+	if (error)
+		goto out_putname;
+
+	/* Lookup child - XXX optimize, racy */
+	error = do_path_lookup(dfd, s, flags, &child_nd);
+	if (error)
+		goto out_path_put;
+	*child = child_nd.path;
+	*tmp = s;
+	return 0;
+
+out_path_put:
+	path_put(&parent_nd->path);
+out_putname:
+	putname(s);
+	return error;
+}
+
 /*
  * It's inline, so penalty for filesystems that don't use sticky bit is
  * minimal.
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 05b441d..83dc8b5 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -58,6 +58,8 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_RENAME_TARGET	0x0800
 
 extern int user_path_at(int, const char __user *, unsigned, struct path *);
+extern int user_path_nd(int, const char __user *, unsigned,
+			struct nameidata *, struct path *, char **);
 
 #define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
 #define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path)
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 63/74] union-mount: In-kernel file copyup routines
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (17 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 62/74] VFS: Create user_path_nd() to lookup both parent and target Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 64/74] union-mount: Implement union-aware access()/faccessat() Valerie Aurora
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

When a file on the read-only layer of a union mount is altered, it
must be copied up to the topmost read-write layer.  This patch creates
union_copyup() and its supporting routines.

Thanks to Valdis Kletnieks <Valdis.Kletnieks@vt.edu> for a bug fix.

XXX - split up

XXX - If dir xattr copyup fails, delete the newly created dir

XXX - set correct owner after copyup

XXX - reimplement with get_unlinked_inode()

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/union.c |  326 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 fs/union.h |    6 +
 2 files changed, 330 insertions(+), 2 deletions(-)

diff --git a/fs/union.c b/fs/union.c
index 2fdc095..33d7a4c 100644
--- a/fs/union.c
+++ b/fs/union.c
@@ -24,6 +24,8 @@
 #include <linux/namei.h>
 #include <linux/file.h>
 #include <linux/security.h>
+#include <linux/splice.h>
+#include <linux/xattr.h>
 
 #include "union.h"
 
@@ -97,6 +99,72 @@ int union_add_dir(struct path *topmost, struct path *lower,
 }
 
 /**
+ * union_copyup_xattr
+ *
+ * @old: dentry of original file
+ * @new: dentry of new copy
+ *
+ * Copy up extended attributes from the original file to the new one.
+ *
+ * XXX - Permissions?  For now, copying up every xattr.
+ */
+
+static int union_copyup_xattr(struct dentry *old, struct dentry *new)
+{
+	ssize_t list_size, size;
+	char *buf, *name, *value;
+	int error;
+
+	/* Check for xattr support */
+	if (!old->d_inode->i_op->getxattr ||
+	    !new->d_inode->i_op->getxattr)
+		return 0;
+
+	/* Find out how big the list of xattrs is */
+	list_size = vfs_listxattr(old, NULL, 0);
+	if (list_size <= 0)
+		return list_size;
+
+	/* Allocate memory for the list */
+	buf = kzalloc(list_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* Allocate memory for the xattr's value */
+	error = -ENOMEM;
+	value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL);
+	if (!value)
+		goto out;
+
+	/* Actually get the list of xattrs */
+	list_size = vfs_listxattr(old, buf, list_size);
+	if (list_size <= 0) {
+		error = list_size;
+		goto out_free_value;
+	}
+
+	for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
+		/* XXX Locking? old is on read-only fs */
+		size = vfs_getxattr(old, name, value, XATTR_SIZE_MAX);
+		if (size <= 0) {
+			error = size;
+			goto out_free_value;
+		}
+		/* XXX do we really need to check for size overflow? */
+		/* XXX locks new dentry, lock ordering problems? */
+		error = vfs_setxattr(new, name, value, size, 0);
+		if (error)
+			goto out_free_value;
+	}
+
+out_free_value:
+	kfree(value);
+out:
+	kfree(buf);
+	return error;
+}
+
+/**
  * union_create_topmost_dir - Create a matching dir in the topmost file system
  *
  * @parent - parent of target on topmost layer
@@ -140,12 +208,18 @@ int union_create_topmost_dir(struct path *parent, struct qstr *name,
 
 	error = union_copyup_xattr(lower->dentry, topmost->dentry);
 	if (error)
-		dput(topmost->dentry);
+		goto out_rmdir;
 
 	fsnotify_mkdir(dir, topmost->dentry);
-out:
+
 	mnt_drop_write(parent->mnt);
 
+	return 0;
+out_rmdir:
+	/* XXX rm created dir */
+	dput(topmost->dentry);
+out:
+	mnt_drop_write(parent->mnt);
 	return error;
 }
 
@@ -390,3 +464,251 @@ generic_readdir_fallthru(struct dentry *topmost_dentry, const char *name,
 	}
 	return -ENOENT;
 }
+
+/**
+ * union_create_file
+ *
+ * @nd: namediata for source file
+ * @old: path of the source file
+ * @new: path of the new file, negative dentry
+ *
+ * Must already have mnt_want_write() on the mnt and the parent's
+ * i_mutex.
+ */
+
+static int union_create_file(struct nameidata *nd, struct path *old,
+			     struct dentry *new)
+{
+	struct path *parent = &nd->path;
+	BUG_ON(!mutex_is_locked(&parent->dentry->d_inode->i_mutex));
+
+	return vfs_create(parent->dentry->d_inode, new,
+			  old->dentry->d_inode->i_mode, nd);
+}
+
+/**
+ * union_create_symlink
+ *
+ * @nd: namediata for source symlink
+ * @old: path of the source symlink
+ * @new: path of the new symlink, negative dentry
+ *
+ * Must already have mnt_want_write() on the mnt and the parent's
+ * i_mutex.
+ */
+
+static int union_create_symlink(struct nameidata *nd, struct path *old,
+				struct dentry *new)
+{
+	void *cookie;
+	int error;
+
+	BUG_ON(!mutex_is_locked(&nd->path.dentry->d_inode->i_mutex));
+	/*
+	 * We want the contents of this symlink, not to follow it, so
+	 * this is modeled on generic_readlink() rather than
+	 * do_follow_link().
+	 */
+	nd->depth = 0;
+	cookie = old->dentry->d_inode->i_op->follow_link(old->dentry, nd);
+	if (IS_ERR(cookie))
+		return PTR_ERR(cookie);
+	/* Create a copy of the link on the top layer */
+	error = vfs_symlink(nd->path.dentry->d_inode, new,
+			    nd_get_link(nd));
+	if (old->dentry->d_inode->i_op->put_link)
+		old->dentry->d_inode->i_op->put_link(old->dentry, nd, cookie);
+	return error;
+}
+
+/**
+ * union_copyup_data - Copy up len bytes of old's data to new
+ *
+ * @old: path of source file
+ * @new_mnt: vfsmount of target file
+ * @new_dentry: dentry of target file
+ * @len: number of bytes to copy
+ */
+
+static int union_copyup_data(struct path *old, struct vfsmount *new_mnt,
+			     struct dentry *new_dentry, size_t len)
+{
+	struct file *old_file;
+	struct file *new_file;
+	const struct cred *cred = current_cred();
+	loff_t offset = 0;
+	long bytes;
+	int error = 0;
+
+	if (len == 0)
+		return 0;
+
+	/* Get reference to balance later fput() */
+	path_get(old);
+	old_file = dentry_open(old->dentry, old->mnt, O_RDONLY, cred);
+	if (IS_ERR(old_file))
+		return PTR_ERR(old_file);
+
+	mntget(new_mnt);
+	dget(new_dentry);
+	new_file = dentry_open(new_dentry, new_mnt, O_WRONLY, cred);
+	if (IS_ERR(new_file)) {
+		error = PTR_ERR(new_file);
+		goto out_fput;
+	}
+
+	bytes = do_splice_direct(old_file, &offset, new_file, len,
+				 SPLICE_F_MOVE);
+	if (bytes < 0)
+		error = bytes;
+
+	fput(new_file);
+out_fput:
+	fput(old_file);
+	return error;
+}
+
+/**
+ * __union_copyup_len - Copy up a file and len bytes of data
+ *
+ * @nd: nameidata for topmost parent dir
+ * @path: path of file to be copied up
+ * @len: number of bytes of file data to copy up
+ *
+ * Parent's i_mutex must be held by caller.  Newly copied up path is
+ * returned in @path and original is path_put().
+ */
+
+static int __union_copyup_len(struct nameidata *nd, struct path *path,
+			      size_t len)
+{
+	struct path *parent = &nd->path;
+	struct dentry *dentry;
+	int error;
+
+	BUG_ON(!mutex_is_locked(&parent->dentry->d_inode->i_mutex));
+
+	dentry = lookup_one_len(path->dentry->d_name.name, parent->dentry,
+				path->dentry->d_name.len);
+	if (IS_ERR(dentry))
+		return PTR_ERR(dentry);
+
+	if (dentry->d_inode) {
+		/*
+		 * We raced with someone else and "lost."  That's
+		 * okay, they did all the work of copying up the file.
+		 * Note that currently data copyup happens under the
+		 * parent dir's i_mutex.  If we move it outside that,
+		 * we'll need some way of waiting for the data copyup
+		 * to complete here.
+		 */
+		error = 0;
+		goto out_newpath;
+	}
+	if (S_ISREG(path->dentry->d_inode->i_mode)) {
+		/* Create file */
+		error = union_create_file(nd, path, dentry);
+		if (error)
+			goto out_dput;
+		/* Copyup data */
+		error = union_copyup_data(path, parent->mnt, dentry, len);
+	} else {
+		BUG_ON(!S_ISLNK(path->dentry->d_inode->i_mode));
+		error = union_create_symlink(nd, path, dentry);
+	}
+	if (error) {
+		/* Most likely error: ENOSPC */
+		vfs_unlink(parent->dentry->d_inode, dentry);
+		goto out_dput;
+	}
+	/* XXX Copyup xattrs and any other dangly bits */
+	error = union_copyup_xattr(path->dentry, dentry);
+	if (error)
+		goto out_dput;
+out_newpath:
+	/* path_put() of original must happen before we copy in new */
+	path_put(path);
+	path->dentry = dentry;
+	path->mnt = mntget(parent->mnt);
+	return error;
+out_dput:
+	/* Don't path_put(path), let caller unwind */
+	dput(dentry);
+	return error;
+}
+
+/**
+ * do_union_copyup_len - Copy up a file given its path (and its parent's)
+ *
+ * @nd: nameidata for topmost parent dir
+ * @path: path of file to be copied up
+ * @copy_all: if set, copy all of the file's data and ignore @len
+ * @len: if @copy_all is not set, number of bytes of file data to copy up
+ *
+ * Newly copied up path is returned in @path.
+ */
+
+static int do_union_copyup_len(struct nameidata *nd, struct path *path,
+			       int copy_all, size_t len)
+{
+	struct path *parent = &nd->path;
+	int error;
+
+	if (!IS_DIR_UNIONED(parent->dentry))
+		return 0;
+	if (parent->mnt == path->mnt)
+		return 0;
+	if (!S_ISREG(path->dentry->d_inode->i_mode) &&
+	    !S_ISLNK(path->dentry->d_inode->i_mode))
+		return 0;
+
+	BUG_ON(!S_ISDIR(parent->dentry->d_inode->i_mode));
+
+	mutex_lock(&parent->dentry->d_inode->i_mutex);
+	error = -ENOENT;
+	if (IS_DEADDIR(parent->dentry->d_inode))
+		goto out_unlock;
+
+	if (copy_all && S_ISREG(path->dentry->d_inode->i_mode)) {
+		error = -EFBIG;
+		len = i_size_read(path->dentry->d_inode);
+		/* Check for overflow of file size */
+		if (((size_t)len != len) || ((ssize_t)len != len))
+			goto out_unlock;
+	}
+
+	error = __union_copyup_len(nd, path, len);
+
+out_unlock:
+	mutex_unlock(&parent->dentry->d_inode->i_mutex);
+	return error;
+}
+
+/*
+ * Helper function to copy up all of a file
+ */
+int union_copyup(struct nameidata *nd, struct path *path)
+{
+	return do_union_copyup_len(nd, path, 1, 0);
+}
+
+/*
+ * Unlocked helper function to copy up all of a file
+ */
+int __union_copyup(struct nameidata *nd, struct path *path)
+{
+	size_t len;
+	len = i_size_read(path->dentry->d_inode);
+	if (((size_t)len != len) || ((ssize_t)len != len))
+		return -EFBIG;
+
+	return __union_copyup_len(nd, path, len);
+}
+
+/*
+ * Helper function to copy up part of a file
+ */
+int union_copyup_len(struct nameidata *nd, struct path *path, size_t len)
+{
+	return do_union_copyup_len(nd, path, 0, len);
+}
diff --git a/fs/union.h b/fs/union.h
index 4620cc1..0fcc302 100644
--- a/fs/union.h
+++ b/fs/union.h
@@ -59,6 +59,9 @@ extern int union_create_topmost_dir(struct path *, struct qstr *, struct path *,
 extern int union_copyup_dir(struct path *);
 extern int generic_readdir_fallthru(struct dentry *topmost_dentry, const char *name,
 				    int namlen, ino_t *ino, unsigned char *d_type);
+extern int union_copyup(struct nameidata *, struct path *);
+extern int __union_copyup(struct nameidata *, struct path *);
+extern int union_copyup_len(struct nameidata *, struct path *, size_t len);
 
 static inline int needs_lookup_union(struct path *parent_path, struct path *path)
 {
@@ -93,6 +96,9 @@ static inline struct path *union_find_dir(struct dentry *dentry,
 #define needs_lookup_union(x, y)	({ (0); })
 #define union_copyup_dir(x)		({ BUG(); (0); })
 #define generic_readdir_fallthru(w, x, y, z)	({ BUG(); (0); })
+#define union_copyup(x, y)		({ BUG(); (0); })
+#define __union_copyup(x, y)		({ BUG(); (0); })
+#define union_copyup_len(x, y, z)	({ BUG(); (0); })
 
 #endif	/* CONFIG_UNION_MOUNT */
 #endif	/* __KERNEL__ */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 64/74] union-mount: Implement union-aware access()/faccessat()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (18 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 63/74] union-mount: In-kernel file copyup routines Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 65/74] union-mount: Implement union-aware link() Valerie Aurora
                   ` (9 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

For union mounts, a file located on the lower layer will incorrectly
return EROFS on an access check.  To fix this, use the new
path_permission() call, which ignores a read-only lower layer file
system if the target will be copied up to the topmost file system.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/open.c |   21 +++++++++++++++++----
 1 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index d74e198..912f4dc 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -32,6 +32,7 @@
 #include <linux/dnotify.h>
 
 #include "internal.h"
+#include "union.h"
 
 int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
 	struct file *filp)
@@ -288,7 +289,10 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
 	const struct cred *old_cred;
 	struct cred *override_cred;
 	struct path path;
+	struct nameidata nd;
+	struct vfsmount *mnt;
 	struct inode *inode;
+	char *tmp;
 	int res;
 
 	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
@@ -312,10 +316,17 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
 
 	old_cred = override_creds(override_cred);
 
-	res = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
+	res = user_path_nd(dfd, filename, LOOKUP_FOLLOW,
+				   &nd, &path, &tmp);
 	if (res)
 		goto out;
 
+	/* For union mounts, use the topmost mnt's permissions */
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		mnt = nd.path.mnt;
+	else
+		mnt = path.mnt;
+
 	inode = path.dentry->d_inode;
 
 	if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
@@ -324,11 +335,11 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
 		 * with the "noexec" flag.
 		 */
 		res = -EACCES;
-		if (path.mnt->mnt_flags & MNT_NOEXEC)
+		if (mnt->mnt_flags & MNT_NOEXEC)
 			goto out_path_release;
 	}
 
-	res = inode_permission(inode, mode | MAY_ACCESS);
+	res = path_permission(&path, &nd.path, mode | MAY_ACCESS);
 	/* SuS v2 requires we report a read only fs too */
 	if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
 		goto out_path_release;
@@ -342,11 +353,13 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
 	 * inherently racy and know that the fs may change
 	 * state before we even see this result.
 	 */
-	if (__mnt_is_readonly(path.mnt))
+	if (__mnt_is_readonly(mnt))
 		res = -EROFS;
 
 out_path_release:
 	path_put(&path);
+	path_put(&nd.path);
+	putname(tmp);
 out:
 	revert_creds(old_cred);
 	put_cred(override_cred);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 65/74] union-mount: Implement union-aware link()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (19 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 64/74] union-mount: Implement union-aware access()/faccessat() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 66/74] union-mount: Implement union-aware rename() Valerie Aurora
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>


Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   24 ++++++++++++++++++++----
 1 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 5eec62c..6ea9cdc 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2976,16 +2976,18 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
 {
 	struct dentry *new_dentry;
 	struct nameidata nd;
+	struct nameidata old_nd;
 	struct path old_path;
 	int error;
 	char *to;
+	char *from;
 
 	if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
 		return -EINVAL;
 
-	error = user_path_at(olddfd, oldname,
+	error = user_path_nd(olddfd, oldname,
 			     flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
-			     &old_path);
+			     &old_nd, &old_path, &from);
 	if (error)
 		return error;
 
@@ -2993,8 +2995,20 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
 	if (error)
 		goto out;
 	error = -EXDEV;
-	if (old_path.mnt != nd.path.mnt)
-		goto out_release;
+	if (old_path.mnt != nd.path.mnt) {
+		if (IS_DIR_UNIONED(old_nd.path.dentry) &&
+		    (old_nd.path.mnt == nd.path.mnt)) {
+			error = mnt_want_write(old_nd.path.mnt);
+			if (error)
+				goto out_release;
+			error = union_copyup(&old_nd, &old_path);
+			mnt_drop_write(old_nd.path.mnt);
+			if (error)
+				goto out_release;
+		} else {
+			goto out_release;
+		}
+	}
 	new_dentry = lookup_create(&nd, 0);
 	error = PTR_ERR(new_dentry);
 	if (IS_ERR(new_dentry))
@@ -3017,6 +3031,8 @@ out_release:
 	putname(to);
 out:
 	path_put(&old_path);
+	path_put(&old_nd.path);
+	putname(from);
 
 	return error;
 }
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 66/74] union-mount: Implement union-aware rename()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (20 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 65/74] union-mount: Implement union-aware link() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 67/74] union-mount: Implement union-aware writable open() Valerie Aurora
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

On rename() of a file on union mount, copyup and whiteout the source
file.

XXX - fix comments and make more readable

XXX - Convert newly empty unioned dirs to not-unioned

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 6ea9cdc..6c7cff3 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3193,6 +3193,7 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 {
 	struct dentry *old_dir, *new_dir;
 	struct path old, new;
+	struct path to_whiteout = {NULL, NULL};
 	struct dentry *trap;
 	struct nameidata oldnd, newnd;
 	char *from;
@@ -3210,13 +3211,6 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 	error = -EXDEV;
 	if (oldnd.path.mnt != newnd.path.mnt)
 		goto exit2;
-
-	/* rename() on union mounts not implemented yet */
-	error = -EXDEV;
-	if (IS_DIR_UNIONED(oldnd.path.dentry) ||
-	    IS_DIR_UNIONED(newnd.path.dentry))
-		goto exit2;
-
 	old_dir = oldnd.path.dentry;
 	error = -EBUSY;
 	if (oldnd.last_type != LAST_NORM)
@@ -3251,6 +3245,11 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 	error = -EINVAL;
 	if (old.dentry == trap)
 		goto exit4;
+	error = -EXDEV;
+	/* Can't rename a directory from a lower layer */
+	if (IS_DIR_UNIONED(oldnd.path.dentry) &&
+	    IS_DIR_UNIONED(old.dentry))
+		goto exit4;
 	error = lookup_hash(&newnd, &newnd.last, &new);
 	if (error)
 		goto exit4;
@@ -3258,6 +3257,48 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 	error = -ENOTEMPTY;
 	if (new.dentry == trap)
 		goto exit5;
+	error = -EXDEV;
+	/* Can't rename over directories on the lower layer */
+	if (IS_DIR_UNIONED(newnd.path.dentry) &&
+	    IS_DIR_UNIONED(new.dentry))
+		goto exit5;
+
+	/* If source is on lower layer, copy up */
+	if (IS_DIR_UNIONED(oldnd.path.dentry) &&
+	    (old.mnt != oldnd.path.mnt)) {
+		/* Save the lower path to avoid a second lookup for whiteout */
+		to_whiteout.mnt = mntget(old.mnt);
+		to_whiteout.dentry = dget(old.dentry);
+		error = __union_copyup(&oldnd, &old);
+		if (error)
+			goto exit5;
+	}
+
+	/* If target is on lower layer, get negative dentry for topmost */
+	if (IS_DIR_UNIONED(newnd.path.dentry) &&
+	    (new.mnt != newnd.path.mnt)) {
+		struct dentry *dentry;
+		/*
+		 * At this point, source and target are both files,
+		 * the source is on the topmost layer, and the target
+		 * is on a lower layer.  We want the target dentry to
+		 * disappear from the namespace, and give vfs_rename a
+		 * negative dentry from the topmost layer.
+		 */
+		/* We already did lookup once, no need to check perm */
+		dentry = __lookup_hash(&newnd.last, newnd.path.dentry, &newnd);
+		if (IS_ERR(dentry)) {
+			error = PTR_ERR(dentry);
+			goto exit5;
+		}
+		/* We no longer need the lower target dentry.  It
+		 * definitely should be removed from the hash table */
+		/* XXX what about failure case? */
+		d_delete(new.dentry);
+		mntput(new.mnt);
+		new.mnt = mntget(newnd.path.mnt);
+		new.dentry = dentry;
+	}
 
 	error = mnt_want_write(oldnd.path.mnt);
 	if (error)
@@ -3268,6 +3309,26 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
 		goto exit6;
 	error = vfs_rename(old_dir->d_inode, old.dentry,
 				   new_dir->d_inode, new.dentry);
+	if (error)
+		goto exit6;
+	/* Now whiteout the source */
+	if (IS_DIR_UNIONED(oldnd.path.dentry)) {
+		if (!to_whiteout.dentry) {
+			struct dentry *dentry;
+			/* We could have exposed a lower level entry */
+			dentry = __lookup_hash(&oldnd.last, oldnd.path.dentry, &oldnd);
+			if (IS_ERR(dentry)) {
+				error = PTR_ERR(dentry);
+				goto exit6;
+			}
+			to_whiteout.dentry = dentry;
+			to_whiteout.mnt = mntget(oldnd.path.mnt);
+		}
+
+		if (to_whiteout.dentry->d_inode)
+			error = do_whiteout(&oldnd, &to_whiteout, 0);
+		path_put(&to_whiteout);
+	}
 exit6:
 	mnt_drop_write(oldnd.path.mnt);
 exit5:
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 67/74] union-mount: Implement union-aware writable open()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (21 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 66/74] union-mount: Implement union-aware rename() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:04 ` [PATCH 68/74] union-mount: Implement union-aware chown() Valerie Aurora
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>

Copy up a file when opened with write permissions.  Does not copy up
the file data when O_TRUNC is specified.

Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/namei.c |   28 ++++++++++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 6c7cff3..7e17d13 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1945,6 +1945,24 @@ exit:
 	return ERR_PTR(error);
 }
 
+static int open_union_copyup(struct nameidata *nd, struct path *path,
+			     int open_flag)
+{
+	struct vfsmount *oldmnt = path->mnt;
+	int error;
+
+	if (open_flag & O_TRUNC)
+		error = union_copyup_len(nd, path, 0);
+	else
+		error = union_copyup(nd, path);
+	if (error)
+		return error;
+	if (oldmnt != path->mnt)
+		mntput(nd->path.mnt);
+
+	return error;
+}
+
 static struct file *do_last(struct nameidata *nd, struct path *path,
 			    int open_flag, int acc_mode,
 			    int mode, const char *pathname)
@@ -1996,6 +2014,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
 			if (!path->dentry->d_inode->i_op->lookup)
 				goto exit_dput;
 		}
+		if (acc_mode & MAY_WRITE) {
+			error = open_union_copyup(nd, path, open_flag);
+			if (error)
+				goto exit_dput;
+		}
 		path_to_nameidata(path, nd);
 		audit_inode(pathname, nd->path.dentry);
 		goto ok;
@@ -2067,6 +2090,11 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
 	if (path->dentry->d_inode->i_op->follow_link)
 		return NULL;
 
+	if (acc_mode & MAY_WRITE) {
+		error = open_union_copyup(nd, path, open_flag);
+		if (error)
+			goto exit_dput;
+	}
 	path_to_nameidata(path, nd);
 	error = -EISDIR;
 	if (S_ISDIR(path->dentry->d_inode->i_mode))
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 68/74] union-mount: Implement union-aware chown()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (22 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 67/74] union-mount: Implement union-aware writable open() Valerie Aurora
@ 2011-03-23  2:04 ` Valerie Aurora
  2011-03-23  2:05 ` [PATCH 69/74] union-mount: Implement union-aware truncate() Valerie Aurora
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:04 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>


Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/open.c |   23 ++++++++++++++++++++---
 1 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index 912f4dc..2b12488 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -552,18 +552,35 @@ static int chown_common(struct path *path, uid_t user, gid_t group)
 SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group)
 {
 	struct path path;
+	struct nameidata nd;
+	struct vfsmount *mnt;
+	char *tmp;
 	int error;
 
-	error = user_path(filename, &path);
+	error = user_path_nd(AT_FDCWD, filename, LOOKUP_FOLLOW,
+				     &nd, &path, &tmp);
 	if (error)
 		goto out;
-	error = mnt_want_write(path.mnt);
+
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		mnt = nd.path.mnt;
+	else
+		mnt = path.mnt;
+
+	error = mnt_want_write(mnt);
 	if (error)
 		goto out_release;
+
+	error = union_copyup(&nd, &path);
+	if (error)
+		goto out_drop_write;
 	error = chown_common(&path, user, group);
-	mnt_drop_write(path.mnt);
+out_drop_write:
+	mnt_drop_write(mnt);
 out_release:
 	path_put(&path);
+	path_put(&nd.path);
+	putname(tmp);
 out:
 	return error;
 }
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 69/74] union-mount: Implement union-aware truncate()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (23 preceding siblings ...)
  2011-03-23  2:04 ` [PATCH 68/74] union-mount: Implement union-aware chown() Valerie Aurora
@ 2011-03-23  2:05 ` Valerie Aurora
  2011-03-23  2:05 ` [PATCH 70/74] union-mount: Implement union-aware chmod()/fchmodat() Valerie Aurora
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:05 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>


Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/open.c |   24 ++++++++++++++++++++----
 1 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index 2b12488..23f4b75 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -65,14 +65,17 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
 static long do_sys_truncate(const char __user *pathname, loff_t length)
 {
 	struct path path;
+	struct nameidata nd;
+	struct vfsmount *mnt;
 	struct inode *inode;
+	char *tmp;
 	int error;
 
 	error = -EINVAL;
 	if (length < 0)	/* sorry, but loff_t says... */
 		goto out;
 
-	error = user_path(pathname, &path);
+	error = user_path_nd(AT_FDCWD, pathname, 0, &nd, &path, &tmp);
 	if (error)
 		goto out;
 	inode = path.dentry->d_inode;
@@ -86,11 +89,16 @@ static long do_sys_truncate(const char __user *pathname, loff_t length)
 	if (!S_ISREG(inode->i_mode))
 		goto dput_and_out;
 
-	error = mnt_want_write(path.mnt);
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		mnt = nd.path.mnt;
+	else
+		mnt = path.mnt;
+
+	error = mnt_want_write(mnt);
 	if (error)
 		goto dput_and_out;
 
-	error = inode_permission(inode, MAY_WRITE);
+	error = path_permission(&path, &nd.path, MAY_WRITE);
 	if (error)
 		goto mnt_drop_write_and_out;
 
@@ -98,6 +106,12 @@ static long do_sys_truncate(const char __user *pathname, loff_t length)
 	if (IS_APPEND(inode))
 		goto mnt_drop_write_and_out;
 
+	error = union_copyup_len(&nd, &path, length);
+	if (error)
+		goto mnt_drop_write_and_out;
+
+	/* path may have changed after copyup */
+	inode = path.dentry->d_inode;
 	error = get_write_access(inode);
 	if (error)
 		goto mnt_drop_write_and_out;
@@ -119,9 +133,11 @@ static long do_sys_truncate(const char __user *pathname, loff_t length)
 put_write_and_out:
 	put_write_access(inode);
 mnt_drop_write_and_out:
-	mnt_drop_write(path.mnt);
+	mnt_drop_write(mnt);
 dput_and_out:
 	path_put(&path);
+	path_put(&nd.path);
+	putname(tmp);
 out:
 	return error;
 }
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 70/74] union-mount: Implement union-aware chmod()/fchmodat()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (24 preceding siblings ...)
  2011-03-23  2:05 ` [PATCH 69/74] union-mount: Implement union-aware truncate() Valerie Aurora
@ 2011-03-23  2:05 ` Valerie Aurora
  2011-03-23  2:05 ` [PATCH 71/74] union-mount: Implement union-aware lchown() Valerie Aurora
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:05 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>


Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/open.c |   25 +++++++++++++++++++++----
 1 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index 23f4b75..4f17626 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -503,18 +503,32 @@ out:
 SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, mode_t, mode)
 {
 	struct path path;
+	struct nameidata nd;
+	struct vfsmount *mnt;
 	struct inode *inode;
+	char *tmp;
 	int error;
 	struct iattr newattrs;
 
-	error = user_path_at(dfd, filename, LOOKUP_FOLLOW, &path);
+	error = user_path_nd(dfd, filename, LOOKUP_FOLLOW, &nd,
+				     &path, &tmp);
 	if (error)
 		goto out;
-	inode = path.dentry->d_inode;
 
-	error = mnt_want_write(path.mnt);
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		mnt = nd.path.mnt;
+	else
+		mnt = path.mnt;
+
+	error = mnt_want_write(mnt);
 	if (error)
 		goto dput_and_out;
+
+	error = union_copyup(&nd, &path);
+	if (error)
+		goto mnt_drop_write_and_out;
+
+	inode = path.dentry->d_inode;
 	mutex_lock(&inode->i_mutex);
 	error = security_path_chmod(path.dentry, path.mnt, mode);
 	if (error)
@@ -526,9 +540,12 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, mode_t, mode)
 	error = notify_change(path.dentry, &newattrs);
 out_unlock:
 	mutex_unlock(&inode->i_mutex);
-	mnt_drop_write(path.mnt);
+mnt_drop_write_and_out:
+	mnt_drop_write(mnt);
 dput_and_out:
 	path_put(&path);
+	path_put(&nd.path);
+	putname(tmp);
 out:
 	return error;
 }
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 71/74] union-mount: Implement union-aware lchown()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (25 preceding siblings ...)
  2011-03-23  2:05 ` [PATCH 70/74] union-mount: Implement union-aware chmod()/fchmodat() Valerie Aurora
@ 2011-03-23  2:05 ` Valerie Aurora
  2011-03-23  2:05 ` [PATCH 72/74] union-mount: Implement union-aware utimensat() Valerie Aurora
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:05 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>


Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/open.c |   23 ++++++++++++++++++++---
 1 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index 4f17626..9f8251a 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -646,18 +646,35 @@ out:
 SYSCALL_DEFINE3(lchown, const char __user *, filename, uid_t, user, gid_t, group)
 {
 	struct path path;
+	struct nameidata nd;
+	struct vfsmount *mnt;
+	char *tmp;
 	int error;
 
-	error = user_lpath(filename, &path);
+	error = user_path_nd(AT_FDCWD, filename, 0, &nd, &path, &tmp);
 	if (error)
 		goto out;
-	error = mnt_want_write(path.mnt);
+
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		mnt = nd.path.mnt;
+	else
+		mnt = path.mnt;
+
+	error = mnt_want_write(mnt);
 	if (error)
 		goto out_release;
+
+	error = union_copyup(&nd, &path);
+	if (error)
+		goto out_drop_write;
+
 	error = chown_common(&path, user, group);
-	mnt_drop_write(path.mnt);
+out_drop_write:
+	mnt_drop_write(mnt);
 out_release:
 	path_put(&path);
+	path_put(&nd.path);
+	putname(tmp);
 out:
 	return error;
 }
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 72/74] union-mount: Implement union-aware utimensat()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (26 preceding siblings ...)
  2011-03-23  2:05 ` [PATCH 71/74] union-mount: Implement union-aware lchown() Valerie Aurora
@ 2011-03-23  2:05 ` Valerie Aurora
  2011-03-23  2:05 ` [PATCH 73/74] union-mount: Implement union-aware setxattr() Valerie Aurora
  2011-03-23  2:05 ` [PATCH 74/74] union-mount: Implement union-aware lsetxattr() Valerie Aurora
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:05 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>


Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/utimes.c |   14 ++++++++++++--
 1 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/fs/utimes.c b/fs/utimes.c
index 179b586..da6c2ff 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -8,8 +8,10 @@
 #include <linux/stat.h>
 #include <linux/utime.h>
 #include <linux/syscalls.h>
+#include <linux/slab.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
+#include "union.h"
 
 #ifdef __ARCH_WANT_SYS_UTIME
 
@@ -153,18 +155,26 @@ long do_utimes(int dfd, const char __user *filename, struct timespec *times,
 		error = utimes_common(&file->f_path, times);
 		fput(file);
 	} else {
+		struct nameidata nd;
+		char *tmp;
 		struct path path;
 		int lookup_flags = 0;
 
 		if (!(flags & AT_SYMLINK_NOFOLLOW))
 			lookup_flags |= LOOKUP_FOLLOW;
 
-		error = user_path_at(dfd, filename, lookup_flags, &path);
+		error = user_path_nd(dfd, filename, lookup_flags, &nd, &path,
+				     &tmp);
 		if (error)
 			goto out;
 
-		error = utimes_common(&path, times);
+		error = union_copyup(&nd, &path);
+
+		if (!error)
+			error = utimes_common(&path, times);
 		path_put(&path);
+		path_put(&nd.path);
+		putname(tmp);
 	}
 
 out:
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 73/74] union-mount: Implement union-aware setxattr()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (27 preceding siblings ...)
  2011-03-23  2:05 ` [PATCH 72/74] union-mount: Implement union-aware utimensat() Valerie Aurora
@ 2011-03-23  2:05 ` Valerie Aurora
  2011-03-23  2:05 ` [PATCH 74/74] union-mount: Implement union-aware lsetxattr() Valerie Aurora
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:05 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>


Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/xattr.c |   34 +++++++++++++++++++++++++++-------
 1 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 01bb813..7869788 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -19,7 +19,7 @@
 #include <linux/fsnotify.h>
 #include <linux/audit.h>
 #include <asm/uaccess.h>
-
+#include "union.h"
 
 /*
  * Check permissions for extended attribute access.  This is a bit complicated
@@ -281,17 +281,37 @@ SYSCALL_DEFINE5(setxattr, const char __user *, pathname,
 		size_t, size, int, flags)
 {
 	struct path path;
+	struct nameidata nd;
+	struct vfsmount *mnt;
+	char *tmp;
 	int error;
 
-	error = user_path(pathname, &path);
+	error = user_path_nd(AT_FDCWD, pathname, LOOKUP_FOLLOW, &nd, &path,
+			     &tmp);
 	if (error)
 		return error;
-	error = mnt_want_write(path.mnt);
-	if (!error) {
-		error = setxattr(path.dentry, name, value, size, flags);
-		mnt_drop_write(path.mnt);
-	}
+
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		mnt = nd.path.mnt;
+	else
+		mnt = path.mnt;
+
+	error = mnt_want_write(mnt);
+	if (error)
+		goto out;
+
+	error = union_copyup(&nd, &path);
+	if (error)
+		goto out_drop_write;
+
+	error = setxattr(path.dentry, name, value, size, flags);
+
+out_drop_write:
+	mnt_drop_write(mnt);
+out:
 	path_put(&path);
+	path_put(&nd.path);
+	putname(tmp);
 	return error;
 }
 
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 74/74] union-mount: Implement union-aware lsetxattr()
  2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
                   ` (28 preceding siblings ...)
  2011-03-23  2:05 ` [PATCH 73/74] union-mount: Implement union-aware setxattr() Valerie Aurora
@ 2011-03-23  2:05 ` Valerie Aurora
  29 siblings, 0 replies; 32+ messages in thread
From: Valerie Aurora @ 2011-03-23  2:05 UTC (permalink / raw)
  To: linux-fsdevel, linux; +Cc: viro, Valerie Aurora, Valerie Aurora

From: Valerie Aurora <vaurora@redhat.com>


Signed-off-by: Valerie Aurora <valerie.aurora@gmail.com>
---
 fs/xattr.c |   31 +++++++++++++++++++++++++------
 1 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/fs/xattr.c b/fs/xattr.c
index 7869788..67815eb 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -320,17 +320,36 @@ SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname,
 		size_t, size, int, flags)
 {
 	struct path path;
+	struct nameidata nd;
+	struct vfsmount *mnt;
+	char *tmp;
 	int error;
 
-	error = user_lpath(pathname, &path);
+	error = user_path_nd(AT_FDCWD, pathname, 0, &nd, &path, &tmp);
 	if (error)
 		return error;
-	error = mnt_want_write(path.mnt);
-	if (!error) {
-		error = setxattr(path.dentry, name, value, size, flags);
-		mnt_drop_write(path.mnt);
-	}
+
+	if (IS_DIR_UNIONED(nd.path.dentry))
+		mnt = nd.path.mnt;
+	else
+		mnt = path.mnt;
+
+	error = mnt_want_write(mnt);
+	if (error)
+		goto out;
+
+	error = union_copyup(&nd, &path);
+	if (error)
+		goto out_drop_write;
+
+	error = setxattr(path.dentry, name, value, size, flags);
+
+out_drop_write:
+	mnt_drop_write(mnt);
+out:
 	path_put(&path);
+	path_put(&nd.path);
+	putname(tmp);
 	return error;
 }
 
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2011-03-23  2:06 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-03-23  2:04 [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora
2011-03-23  2:04 ` [PATCH 45/74] union-mount: Basic infrastructure of __lookup_union() Valerie Aurora
2011-03-23  2:04 ` [PATCH 46/74] union-mount: Process negative dentries in __lookup_union() Valerie Aurora
2011-03-23  2:04 ` [PATCH 47/74] union-mount: Return files found in lower layers " Valerie Aurora
2011-03-23  2:04 ` [PATCH 48/74] union-mount: Build union stack " Valerie Aurora
2011-03-23  2:04 ` [PATCH 49/74] union-mount: Follow mount " Valerie Aurora
2011-03-23  2:04 ` [PATCH 50/74] union-mount: Add lookup_union() Valerie Aurora
2011-03-23  2:04 ` [PATCH 51/74] union-mount: Add do_lookup_union() wrapper for __lookup_union() Valerie Aurora
2011-03-23  2:04 ` [PATCH 52/74] union-mount: Call union lookup functions in lookup path Valerie Aurora
2011-03-23  2:04 ` [PATCH 53/74] union-mount: Create whiteout on unlink() Valerie Aurora
2011-03-23  2:04 ` [PATCH 54/74] union-mount: Create whiteout on rmdir() Valerie Aurora
2011-03-23  2:04 ` [PATCH 55/74] union-mount: Set opaque flag on new directories in unioned file systems Valerie Aurora
2011-03-23  2:04 ` [PATCH 56/74] union-mount: Copy up directory entries on first readdir() Valerie Aurora
2011-03-23  2:04 ` [PATCH 57/74] union-mount: Add generic_readdir_fallthru() helper Valerie Aurora
2011-03-23  2:04 ` [PATCH 58/74] fallthru: ext2 support for lookup of d_type/d_ino in fallthrus Valerie Aurora
2011-03-23  2:04 ` [PATCH 59/74] fallthru: tmpfs " Valerie Aurora
2011-03-23  2:04 ` [PATCH 60/74] fallthru: jffs2 " Valerie Aurora
2011-03-23  2:04 ` [PATCH 61/74] VFS: Split inode_permission() and create path_permission() Valerie Aurora
2011-03-23  2:04 ` [PATCH 62/74] VFS: Create user_path_nd() to lookup both parent and target Valerie Aurora
2011-03-23  2:04 ` [PATCH 63/74] union-mount: In-kernel file copyup routines Valerie Aurora
2011-03-23  2:04 ` [PATCH 64/74] union-mount: Implement union-aware access()/faccessat() Valerie Aurora
2011-03-23  2:04 ` [PATCH 65/74] union-mount: Implement union-aware link() Valerie Aurora
2011-03-23  2:04 ` [PATCH 66/74] union-mount: Implement union-aware rename() Valerie Aurora
2011-03-23  2:04 ` [PATCH 67/74] union-mount: Implement union-aware writable open() Valerie Aurora
2011-03-23  2:04 ` [PATCH 68/74] union-mount: Implement union-aware chown() Valerie Aurora
2011-03-23  2:05 ` [PATCH 69/74] union-mount: Implement union-aware truncate() Valerie Aurora
2011-03-23  2:05 ` [PATCH 70/74] union-mount: Implement union-aware chmod()/fchmodat() Valerie Aurora
2011-03-23  2:05 ` [PATCH 71/74] union-mount: Implement union-aware lchown() Valerie Aurora
2011-03-23  2:05 ` [PATCH 72/74] union-mount: Implement union-aware utimensat() Valerie Aurora
2011-03-23  2:05 ` [PATCH 73/74] union-mount: Implement union-aware setxattr() Valerie Aurora
2011-03-23  2:05 ` [PATCH 74/74] union-mount: Implement union-aware lsetxattr() Valerie Aurora
  -- strict thread matches above, loose matches on Subject: below --
2011-03-23  1:58 [PATCH 00/74] Union mounts version something or other Valerie Aurora
2011-03-23  1:59 ` [PATCH 44/74] union-mount: Temporarily disable some syscalls Valerie Aurora

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).