All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ian Kent <raven@themaw.net>
To: Valerie Aurora <vaurora@redhat.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>,
	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
Subject: Re: [PATCH 22/38] union-mount: Implement union lookup
Date: Tue, 13 Jul 2010 12:49:10 +0800	[thread overview]
Message-ID: <20100713044909.GG3949@zeus.themaw.net> (raw)
In-Reply-To: <1276627208-17242-23-git-send-email-vaurora@redhat.com>

On Tue, Jun 15, 2010 at 11:39:52AM -0700, Valerie Aurora wrote:
> 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 - What about different permissions on different layers on the same
> directory name?  Should complain, fail, test permissions on all
> layers, what?
> ---
>  fs/namei.c |  171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  fs/union.c |   94 +++++++++++++++++++++++++++++++++
>  fs/union.h |    7 +++
>  3 files changed, 271 insertions(+), 1 deletions(-)
> 
> diff --git a/fs/namei.c b/fs/namei.c
> index 06aad7e..45be5e5 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)
> @@ -722,6 +723,160 @@ 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;
> +	/* new_ud is the tail of the list of union dirs for this dentry */

Should new_ud be next_ud, since there is no new_ud defined?

> +	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;
> +		}
> +
> +		/* 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.
> @@ -752,6 +907,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:
> @@ -1223,8 +1383,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,
> @@ -2888,7 +3053,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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

  reply	other threads:[~2010-07-13  4:49 UTC|newest]

Thread overview: 112+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-06-15 18:39 [PATCH 00/38] Union mounts - union stack as linked list Valerie Aurora
2010-06-15 18:39 ` [PATCH 01/38] VFS: Comment follow_mount() and friends Valerie Aurora
2010-06-15 18:39 ` [PATCH 02/38] VFS: Make lookup_hash() return a struct path Valerie Aurora
2010-06-15 18:39 ` [PATCH 03/38] VFS: Add read-only users count to superblock Valerie Aurora
2010-06-15 18:39 ` [PATCH 04/38] autofs4: Save autofs trigger's vfsmount in super block info Valerie Aurora
2010-06-16  4:04   ` [autofs] " Ian Kent
2010-06-16 23:14     ` Valerie Aurora
2010-06-17  2:04       ` Ian Kent
2010-06-21  3:39     ` Ian Kent
2010-06-21 13:06       ` Miklos Szeredi
2010-06-21 13:24         ` Ian Kent
2010-06-22  4:46         ` Ian Kent
2010-06-22  5:49           ` J. R. Okajima
2010-06-22 13:11             ` Ian Kent
2010-06-23  1:23             ` Ian Kent
2010-06-23  2:07               ` J. R. Okajima
2010-06-23  2:37                 ` Ian Kent
2010-06-24  1:35                 ` Ian Kent
2010-06-24  5:16       ` Ian Kent
2010-06-15 18:39 ` [PATCH 05/38] whiteout/NFSD: Don't return information about whiteouts to userspace Valerie Aurora
2010-06-15 18:39   ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 06/38] whiteout: Add vfs_whiteout() and whiteout inode operation Valerie Aurora
2010-07-13  3:52   ` Ian Kent
2010-07-16 19:50     ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 07/38] whiteout: Set S_OPAQUE inode flag when creating directories Valerie Aurora
2010-07-13  4:05   ` Ian Kent
2010-07-16 20:12     ` Valerie Aurora
2010-07-17  4:14       ` Ian Kent
2010-06-15 18:39 ` [PATCH 08/38] whiteout: Allow removal of a directory with whiteouts Valerie Aurora
2010-06-15 18:39 ` [PATCH 09/38] whiteout: tmpfs whiteout support Valerie Aurora
2010-06-15 18:39   ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 10/38] whiteout: Split of ext2_append_link() from ext2_add_link() Valerie Aurora
2010-06-15 18:39 ` [PATCH 11/38] whiteout: ext2 whiteout support Valerie Aurora
2010-07-13  4:24   ` Ian Kent
2010-07-19 22:14     ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 12/38] whiteout: jffs2 " Valerie Aurora
2010-06-15 18:39   ` Valerie Aurora
2010-06-15 18:39   ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 13/38] fallthru: Basic fallthru definitions Valerie Aurora
2010-06-15 18:39 ` [PATCH 14/38] fallthru: ext2 fallthru support Valerie Aurora
2010-07-13  4:30   ` Ian Kent
2010-08-04 14:44   ` Miklos Szeredi
2010-08-04 22:48     ` Valerie Aurora
2010-08-05 10:36       ` Miklos Szeredi
2010-08-05 23:30         ` Valerie Aurora
2010-08-06  8:15           ` Miklos Szeredi
2010-08-06 17:16             ` Valerie Aurora
2010-08-06 17:44               ` Miklos Szeredi
2010-08-04 23:04     ` Valerie Aurora
2010-08-05 11:13       ` Miklos Szeredi
2010-08-06 17:12         ` Valerie Aurora
2010-08-17 22:27         ` Valerie Aurora
2010-08-18  8:26           ` Miklos Szeredi
2010-06-15 18:39 ` [PATCH 15/38] fallthru: jffs2 " Valerie Aurora
2010-06-15 18:39   ` Valerie Aurora
2010-06-15 18:39   ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 16/38] fallthru: tmpfs " Valerie Aurora
2010-06-15 18:39 ` [PATCH 17/38] union-mount: Union mounts documentation Valerie Aurora
2010-06-17  8:01   ` Alex Riesen
2010-06-17 18:39     ` Valerie Aurora
2010-06-17 20:32       ` Alex Riesen
2010-06-18 21:06         ` Valerie Aurora
2010-06-21 13:14       ` Miklos Szeredi
2010-06-21 23:17         ` Valerie Aurora
2010-06-23  8:43         ` Alex Riesen
2010-06-23  8:43           ` Alex Riesen
2010-06-15 18:39 ` [PATCH 18/38] union-mount: Introduce MNT_UNION and MS_UNION flags Valerie Aurora
2010-06-15 18:39 ` [PATCH 19/38] union-mount: Introduce union_dir structure and basic operations Valerie Aurora
2010-07-13  4:39   ` Ian Kent
2010-07-16 20:51     ` Valerie Aurora
2010-08-04 14:51   ` Miklos Szeredi
2010-08-04 19:47     ` Valerie Aurora
2010-08-05 10:28       ` Miklos Szeredi
2010-08-06 17:09         ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 20/38] union-mount: Free union dirs on removal from dcache Valerie Aurora
2010-06-15 18:39 ` [PATCH 21/38] union-mount: Support for mounting union mount file systems Valerie Aurora
2010-07-13  4:47   ` Ian Kent
2010-07-16 21:02     ` Valerie Aurora
2010-07-20  3:12       ` Ian Kent
2010-08-04 21:59         ` Valerie Aurora
2010-08-05 10:34           ` Miklos Szeredi
2010-08-06 16:33             ` Valerie Aurora
2010-07-16 21:05     ` Valerie Aurora
2010-08-04 14:55   ` Miklos Szeredi
2010-08-04 19:50     ` Valerie Aurora
2010-08-05  4:26       ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 22/38] union-mount: Implement union lookup Valerie Aurora
2010-07-13  4:49   ` Ian Kent [this message]
2010-07-19 21:58     ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 23/38] union-mount: Call do_whiteout() on unlink and rmdir in unions Valerie Aurora
2010-06-15 18:39 ` [PATCH 24/38] union-mount: Copy up directory entries on first readdir() Valerie Aurora
2010-07-13  4:51   ` Ian Kent
2010-06-15 18:39 ` [PATCH 25/38] VFS: Split inode_permission() and create path_permission() Valerie Aurora
2010-06-15 18:39 ` [PATCH 26/38] VFS: Create user_path_nd() to lookup both parent and target Valerie Aurora
2010-06-15 18:39 ` [PATCH 27/38] union-mount: In-kernel file copyup routines Valerie Aurora
2010-07-13  4:56   ` Ian Kent
2010-07-19 22:41     ` Valerie Aurora
2010-08-04 15:26   ` Miklos Szeredi
2010-08-05 19:54     ` Valerie Aurora
2010-06-15 18:39 ` [PATCH 28/38] union-mount: Implement union-aware access()/faccessat() Valerie Aurora
2010-06-15 18:39 ` [PATCH 29/38] union-mount: Implement union-aware link() Valerie Aurora
2010-06-15 18:40 ` [PATCH 30/38] union-mount: Implement union-aware rename() Valerie Aurora
2010-06-15 18:40 ` [PATCH 31/38] union-mount: Implement union-aware writable open() Valerie Aurora
2010-06-15 18:40 ` [PATCH 32/38] union-mount: Implement union-aware chown() Valerie Aurora
2010-06-15 18:40 ` [PATCH 33/38] union-mount: Implement union-aware truncate() Valerie Aurora
2010-06-15 18:40 ` [PATCH 34/38] union-mount: Implement union-aware chmod()/fchmodat() Valerie Aurora
2010-06-15 18:40 ` [PATCH 35/38] union-mount: Implement union-aware lchown() Valerie Aurora
2010-06-15 18:40 ` [PATCH 36/38] union-mount: Implement union-aware utimensat() Valerie Aurora
2010-06-15 18:40 ` [PATCH 37/38] union-mount: Implement union-aware setxattr() Valerie Aurora
2010-06-15 18:40 ` [PATCH 38/38] union-mount: Implement union-aware lsetxattr() Valerie Aurora
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-08-06 22:34 [PATCH 00/38] VFS union mounts - Add MS_FALLTHRU Valerie Aurora
2010-08-06 22:35 ` [PATCH 22/38] union-mount: Implement union lookup 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=20100713044909.GG3949@zeus.themaw.net \
    --to=raven@themaw.net \
    --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=vaurora@redhat.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.