From: Valerie Aurora <vaurora@redhat.com>
To: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Miklos Szeredi <miklos@szeredi.hu>, Jan Blunck <jblunck@suse.de>,
Christoph Hellwig <hch@infradead.org>,
linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
Valerie Aurora <vaurora@redhat.com>
Subject: [PATCH 22/38] union-mount: Implement union lookup
Date: Fri, 6 Aug 2010 15:35:08 -0700 [thread overview]
Message-ID: <1281134124-17041-23-git-send-email-vaurora@redhat.com> (raw)
In-Reply-To: <1281134124-17041-1-git-send-email-vaurora@redhat.com>
Implement unioned directories, whiteouts, and fallthrus in pathname
lookup routines. do_lookup() and lookup_hash() call lookup_union()
after looking up the dentry from the top-level file system.
lookup_union() is centered around __lookup_hash(), which does cached
and/or real lookups and revalidates each dentry in the union stack.
XXX - implement negative union cache entries
XXX - handle different permissions on directories
Signed-off-by: Valerie Aurora <vaurora@redhat.com>
---
fs/namei.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/union.c | 94 ++++++++++++++++++++++++++++++++
fs/union.h | 7 +++
3 files changed, 274 insertions(+), 1 deletions(-)
diff --git a/fs/namei.c b/fs/namei.c
index 0b6378e..0821544 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)
@@ -723,6 +724,163 @@ 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 - Given a path from the topmost layer, lookup and
+ * revalidate each dentry in its union stack, building it if necessary
+ *
+ * @nd - nameidata for the parent of @topmost
+ * @name - pathname from this element on
+ * @topmost - path of the topmost matching dentry
+ *
+ * Given the nameidata and the path of the topmost dentry for this
+ * pathname, lookup, revalidate, and build the associated union stack.
+ * @topmost must be either a negative dentry or a directory, and not a
+ * whiteout.
+ *
+ * This function may stomp nd->path with the path of the parent
+ * directory of lower layer, so the caller must save nd->path and
+ * restore it afterwards. You probably want to use lookup_union(),
+ * not __lookup_union().
+ */
+
+static int __lookup_union(struct nameidata *nd, struct qstr *name,
+ struct path *topmost)
+{
+ struct path parent = nd->path;
+ struct path lower, upper;
+ struct union_dir *ud;
+ /* next_ud is the head of the list of union dirs for this dentry */
+ struct union_dir **next_ud = &topmost->dentry->d_union_dir;
+ int err = 0;
+
+ /*
+ * upper is either a negative dentry from the top layer, or it
+ * is the most recent positive dentry for a directory that
+ * we've seen.
+ */
+ upper = *topmost;
+
+ /* Go through each dir underlying the parent, looking for a match */
+ for (ud = nd->path.dentry->d_union_dir; ud != NULL; ud = ud->u_lower) {
+ BUG_ON(ud->u_this.dentry->d_count.counter == 0);
+ /* Change the nameidata to point to this level's dir */
+ nd->path = ud->u_this;
+ /* Lookup the child in this level */
+ 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;
+ }
+
+ if (!lower.dentry->d_inode) {
+ if (d_is_whiteout(lower.dentry))
+ break;
+ if (IS_OPAQUE(nd->path.dentry->d_inode) &&
+ !d_is_fallthru(lower.dentry))
+ break;
+ /* Plain old negative! Keep looking */
+ path_put(&lower);
+ continue;
+ }
+
+ /* Finding a non-dir ends the lookup, one way or another */
+ if (!S_ISDIR(lower.dentry->d_inode->i_mode)) {
+ /* Ignore file below dir - invalid */
+ if (upper.dentry->d_inode &&
+ S_ISDIR(upper.dentry->d_inode->i_mode)) {
+ path_put(&lower);
+ break;
+ }
+ /* Bingo, found our target */
+ dput(topmost->dentry);
+ /* mntput(topmost) done in link_path_walk() */
+ *topmost = lower;
+ break;
+ }
+
+ /* Allow read-only submounts on lower layers */
+ follow_mount(&lower);
+
+ /* Found a directory. Create the topmost version if it doesn't exist */
+ if (!topmost->dentry->d_inode) {
+ err = union_create_topmost_dir(&parent, name, topmost,
+ &lower);
+ if (err) {
+ path_put(&lower);
+ return err;
+ }
+ }
+
+ err = union_add_dir(&upper, &lower, next_ud);
+ if (err)
+ break;
+
+ next_ud = &(*next_ud)->u_lower;
+ upper = lower;
+ }
+out:
+ return 0;
+}
+
+/*
+ * lookup_union - revalidate and build union stack for this path
+ *
+ * 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.
+ */
+static int lookup_union(struct nameidata *nd, struct qstr *name,
+ struct path *topmost)
+{
+ struct path saved_path;
+ int err;
+
+ 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));
+
+ saved_path = nd->path;
+ path_get(&saved_path);
+
+ err = __lookup_union(nd, name, topmost);
+
+ nd->path = saved_path;
+ path_put(&saved_path);
+
+ return err;
+}
+
+/*
+ * 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;
+}
+
/*
* It's more convoluted than I'd like it to be, but... it's still fairly
* small and for now I'd prefer to have fast path as straight as possible.
@@ -753,6 +911,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:
@@ -1224,8 +1387,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,
@@ -2889,7 +3057,11 @@ 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 */
+ /* XXX much harsher check than necessary - can do some renames */
+ 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)
diff --git a/fs/union.c b/fs/union.c
index 02abb7c..c089c02 100644
--- a/fs/union.c
+++ b/fs/union.c
@@ -21,6 +21,7 @@
#include <linux/mount.h>
#include <linux/fs_struct.h>
#include <linux/slab.h>
+#include <linux/namei.h>
#include "union.h"
@@ -117,3 +118,96 @@ void d_free_unions(struct dentry *dentry)
}
dentry->d_union_dir = NULL;
}
+
+/**
+ * needs_lookup_union - Avoid union lookup when not necessary
+ *
+ * @parent_path: path of the parent directory
+ * @path: path of the lookup target
+ *
+ * Check to see if the target needs union lookup. Two cases need
+ * union lookup: the target is a directory, and the target is a
+ * negative dentry.
+ *
+ * Returns 0 if this dentry is definitely not unioned. Returns 1 if
+ * it is possible this dentry is unioned.
+ */
+
+int needs_lookup_union(struct path *parent_path, struct path *path)
+{
+ /*
+ * If the target is the root of the mount, then its union
+ * stack was already created at mount time (if this is a union
+ * mount).
+ */
+ if (IS_ROOT(path->dentry))
+ return 0;
+
+ /* Only dentries in a unioned directory need a union lookup. */
+ if (!IS_DIR_UNIONED(parent_path->dentry))
+ return 0;
+
+ /* Whiteouts cover up everything below */
+ if (d_is_whiteout(path->dentry))
+ return 0;
+
+ /* Opaque dirs cover except if this is a fallthru */
+ if (IS_OPAQUE(parent_path->dentry->d_inode) &&
+ !d_is_fallthru(path->dentry))
+ return 0;
+
+ /*
+ * XXX Negative dentries in unioned directories must always go
+ * through a full union lookup because there might be a
+ * matching entry below it. To improve performance, we should
+ * mark negative dentries in some way to show they have
+ * already been looked up in the union and nothing was found.
+ * Maybe mark it opaque?
+ */
+ if (!path->dentry->d_inode)
+ return 1;
+
+ /*
+ * If it's not a directory and it's a positive dentry, then we
+ * already have the topmost dentry and we don't need to do any
+ * lookup in lower layers.
+ */
+
+ if (!S_ISDIR(path->dentry->d_inode->i_mode))
+ return 0;
+
+ /* Is the union stack already constructed? */
+ if (IS_DIR_UNIONED(path->dentry))
+ return 0;
+
+ /*
+ * XXX This is like the negative dentry case. This directory
+ * may have no matching directories in the lower layers, or
+ * this may just be the first time we looked it up. We can't
+ * tell the difference.
+ */
+ return 1;
+}
+
+/*
+ * union_create_topmost_dir - Create a matching dir in the topmost file system
+ */
+
+int union_create_topmost_dir(struct path *parent, struct qstr *name,
+ struct path *topmost, struct path *lower)
+{
+ int mode = lower->dentry->d_inode->i_mode;
+ int res;
+
+ BUG_ON(topmost->dentry->d_inode);
+
+ res = mnt_want_write(parent->mnt);
+ if (res)
+ return res;
+
+ res = vfs_mkdir(parent->dentry->d_inode, topmost->dentry, mode);
+
+ mnt_drop_write(parent->mnt);
+
+ return res;
+}
diff --git a/fs/union.h b/fs/union.h
index 04efc1f..505f132 100644
--- a/fs/union.h
+++ b/fs/union.h
@@ -51,15 +51,22 @@ struct union_dir {
};
#define IS_MNT_UNION(mnt) ((mnt)->mnt_flags & MNT_UNION)
+#define IS_DIR_UNIONED(dentry) ((dentry)->d_union_dir)
extern int union_add_dir(struct path *, struct path *, struct union_dir **);
extern void d_free_unions(struct dentry *);
+int needs_lookup_union(struct path *, struct path *);
+int union_create_topmost_dir(struct path *, struct qstr *, struct path *,
+ struct path *);
#else /* CONFIG_UNION_MOUNT */
#define IS_MNT_UNION(x) (0)
+#define IS_DIR_UNIONED(x) (0)
#define union_add_dir(x, y, z) ({ BUG(); (NULL); })
#define d_free_unions(x) do { } while (0)
+#define needs_lookup_union(x, y) ({ (0); })
+#define union_create_topmost_dir(w, x, y, z) ({ BUG(); (NULL); })
#endif /* CONFIG_UNION_MOUNT */
#endif /* __KERNEL__ */
--
1.6.3.3
next prev parent reply other threads:[~2010-08-06 22:36 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-08-06 22:34 [PATCH 00/38] VFS union mounts - Add MS_FALLTHRU Valerie Aurora
2010-08-06 22:34 ` [PATCH 01/38] VFS: Comment follow_mount() and friends Valerie Aurora
2010-08-06 22:34 ` [PATCH 02/38] VFS: Make lookup_hash() return a struct path Valerie Aurora
2010-08-06 22:34 ` [PATCH 03/38] VFS: Add read-only users count to superblock Valerie Aurora
2010-08-06 22:34 ` [PATCH 04/38] autofs4: Save autofs trigger's vfsmount in super block info Valerie Aurora
2010-08-06 22:34 ` [PATCH 05/38] whiteout/NFSD: Don't return information about whiteouts to userspace Valerie Aurora
[not found] ` <1281134124-17041-6-git-send-email-vaurora-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
2010-08-06 23:36 ` J. Bruce Fields
2010-08-06 22:34 ` [PATCH 06/38] whiteout: Add vfs_whiteout() and whiteout inode operation Valerie Aurora
2010-08-06 22:34 ` [PATCH 07/38] whiteout: Set opaque flag if new directory was previously a whiteout Valerie Aurora
2010-08-06 22:34 ` [PATCH 08/38] whiteout: Allow removal of a directory with whiteouts Valerie Aurora
2010-08-06 22:34 ` [PATCH 09/38] whiteout: tmpfs whiteout support Valerie Aurora
2010-08-06 22:34 ` [PATCH 10/38] whiteout: Split of ext2_append_link() from ext2_add_link() Valerie Aurora
2010-08-06 22:34 ` [PATCH 11/38] whiteout: ext2 whiteout support Valerie Aurora
2010-08-06 22:34 ` [PATCH 12/38] whiteout: jffs2 " Valerie Aurora
2010-08-06 22:34 ` [PATCH 13/38] fallthru: Basic fallthru definitions Valerie Aurora
2010-08-06 22:35 ` [PATCH 14/38] fallthru: ext2 fallthru support Valerie Aurora
2010-08-07 0:28 ` Andreas Dilger
2010-08-08 16:40 ` Valerie Aurora
2010-08-06 22:35 ` [PATCH 15/38] fallthru: jffs2 " Valerie Aurora
2010-08-06 22:35 ` [PATCH 16/38] fallthru: tmpfs " Valerie Aurora
2010-08-06 22:35 ` [PATCH 17/38] union-mount: Union mounts documentation Valerie Aurora
2010-08-06 22:35 ` [PATCH 18/38] union-mount: Introduce MNT_UNION and MS_UNION flags Valerie Aurora
2010-08-06 22:35 ` [PATCH 19/38] union-mount: Introduce union_dir structure and basic operations Valerie Aurora
2010-08-06 22:35 ` [PATCH 20/38] union-mount: Free union dirs on removal from dcache Valerie Aurora
2010-08-06 22:35 ` [PATCH 21/38] union-mount: Support for union mounting file systems Valerie Aurora
2010-08-06 22:35 ` Valerie Aurora [this message]
2010-08-06 22:35 ` [PATCH 23/38] union-mount: Call do_whiteout() on unlink and rmdir in unions Valerie Aurora
2010-08-06 22:35 ` [PATCH 24/38] union-mount: Copy up directory entries on first readdir() Valerie Aurora
2010-08-06 22:35 ` [PATCH 25/38] VFS: Split inode_permission() and create path_permission() Valerie Aurora
2010-08-06 22:35 ` [PATCH 26/38] VFS: Create user_path_nd() to lookup both parent and target Valerie Aurora
2010-08-06 22:35 ` [PATCH 27/38] union-mount: In-kernel file copyup routines Valerie Aurora
2010-08-06 22:35 ` [PATCH 28/38] union-mount: Implement union-aware access()/faccessat() Valerie Aurora
2010-08-06 22:35 ` [PATCH 29/38] union-mount: Implement union-aware link() Valerie Aurora
2010-08-06 22:35 ` [PATCH 30/38] union-mount: Implement union-aware rename() Valerie Aurora
2010-08-06 22:35 ` [PATCH 31/38] union-mount: Implement union-aware writable open() Valerie Aurora
2010-08-06 22:35 ` [PATCH 32/38] union-mount: Implement union-aware chown() Valerie Aurora
2010-08-06 22:35 ` [PATCH 33/38] union-mount: Implement union-aware truncate() Valerie Aurora
2010-08-06 22:35 ` [PATCH 34/38] union-mount: Implement union-aware chmod()/fchmodat() Valerie Aurora
2010-08-06 22:35 ` [PATCH 35/38] union-mount: Implement union-aware lchown() Valerie Aurora
2010-08-06 22:35 ` [PATCH 36/38] union-mount: Implement union-aware utimensat() Valerie Aurora
2010-08-06 22:35 ` [PATCH 37/38] union-mount: Implement union-aware setxattr() Valerie Aurora
2010-08-06 22:35 ` [PATCH 38/38] union-mount: Implement union-aware lsetxattr() Valerie Aurora
-- strict thread matches above, loose matches on Subject: below --
2010-06-25 19:04 [PATCH 00/38] Union mounts - multiple layers and submounts Valerie Aurora
2010-06-25 19:05 ` [PATCH 22/38] union-mount: Implement union lookup Valerie Aurora
2010-06-15 18:39 [PATCH 00/38] Union mounts - union stack as linked list Valerie Aurora
2010-06-15 18:39 ` [PATCH 22/38] union-mount: Implement union lookup Valerie Aurora
2010-07-13 4:49 ` Ian Kent
2010-07-19 21:58 ` Valerie Aurora
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1281134124-17041-23-git-send-email-vaurora@redhat.com \
--to=vaurora@redhat.com \
--cc=hch@infradead.org \
--cc=jblunck@suse.de \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).