[PATCH 1/6] Add a dentry op to handle automounting rather than abusing follow_link() [ver #2]

Ian Kent raven at themaw.net
Sat Jul 24 00:11:04 EDT 2010


On Fri, 2010-07-23 at 16:09 +0100, David Howells wrote:
> Here's an updated patch that:
> 
>  (1) Fixes a bug in error handling (needs to use path_put_conditional not
>      path_put).
> 
>  (2) Absorbs autofs4's decisions about whether to automount or not.  This
>      means that colour-ls won't cause automounts unless -L is supplied or it
>      doesn't get a DT_DIR flag from getdents().  It also means that autofs4
>      can dispense with this logic should it choose to use d_automount().

I think my statements about this were a little incorrect.

In the current kernel the check isn't imposed for ->lookup() but only
->d_revalidate() (the deosn't already exist vs the already exists
cases). Since ->d_lookup() (currently) leaves the dentry negative until
->mkdir() that could be used in the check. But then this may be starting
to get a little too autofs specific, maybe we should re-consider passing
the flags, I don't know.

In any case I'll have a go at using this as is, and see what happens.

> 
>  (3) Moves all the automounter logic out of __follow_mount() into
>      follow_automount().
> 
>  (4) Stops pathwalk at the automount point and returns that point in the fs
>      tree if it decides not to automount rather than reporting ELOOP (see its
>      use of EXDEV for this).
> 
> David
> 
> ---
> From: David Howells <dhowells at redhat.com>
> Subject: [PATCH] Add a dentry op to handle automounting rather than abusing follow_link()
> 
> Add a dentry op (d_automount) to handle automounting directories rather than
> abusing the follow_link() inode operation.  The operation is keyed off a new
> inode flag (S_AUTOMOUNT).
> 
> This makes it easier to add an AT_ flag to suppress terminal segment automount
> during pathwalk.  It should also remove the need for the kludge code in the
> pathwalk algorithm to handle directories with follow_link() semantics.
> 
> I've only changed __follow_mount() to handle automount points, but it might be
> necessary to change follow_mount() too.  The latter is only used from
> follow_dotdot(), but any automounts on ".." should be pinned whilst we're using
> a child of it.
> 
> I've also extracted the mount/don't-mount logic from autofs4 and included it
> here.  It makes the mount go ahead anyway if someone calls open() or creat(),
> tries to traverse the directory, tries to chdir/chroot/etc. into the directory,
> or sticks a '/' on the end of the pathname.  If they do a stat(), however,
> they'll only trigger the automount if they didn't also say O_NOFOLLOW.
> 
> Signed-off-by: David Howells <dhowells at redhat.com>
> ---
> 
>  Documentation/filesystems/Locking |    2 +
>  Documentation/filesystems/vfs.txt |   13 +++++
>  fs/namei.c                        |   96 ++++++++++++++++++++++++++++++-------
>  include/linux/dcache.h            |    5 ++
>  include/linux/fs.h                |    2 +
>  5 files changed, 100 insertions(+), 18 deletions(-)
> 
> 
> diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
> index 96d4293..ccbfa98 100644
> --- a/Documentation/filesystems/Locking
> +++ b/Documentation/filesystems/Locking
> @@ -16,6 +16,7 @@ prototypes:
>  	void (*d_release)(struct dentry *);
>  	void (*d_iput)(struct dentry *, struct inode *);
>  	char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
> +	struct vfsmount *(*d_automount)(struct path *path);
>  
>  locking rules:
>  	none have BKL
> @@ -27,6 +28,7 @@ d_delete:	yes		no		yes		no
>  d_release:	no		no		no		yes
>  d_iput:		no		no		no		yes
>  d_dname:	no		no		no		no
> +d_automount:	no		no		no		yes
>  
>  --------------------------- inode_operations --------------------------- 
>  prototypes:
> diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
> index 94677e7..31a9e8f 100644
> --- a/Documentation/filesystems/vfs.txt
> +++ b/Documentation/filesystems/vfs.txt
> @@ -851,6 +851,7 @@ struct dentry_operations {
>  	void (*d_release)(struct dentry *);
>  	void (*d_iput)(struct dentry *, struct inode *);
>  	char *(*d_dname)(struct dentry *, char *, int);
> +	struct vfsmount *(*d_automount)(struct path *);
>  };
>  
>    d_revalidate: called when the VFS needs to revalidate a dentry. This
> @@ -885,6 +886,18 @@ struct dentry_operations {
>  	at the end of the buffer, and returns a pointer to the first char.
>  	dynamic_dname() helper function is provided to take care of this.
>  
> +  d_automount: called when an automount dentry is to be traversed (optional).
> +	This should create a new VFS mount record, mount it on the directory
> +	and return the record to the caller.  The caller is supplied with a
> +	path parameter giving the automount directory to describe the automount
> +	target and the parent VFS mount record to provide inheritable mount
> +	parameters.  NULL should be returned if someone else managed to make
> +	the automount first.  If the automount failed, then an error code
> +	should be returned.
> +
> +	This function is only used if S_AUTOMOUNT is set on the inode to which
> +	the dentry refers.
> +
>  Example :
>  
>  static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen)
> diff --git a/fs/namei.c b/fs/namei.c
> index 868d0cb..6509ca5 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -617,24 +617,82 @@ int follow_up(struct path *path)
>  	return 1;
>  }
>  
> +/*
> + * Perform an automount
> + * - return -EXDEV to tell __follow_mount() to stop and return the path we were
> + *   called with.
> + */
> +static int follow_automount(struct path *path, unsigned flags, int res)
> +{
> +	struct vfsmount *mnt;
> +
> +	if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
> +		return -EREMOTE;
> +
> +	/* We want to mount if someone is trying to open/create a file of any
> +	 * type under the mountpoint, wants to traverse through the mountpoint
> +	 * or wants to open the mounted directory.
> +	 *
> +	 * We don't want to mount if someone's just doing a stat and they've
> +	 * set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and
> +	 * appended a '/' to the name.
> +	 */
> +	if (!(flags & LOOKUP_FOLLOW) &&
> +	    !(flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY |
> +		       LOOKUP_OPEN | LOOKUP_CREATE)))
> +		return -EXDEV;
> +
> +	current->total_link_count++;
> +	if (current->total_link_count >= 40)
> +		return -ELOOP;
> +
> +	mnt = path->dentry->d_op->d_automount(path);
> +	if (IS_ERR(mnt))
> +		return PTR_ERR(mnt);
> +	if (!mnt) /* mount collision */
> +		return 0;
> +
> +	if (mnt->mnt_sb == path->mnt->mnt_sb &&
> +	    mnt->mnt_root == path->dentry) {
> +		mntput(mnt);
> +		return -ELOOP;
> +	}
> +
> +	dput(path->dentry);
> +	if (res)
> +		mntput(path->mnt);
> +	path->mnt = mnt;
> +	path->dentry = dget(mnt->mnt_root);
> +	return 0;
> +}
> +
>  /* no need for dcache_lock, as serialization is taken care in
>   * namespace.c
>   */
> -static int __follow_mount(struct path *path)
> +static int __follow_mount(struct path *path, unsigned flags)
>  {
> -	int res = 0;
> -	while (d_mountpoint(path->dentry)) {
> -		struct vfsmount *mounted = lookup_mnt(path);
> -		if (!mounted)
> +	struct vfsmount *mounted;
> +	int ret, res = 0;
> +	for (;;) {
> +		while (d_mountpoint(path->dentry)) {
> +			mounted = lookup_mnt(path);
> +			if (!mounted)
> +				break;
> +			dput(path->dentry);
> +			if (res)
> +				mntput(path->mnt);
> +			path->mnt = mounted;
> +			path->dentry = dget(mounted->mnt_root);
> +			res = 1;
> +		}
> +		if (!d_automount_point(path->dentry))
>  			break;
> -		dput(path->dentry);
> -		if (res)
> -			mntput(path->mnt);
> -		path->mnt = mounted;
> -		path->dentry = dget(mounted->mnt_root);
> +		ret = follow_automount(path, flags, res);
> +		if (ret < 0)
> +			return ret == -EXDEV ? 0 : ret;
>  		res = 1;
>  	}
> -	return res;
> +	return 0;
>  }
>  
>  static void follow_mount(struct path *path)
> @@ -702,6 +760,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
>  	struct vfsmount *mnt = nd->path.mnt;
>  	struct dentry *dentry, *parent;
>  	struct inode *dir;
> +	int ret;
> +
>  	/*
>  	 * See if the low-level filesystem might want
>  	 * to use its own hash..
> @@ -720,8 +780,10 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
>  done:
>  	path->mnt = mnt;
>  	path->dentry = dentry;
> -	__follow_mount(path);
> -	return 0;
> +	ret = __follow_mount(path, nd->flags);
> +	if (unlikely(ret < 0))
> +		path_put_conditional(path, nd);
> +	return ret;
>  
>  need_lookup:
>  	parent = nd->path.dentry;
> @@ -1721,11 +1783,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
>  	if (open_flag & O_EXCL)
>  		goto exit_dput;
>  
> -	if (__follow_mount(path)) {
> -		error = -ELOOP;
> -		if (open_flag & O_NOFOLLOW)
> -			goto exit_dput;
> -	}
> +	error = __follow_mount(path, nd->flags);
> +	if (error < 0)
> +		goto exit_dput;
>  
>  	error = -ENOENT;
>  	if (!path->dentry->d_inode)
> diff --git a/include/linux/dcache.h b/include/linux/dcache.h
> index eebb617..5380bff 100644
> --- a/include/linux/dcache.h
> +++ b/include/linux/dcache.h
> @@ -139,6 +139,7 @@ struct dentry_operations {
>  	void (*d_release)(struct dentry *);
>  	void (*d_iput)(struct dentry *, struct inode *);
>  	char *(*d_dname)(struct dentry *, char *, int);
> +	struct vfsmount *(*d_automount)(struct path *);
>  };
>  
>  /* the dentry parameter passed to d_hash and d_compare is the parent
> @@ -157,6 +158,7 @@ d_compare:	no		yes		yes      no
>  d_delete:	no		yes		no       no
>  d_release:	no		no		no       yes
>  d_iput:		no		no		no       yes
> +d_automount:	no		no		no	 yes
>   */
>  
>  /* d_flags entries */
> @@ -389,6 +391,9 @@ static inline int d_mountpoint(struct dentry *dentry)
>  	return dentry->d_mounted;
>  }
>  
> +#define d_automount_point(dentry) \
> +	(dentry->d_inode && IS_AUTOMOUNT(dentry->d_inode))
> +
>  extern struct vfsmount *lookup_mnt(struct path *);
>  extern struct dentry *lookup_create(struct nameidata *nd, int is_dir);
>  
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 68ca1b0..a83fc81 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -235,6 +235,7 @@ struct inodes_stat_t {
>  #define S_NOCMTIME	128	/* Do not update file c/mtime */
>  #define S_SWAPFILE	256	/* Do not truncate: swapon got its bmaps */
>  #define S_PRIVATE	512	/* Inode is fs-internal */
> +#define S_AUTOMOUNT	1024	/* Automount/referral quasi-directory */
>  
>  /*
>   * Note that nosuid etc flags are inode-specific: setting some file-system
> @@ -269,6 +270,7 @@ struct inodes_stat_t {
>  #define IS_NOCMTIME(inode)	((inode)->i_flags & S_NOCMTIME)
>  #define IS_SWAPFILE(inode)	((inode)->i_flags & S_SWAPFILE)
>  #define IS_PRIVATE(inode)	((inode)->i_flags & S_PRIVATE)
> +#define IS_AUTOMOUNT(inode)	((inode)->i_flags & S_AUTOMOUNT)
>  
>  /* the read-only stuff doesn't really belong here, but any other place is
>     probably as bad and I don't want to create yet another include file. */





More information about the linux-afs mailing list