Re: [PATCH 3/3] autofs - fix AT_NO_AUTOMOUNT not being honored

From: NeilBrown
Date: Mon Nov 20 2017 - 20:53:33 EST


On Wed, May 10 2017, Ian Kent wrote:

> The fstatat(2) and statx() calls can pass the flag AT_NO_AUTOMOUNT
> which is meant to clear the LOOKUP_AUTOMOUNT flag and prevent triggering
> of an automount by the call. But this flag is unconditionally cleared
> for all stat family system calls except statx().
>
> stat family system calls have always triggered mount requests for the
> negative dentry case in follow_automount() which is intended but prevents
> the fstatat(2) and statx() AT_NO_AUTOMOUNT case from being handled.
>
> In order to handle the AT_NO_AUTOMOUNT for both system calls the
> negative dentry case in follow_automount() needs to be changed to
> return ENOENT when the LOOKUP_AUTOMOUNT flag is clear (and the other
> required flags are clear).
>
> AFAICT this change doesn't have any noticable side effects and may,
> in some use cases (although I didn't see it in testing) prevent
> unnecessary callbacks to the automount daemon.

Actually, this patch does have a noticeable side effect.

Previously, if /home were an indirect mount point so that /home/neilb
would mount my home directory, "ls -l /home/neilb" would always work.
Now it doesn't.

This happens because "ls" calls 'lstat' on the path and when that fails
with "ENOENT", it doesn't bother trying to open.

An alternate approach to the problem that this patch addresses is to
*not* change follow_automount() but instead change the automount daemon
and possibly autofs.

When a notification of access for an indirect mount point is received,
it would firstly just create the directory - not triggering a mount.
If another notification is then received (after the directory has been
created), it then triggers the mount.

I suspect this might need changes to autofs to avoid races when two
threads call lstat() on the same path at the same time. We would need
to ensure that automount didn't see this as two requests.... though
maybe it already has enough locking.

Does that seem reasonable? Should we revert this patch (as a
regression) and do something in automount instead?

Thanks,
NeilBrown


>
> It's also possible that a stat family call has been made with a
> path that is in the process of being mounted by some other process.
> But stat family calls should return the automount state of the path
> as it is "now" so it shouldn't wait for mount completion.
>
> This is the same semantic as the positive dentry case already
> handled.
>
> Signed-off-by: Ian Kent <raven@xxxxxxxxxx>
> Cc: David Howells <dhowells@xxxxxxxxxx>
> Cc: Colin Walters <walters@xxxxxxxxxx>
> Cc: Ondrej Holy <oholy@xxxxxxxxxx>
> Cc: stable@xxxxxxxxxxxxxxx
> ---
> fs/namei.c | 15 ++++++++++++---
> include/linux/fs.h | 3 +--
> 2 files changed, 13 insertions(+), 5 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 7286f87..cd74838 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -1129,9 +1129,18 @@ static int follow_automount(struct path *path, struct nameidata *nd,
> * of the daemon to instantiate them before they can be used.
> */
> if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
> - LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) &&
> - path->dentry->d_inode)
> - return -EISDIR;
> + LOOKUP_OPEN | LOOKUP_CREATE |
> + LOOKUP_AUTOMOUNT))) {
> + /* Positive dentry that isn't meant to trigger an
> + * automount, EISDIR will allow it to be used,
> + * otherwise there's no mount here "now" so return
> + * ENOENT.
> + */
> + if (path->dentry->d_inode)
> + return -EISDIR;
> + else
> + return -ENOENT;
> + }
>
> if (path->dentry->d_sb->s_user_ns != &init_user_ns)
> return -EACCES;
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index 26488b4..be09684 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2935,8 +2935,7 @@ static inline int vfs_lstat(const char __user *name, struct kstat *stat)
> static inline int vfs_fstatat(int dfd, const char __user *filename,
> struct kstat *stat, int flags)
> {
> - return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT,
> - stat, STATX_BASIC_STATS);
> + return vfs_statx(dfd, filename, flags, stat, STATX_BASIC_STATS);
> }
> static inline int vfs_fstat(int fd, struct kstat *stat)
> {

Attachment: signature.asc
Description: PGP signature