linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint()
@ 2016-10-03  0:46 Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 1/8] vfs - change d_manage() to take a struct path Ian Kent
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:46 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

This series fixes the potential problem of autofs returning ELOOP when
a mount exists in a propogation private mount namespace other than the
namespace in which the mount is to be performed.

I'm posting the series as an RFC in the hope of catching stupid mistakes
that I may have made before submitting to mmotm. Please note the series
here is against the current Linus tree and may be slightly different when
posted for inclusion in mmotm.

Please review and post any comments.
---

Ian Kent (8):
      vfs - change d_manage() to take a struct path
      vfs - add path_is_mountpoint() helper
      vfs - add path_has_submounts()
      autofs - change autofs4_expire_wait() to take struct path
      autofs - change autofs4_wait() to take struct path
      autofs - use path_is_mountpoint() to fix unreliable d_mountpoint() checks
      autofs - use path_has_submounts() to fix unreliable have_submount() checks
      vfs - remove unused have_submounts() function


 Documentation/filesystems/Locking |    2 +
 Documentation/filesystems/vfs.txt |    2 +
 fs/autofs4/autofs_i.h             |    4 +-
 fs/autofs4/dev-ioctl.c            |    4 +-
 fs/autofs4/expire.c               |    8 +++-
 fs/autofs4/root.c                 |   71 +++++++++++++++++++++----------------
 fs/autofs4/waitq.c                |   13 +++++--
 fs/dcache.c                       |   36 ++++++++++---------
 fs/namei.c                        |   13 +++----
 fs/namespace.c                    |   43 ++++++++++++++++++++++
 include/linux/dcache.h            |    4 +-
 include/linux/fs.h                |    2 +
 12 files changed, 133 insertions(+), 69 deletions(-)

--
Ian

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

* [RFC PATCH 1/8] vfs - change d_manage() to take a struct path
  2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
@ 2016-10-03  0:46 ` Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 2/8] vfs - add path_is_mountpoint() helper Ian Kent
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:46 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

For the autofs module to be able to reliably check if a dentry is a
mountpoint in a multiple namespace environment the ->d_manage() dentry
operation will need to take a path argument instead of a dentry.

Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
---
 Documentation/filesystems/Locking |    2 +-
 Documentation/filesystems/vfs.txt |    2 +-
 fs/autofs4/root.c                 |    5 +++--
 fs/namei.c                        |   13 ++++++-------
 include/linux/dcache.h            |    2 +-
 5 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index d30fb2c..189ea98 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -20,7 +20,7 @@ prototypes:
 	void (*d_iput)(struct dentry *, struct inode *);
 	char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
 	struct vfsmount *(*d_automount)(struct path *path);
-	int (*d_manage)(struct dentry *, bool);
+	int (*d_manage)(struct path *, bool);
 	struct dentry *(*d_real)(struct dentry *, const struct inode *,
 				 unsigned int);
 
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 9ace359..0035ac8 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -939,7 +939,7 @@ struct dentry_operations {
 	void (*d_iput)(struct dentry *, struct inode *);
 	char *(*d_dname)(struct dentry *, char *, int);
 	struct vfsmount *(*d_automount)(struct path *);
-	int (*d_manage)(struct dentry *, bool);
+	int (*d_manage)(struct path *, bool);
 	struct dentry *(*d_real)(struct dentry *, const struct inode *,
 				 unsigned int);
 };
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index fa84bb8..0705daa 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -32,7 +32,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file);
 static struct dentry *autofs4_lookup(struct inode *,
 				     struct dentry *, unsigned int);
 static struct vfsmount *autofs4_d_automount(struct path *);
-static int autofs4_d_manage(struct dentry *, bool);
+static int autofs4_d_manage(struct path *, bool);
 static void autofs4_dentry_release(struct dentry *);
 
 const struct file_operations autofs4_root_operations = {
@@ -421,8 +421,9 @@ done:
 	return NULL;
 }
 
-static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
+static int autofs4_d_manage(struct path *path, bool rcu_walk)
 {
+	struct dentry *dentry = path->dentry;
 	struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
 	struct autofs_info *ino = autofs4_dentry_ino(dentry);
 	int status;
diff --git a/fs/namei.c b/fs/namei.c
index adb0414..e86b9d0 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1200,7 +1200,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
 		if (managed & DCACHE_MANAGE_TRANSIT) {
 			BUG_ON(!path->dentry->d_op);
 			BUG_ON(!path->dentry->d_op->d_manage);
-			ret = path->dentry->d_op->d_manage(path->dentry, false);
+			ret = path->dentry->d_op->d_manage(path, false);
 			if (ret < 0)
 				break;
 		}
@@ -1263,10 +1263,10 @@ int follow_down_one(struct path *path)
 }
 EXPORT_SYMBOL(follow_down_one);
 
-static inline int managed_dentry_rcu(struct dentry *dentry)
+static inline int managed_dentry_rcu(struct path *path)
 {
-	return (dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
-		dentry->d_op->d_manage(dentry, true) : 0;
+	return (path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
+		path->dentry->d_op->d_manage(path, true) : 0;
 }
 
 /*
@@ -1282,7 +1282,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
 		 * Don't forget we might have a non-mountpoint managed dentry
 		 * that wants to block transit.
 		 */
-		switch (managed_dentry_rcu(path->dentry)) {
+		switch (managed_dentry_rcu(path)) {
 		case -ECHILD:
 		default:
 			return false;
@@ -1392,8 +1392,7 @@ int follow_down(struct path *path)
 		if (managed & DCACHE_MANAGE_TRANSIT) {
 			BUG_ON(!path->dentry->d_op);
 			BUG_ON(!path->dentry->d_op->d_manage);
-			ret = path->dentry->d_op->d_manage(
-				path->dentry, false);
+			ret = path->dentry->d_op->d_manage(path, false);
 			if (ret < 0)
 				return ret == -EISDIR ? 0 : ret;
 		}
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 5ff3e9a..ad2df92 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -139,7 +139,7 @@ struct dentry_operations {
 	void (*d_iput)(struct dentry *, struct inode *);
 	char *(*d_dname)(struct dentry *, char *, int);
 	struct vfsmount *(*d_automount)(struct path *);
-	int (*d_manage)(struct dentry *, bool);
+	int (*d_manage)(struct path *, bool);
 	struct dentry *(*d_real)(struct dentry *, const struct inode *,
 				 unsigned int);
 } ____cacheline_aligned;

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

* [RFC PATCH 2/8] vfs - add path_is_mountpoint() helper
  2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 1/8] vfs - change d_manage() to take a struct path Ian Kent
@ 2016-10-03  0:46 ` Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 3/8] vfs - add path_has_submounts() Ian Kent
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:46 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

From: Ian Kent <ikent@redhat.com>

d_mountpoint() can only be used reliably to establish if a dentry is
not mounted in any namespace. It isn't aware of the possibility there
may be multiple mounts using a given dentry that may be in a different
namespace.

Add helper functions, path_is_mountpoint() and an rcu version , that
checks if a struct path is a mountpoint for this case.

Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
---
 fs/namespace.c     |   43 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h |    2 ++
 2 files changed, 45 insertions(+)

diff --git a/fs/namespace.c b/fs/namespace.c
index 7bb2cda..ca1faaa 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1153,6 +1153,49 @@ struct vfsmount *mntget(struct vfsmount *mnt)
 }
 EXPORT_SYMBOL(mntget);
 
+static bool __path_is_mountpoint(struct path *path)
+{
+	struct mount *mount;
+	struct vfsmount *mnt;
+	unsigned seq;
+
+	do {
+		seq = read_seqbegin(&mount_lock);
+		mount = __lookup_mnt(path->mnt, path->dentry);
+		mnt = mount ? &mount->mnt : NULL;
+	} while (mnt &&
+		 !(mnt->mnt_flags & MNT_SYNC_UMOUNT) &&
+		 read_seqretry(&mount_lock, seq));
+
+	return mnt != NULL;
+}
+
+/* Check if path is a mount in current namespace */
+bool path_is_mountpoint(struct path *path)
+{
+	bool res;
+
+	if (!d_mountpoint(path->dentry))
+		return 0;
+
+	rcu_read_lock();
+	res = __path_is_mountpoint(path);
+	rcu_read_unlock();
+
+	return res;
+}
+EXPORT_SYMBOL(path_is_mountpoint);
+
+/* Check if path is a mount in current namespace */
+bool path_is_mountpoint_rcu(struct path *path)
+{
+	if (!d_mountpoint(path->dentry))
+		return 0;
+
+	return __path_is_mountpoint(path);
+}
+EXPORT_SYMBOL(path_is_mountpoint_rcu);
+
 struct vfsmount *mnt_clone_internal(struct path *path)
 {
 	struct mount *p;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 901e25d..d588b26 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2117,6 +2117,8 @@ extern int vfs_ustat(dev_t, struct kstatfs *);
 extern int freeze_super(struct super_block *super);
 extern int thaw_super(struct super_block *super);
 extern bool our_mnt(struct vfsmount *mnt);
+extern bool path_is_mountpoint(struct path *);
+extern bool path_is_mountpoint_rcu(struct path *);
 
 extern int current_umask(void);
 

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

* [RFC PATCH 3/8] vfs - add path_has_submounts()
  2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 1/8] vfs - change d_manage() to take a struct path Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 2/8] vfs - add path_is_mountpoint() helper Ian Kent
@ 2016-10-03  0:46 ` Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 4/8] autofs - change autofs4_expire_wait() to take struct path Ian Kent
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:46 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

From: Ian Kent <ikent@redhat.com>

d_mountpoint() can only be used reliably to establish if a dentry is
not mounted in any namespace. It isn't aware of the possibility there
may be multiple mounts using the given dentry, possibly in a different
namespace.

Add function, path_has_submounts(), that checks is a struct path contains
mounts (or is a mountpoint itself) to handle this case.

Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
---
 fs/dcache.c            |   35 +++++++++++++++++++++++++++++++++++
 include/linux/dcache.h |    1 +
 2 files changed, 36 insertions(+)

diff --git a/fs/dcache.c b/fs/dcache.c
index 5c7cc95..872f04e 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1306,6 +1306,41 @@ int have_submounts(struct dentry *parent)
 }
 EXPORT_SYMBOL(have_submounts);
 
+struct check_mount {
+	struct vfsmount *mnt;
+	unsigned int mounted;
+};
+
+static enum d_walk_ret path_check_mount(void *data, struct dentry *dentry)
+{
+	struct check_mount *info = data;
+	struct path path = { .mnt = info->mnt, .dentry = dentry };
+
+	if (path_is_mountpoint(&path)) {
+		info->mounted = 1;
+		return D_WALK_QUIT;
+	}
+	return D_WALK_CONTINUE;
+}
+
+/**
+ * path_has_submounts - check for mounts over a dentry in the
+ *                      current namespace.
+ * @parent: path to check.
+ *
+ * Return true if the parent or its subdirectories contain
+ * a mount point in the current namespace.
+ */
+int path_has_submounts(struct path *parent)
+{
+	struct check_mount data = { .mnt = parent->mnt, .mounted = 0 };
+
+	d_walk(parent->dentry, &data, path_check_mount, NULL);
+
+	return data.mounted;
+}
+EXPORT_SYMBOL(path_has_submounts);
+
 /*
  * Called by mount code to set a mountpoint and check if the mountpoint is
  * reachable (e.g. NFS can unhash a directory dentry and then the complete
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index ad2df92..5c5d197 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -255,6 +255,7 @@ extern void d_prune_aliases(struct inode *);
 
 /* test whether we have any submounts in a subdir tree */
 extern int have_submounts(struct dentry *);
+extern int path_has_submounts(struct path *);
 
 /*
  * This adds the entry to the hash queues.

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

* [RFC PATCH 4/8] autofs - change autofs4_expire_wait() to take struct path
  2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
                   ` (2 preceding siblings ...)
  2016-10-03  0:46 ` [RFC PATCH 3/8] vfs - add path_has_submounts() Ian Kent
@ 2016-10-03  0:46 ` Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 5/8] autofs - change autofs4_wait() " Ian Kent
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:46 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

From: Ian Kent <ikent@redhat.com>

In order to use the functions path_is_mountpoint() (or it's rcu-walk
variant) and path_has_submounts() autofs needs to pass a struct path
in several places.

Start by changing autofs4_expire_wait() to take a struct path instead
of a struct dentry.

Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
---
 fs/autofs4/autofs_i.h  |    2 +-
 fs/autofs4/dev-ioctl.c |    2 +-
 fs/autofs4/expire.c    |    3 ++-
 fs/autofs4/root.c      |   12 +++++++-----
 4 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index a439548..b548ee6 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -148,7 +148,7 @@ void autofs4_free_ino(struct autofs_info *);
 
 /* Expiration */
 int is_autofs4_dentry(struct dentry *);
-int autofs4_expire_wait(struct dentry *dentry, int rcu_walk);
+int autofs4_expire_wait(struct path *path, int rcu_walk);
 int autofs4_expire_run(struct super_block *, struct vfsmount *,
 		       struct autofs_sb_info *,
 		       struct autofs_packet_expire __user *);
diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
index c7fcc74..3cd96e6 100644
--- a/fs/autofs4/dev-ioctl.c
+++ b/fs/autofs4/dev-ioctl.c
@@ -457,7 +457,7 @@ static int autofs_dev_ioctl_requester(struct file *fp,
 	ino = autofs4_dentry_ino(path.dentry);
 	if (ino) {
 		err = 0;
-		autofs4_expire_wait(path.dentry, 0);
+		autofs4_expire_wait(&path, 0);
 		spin_lock(&sbi->fs_lock);
 		param->requester.uid =
 			from_kuid_munged(current_user_ns(), ino->uid);
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index d8e6d42..7eac498 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -495,8 +495,9 @@ found:
 	return expired;
 }
 
-int autofs4_expire_wait(struct dentry *dentry, int rcu_walk)
+int autofs4_expire_wait(struct path *path, int rcu_walk)
 {
+	struct dentry *dentry = path->dentry;
 	struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
 	struct autofs_info *ino = autofs4_dentry_ino(dentry);
 	int status;
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 0705daa..e5ed061 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -286,22 +286,24 @@ static int autofs4_mount_wait(struct dentry *dentry, bool rcu_walk)
 	return status;
 }
 
-static int do_expire_wait(struct dentry *dentry, bool rcu_walk)
+static int do_expire_wait(struct path *path, bool rcu_walk)
 {
+	struct dentry *dentry = path->dentry;
 	struct dentry *expiring;
 
 	expiring = autofs4_lookup_expiring(dentry, rcu_walk);
 	if (IS_ERR(expiring))
 		return PTR_ERR(expiring);
 	if (!expiring)
-		return autofs4_expire_wait(dentry, rcu_walk);
+		return autofs4_expire_wait(path, rcu_walk);
 	else {
+		struct path this = { .mnt = path->mnt, .dentry = expiring };
 		/*
 		 * If we are racing with expire the request might not
 		 * be quite complete, but the directory has been removed
 		 * so it must have been successful, just wait for it.
 		 */
-		autofs4_expire_wait(expiring, 0);
+		autofs4_expire_wait(&this, 0);
 		autofs4_del_expiring(expiring);
 		dput(expiring);
 	}
@@ -354,7 +356,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
 	 * and the directory was removed, so just go ahead and try
 	 * the mount.
 	 */
-	status = do_expire_wait(dentry, 0);
+	status = do_expire_wait(path, 0);
 	if (status && status != -EAGAIN)
 		return NULL;
 
@@ -438,7 +440,7 @@ static int autofs4_d_manage(struct path *path, bool rcu_walk)
 	}
 
 	/* Wait for pending expires */
-	if (do_expire_wait(dentry, rcu_walk) == -ECHILD)
+	if (do_expire_wait(path, rcu_walk) == -ECHILD)
 		return -ECHILD;
 
 	/*

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

* [RFC PATCH 5/8] autofs - change autofs4_wait() to take struct path
  2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
                   ` (3 preceding siblings ...)
  2016-10-03  0:46 ` [RFC PATCH 4/8] autofs - change autofs4_expire_wait() to take struct path Ian Kent
@ 2016-10-03  0:46 ` Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 6/8] autofs - use path_is_mountpoint() to fix unreliable d_mountpoint() checks Ian Kent
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:46 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

From: Ian Kent <ikent@redhat.com>

In order to use the functions path_is_mountpoint() (or its rcu-walk
variant) and path_has_submounts() autofs needs to pass a struct path
in several places.

Now change autofs4_wait() to take a struct path instead of a struct
dentry.

Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
---
 fs/autofs4/autofs_i.h |    2 +-
 fs/autofs4/expire.c   |    5 +++--
 fs/autofs4/root.c     |   16 ++++++++--------
 fs/autofs4/waitq.c    |    3 ++-
 4 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index b548ee6..1b6ed78 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -220,7 +220,7 @@ static inline int autofs_prepare_pipe(struct file *pipe)
 
 /* Queue management functions */
 
-int autofs4_wait(struct autofs_sb_info *, struct dentry *, enum autofs_notify);
+int autofs4_wait(struct autofs_sb_info *, struct path *, enum autofs_notify);
 int autofs4_wait_release(struct autofs_sb_info *, autofs_wqt_t, int);
 void autofs4_catatonic_mode(struct autofs_sb_info *);
 
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 7eac498..a37ba40 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -526,7 +526,7 @@ retry:
 
 		pr_debug("waiting for expire %p name=%pd\n", dentry, dentry);
 
-		status = autofs4_wait(sbi, dentry, NFY_NONE);
+		status = autofs4_wait(sbi, path, NFY_NONE);
 		wait_for_completion(&ino->expire_complete);
 
 		pr_debug("expire done status=%d\n", status);
@@ -593,11 +593,12 @@ int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
 
 	if (dentry) {
 		struct autofs_info *ino = autofs4_dentry_ino(dentry);
+		struct path path = { .mnt = mnt, .dentry = dentry };
 
 		/* This is synchronous because it makes the daemon a
 		 * little easier
 		 */
-		ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
+		ret = autofs4_wait(sbi, &path, NFY_EXPIRE);
 
 		spin_lock(&sbi->fs_lock);
 		/* avoid rapid-fire expire attempts if expiry fails */
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index e5ed061..a12c248 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -269,17 +269,17 @@ next:
 	return NULL;
 }
 
-static int autofs4_mount_wait(struct dentry *dentry, bool rcu_walk)
+static int autofs4_mount_wait(struct path *path, bool rcu_walk)
 {
-	struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-	struct autofs_info *ino = autofs4_dentry_ino(dentry);
+	struct autofs_sb_info *sbi = autofs4_sbi(path->dentry->d_sb);
+	struct autofs_info *ino = autofs4_dentry_ino(path->dentry);
 	int status = 0;
 
 	if (ino->flags & AUTOFS_INF_PENDING) {
 		if (rcu_walk)
 			return -ECHILD;
-		pr_debug("waiting for mount name=%pd\n", dentry);
-		status = autofs4_wait(sbi, dentry, NFY_MOUNT);
+		pr_debug("waiting for mount name=%pd\n", path->dentry);
+		status = autofs4_wait(sbi, path, NFY_MOUNT);
 		pr_debug("mount wait done status=%d\n", status);
 	}
 	ino->last_used = jiffies;
@@ -364,7 +364,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
 	spin_lock(&sbi->fs_lock);
 	if (ino->flags & AUTOFS_INF_PENDING) {
 		spin_unlock(&sbi->fs_lock);
-		status = autofs4_mount_wait(dentry, 0);
+		status = autofs4_mount_wait(path, 0);
 		if (status)
 			return ERR_PTR(status);
 		goto done;
@@ -405,7 +405,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
 		}
 		ino->flags |= AUTOFS_INF_PENDING;
 		spin_unlock(&sbi->fs_lock);
-		status = autofs4_mount_wait(dentry, 0);
+		status = autofs4_mount_wait(path, 0);
 		spin_lock(&sbi->fs_lock);
 		ino->flags &= ~AUTOFS_INF_PENDING;
 		if (status) {
@@ -447,7 +447,7 @@ static int autofs4_d_manage(struct path *path, bool rcu_walk)
 	 * This dentry may be under construction so wait on mount
 	 * completion.
 	 */
-	status = autofs4_mount_wait(dentry, rcu_walk);
+	status = autofs4_mount_wait(path, rcu_walk);
 	if (status)
 		return status;
 
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index 431fd7e..f757f87 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -345,8 +345,9 @@ static int validate_request(struct autofs_wait_queue **wait,
 }
 
 int autofs4_wait(struct autofs_sb_info *sbi,
-		 struct dentry *dentry, enum autofs_notify notify)
+		 struct path *path, enum autofs_notify notify)
 {
+	struct dentry *dentry = path->dentry;
 	struct autofs_wait_queue *wq;
 	struct qstr qstr;
 	char *name;

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

* [RFC PATCH 6/8] autofs - use path_is_mountpoint() to fix unreliable d_mountpoint() checks
  2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
                   ` (4 preceding siblings ...)
  2016-10-03  0:46 ` [RFC PATCH 5/8] autofs - change autofs4_wait() " Ian Kent
@ 2016-10-03  0:46 ` Ian Kent
  2016-10-03  0:46 ` [RFC PATCH 7/8] autofs - use path_has_submounts() to fix unreliable have_submount() checks Ian Kent
  2016-10-03  0:47 ` [RFC PATCH 8/8] vfs - remove unused have_submounts() function Ian Kent
  7 siblings, 0 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:46 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

From: Ian Kent <ikent@redhat.com>

If an automount mount is clone(2)ed into a file system that is propagation
private, when it later expires in the originating namespace, subsequent
calls to autofs ->d_automount() for that dentry in the original namespace
will return ELOOP until the mount is umounted in the cloned namespace.

Now that a struct path is available where needed use path_is_mountpoint()
instead of d_mountpoint() so we don't get false positives when checking
if a dentry is a mount point in the current namespace.

Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
---
 fs/autofs4/root.c |   24 ++++++++++++++++--------
 1 file changed, 16 insertions(+), 8 deletions(-)

diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index a12c248..0f5d264 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -107,12 +107,15 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
 {
 	struct dentry *dentry = file->f_path.dentry;
 	struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+	struct path path;
 
 	pr_debug("file=%p dentry=%p %pd\n", file, dentry, dentry);
 
 	if (autofs4_oz_mode(sbi))
 		goto out;
 
+	path = file->f_path;
+
 	/*
 	 * An empty directory in an autofs file system is always a
 	 * mount point. The daemon must have failed to mount this
@@ -123,7 +126,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
 	 * it.
 	 */
 	spin_lock(&sbi->lookup_lock);
-	if (!d_mountpoint(dentry) && simple_empty(dentry)) {
+	if (!path_is_mountpoint(&path) && simple_empty(dentry)) {
 		spin_unlock(&sbi->lookup_lock);
 		return -ENOENT;
 	}
@@ -372,15 +375,15 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
 
 	/*
 	 * If the dentry is a symlink it's equivalent to a directory
-	 * having d_mountpoint() true, so there's no need to call back
-	 * to the daemon.
+	 * having path_is_mountpoint() true, so there's no need to call
+	 * back to the daemon.
 	 */
 	if (d_really_is_positive(dentry) && d_is_symlink(dentry)) {
 		spin_unlock(&sbi->fs_lock);
 		goto done;
 	}
 
-	if (!d_mountpoint(dentry)) {
+	if (!path_is_mountpoint(path)) {
 		/*
 		 * It's possible that user space hasn't removed directories
 		 * after umounting a rootless multi-mount, although it
@@ -434,8 +437,13 @@ static int autofs4_d_manage(struct path *path, bool rcu_walk)
 
 	/* The daemon never waits. */
 	if (autofs4_oz_mode(sbi)) {
-		if (!d_mountpoint(dentry))
-			return -EISDIR;
+		if (rcu_walk) {
+			if (!path_is_mountpoint_rcu(path))
+				return -EISDIR;
+		} else {
+			if (!path_is_mountpoint(path))
+				return -EISDIR;
+		}
 		return 0;
 	}
 
@@ -463,7 +471,7 @@ static int autofs4_d_manage(struct path *path, bool rcu_walk)
 
 		if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
 			return 0;
-		if (d_mountpoint(dentry))
+		if (path_is_mountpoint_rcu(path))
 			return 0;
 		inode = d_inode_rcu(dentry);
 		if (inode && S_ISLNK(inode->i_mode))
@@ -490,7 +498,7 @@ static int autofs4_d_manage(struct path *path, bool rcu_walk)
 		 * we can avoid needless calls ->d_automount() and avoid
 		 * an incorrect ELOOP error return.
 		 */
-		if ((!d_mountpoint(dentry) && !simple_empty(dentry)) ||
+		if ((!path_is_mountpoint(path) && !simple_empty(dentry)) ||
 		    (d_really_is_positive(dentry) && d_is_symlink(dentry)))
 			status = -EISDIR;
 	}

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

* [RFC PATCH 7/8] autofs - use path_has_submounts() to fix unreliable have_submount() checks
  2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
                   ` (5 preceding siblings ...)
  2016-10-03  0:46 ` [RFC PATCH 6/8] autofs - use path_is_mountpoint() to fix unreliable d_mountpoint() checks Ian Kent
@ 2016-10-03  0:46 ` Ian Kent
  2016-10-03  0:47 ` [RFC PATCH 8/8] vfs - remove unused have_submounts() function Ian Kent
  7 siblings, 0 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:46 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

From: Ian Kent <ikent@redhat.com>

If an automount mount is clone(2)ed into a file system that is propagation
private, when it later expires in the originating namespace, subsequent
calls to autofs ->d_automount() for that dentry in the original namespace
will return ELOOP until the mount is umounted in the cloned namespace.

Now that a struct path is available where needed use path_has_submounts()
instead of have_submounts() so we don't get false positives when checking
if a dentry is a mount point or contains mounts in the current namespace.

Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
---
 fs/autofs4/dev-ioctl.c |    2 +-
 fs/autofs4/root.c      |   14 +++++++-------
 fs/autofs4/waitq.c     |   10 +++++++---
 3 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
index 3cd96e6..953e790 100644
--- a/fs/autofs4/dev-ioctl.c
+++ b/fs/autofs4/dev-ioctl.c
@@ -564,7 +564,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
 
 		devid = new_encode_dev(dev);
 
-		err = have_submounts(path.dentry);
+		err = path_has_submounts(&path);
 
 		if (follow_down_one(&path))
 			magic = path.dentry->d_sb->s_magic;
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 0f5d264..cf90d37 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -387,16 +387,16 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
 		/*
 		 * It's possible that user space hasn't removed directories
 		 * after umounting a rootless multi-mount, although it
-		 * should. For v5 have_submounts() is sufficient to handle
-		 * this because the leaves of the directory tree under the
-		 * mount never trigger mounts themselves (they have an autofs
-		 * trigger mount mounted on them). But v4 pseudo direct mounts
-		 * do need the leaves to trigger mounts. In this case we
-		 * have no choice but to use the list_empty() check and
+		 * should. For v5 path_has_submounts() is sufficient to
+		 * handle this because the leaves of the directory tree under
+		 * the mount never trigger mounts themselves (they have an
+		 * autofs trigger mount mounted on them). But v4 pseudo direct
+		 * mounts do need the leaves to trigger mounts. In this case
+		 * we have no choice but to use the list_empty() check and
 		 * require user space behave.
 		 */
 		if (sbi->version > 4) {
-			if (have_submounts(dentry)) {
+			if (path_has_submounts(path)) {
 				spin_unlock(&sbi->fs_lock);
 				goto done;
 			}
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
index f757f87..ed05cae 100644
--- a/fs/autofs4/waitq.c
+++ b/fs/autofs4/waitq.c
@@ -250,8 +250,9 @@ autofs4_find_wait(struct autofs_sb_info *sbi, const struct qstr *qstr)
 static int validate_request(struct autofs_wait_queue **wait,
 			    struct autofs_sb_info *sbi,
 			    const struct qstr *qstr,
-			    struct dentry *dentry, enum autofs_notify notify)
+			    struct path *path, enum autofs_notify notify)
 {
+	struct dentry *dentry = path->dentry;
 	struct autofs_wait_queue *wq;
 	struct autofs_info *ino;
 
@@ -314,6 +315,7 @@ static int validate_request(struct autofs_wait_queue **wait,
 	 */
 	if (notify == NFY_MOUNT) {
 		struct dentry *new = NULL;
+		struct path this;
 		int valid = 1;
 
 		/*
@@ -333,7 +335,9 @@ static int validate_request(struct autofs_wait_queue **wait,
 					dentry = new;
 			}
 		}
-		if (have_submounts(dentry))
+		this.mnt = path->mnt;
+		this.dentry = dentry;
+		if (path_has_submounts(&this))
 			valid = 0;
 
 		if (new)
@@ -406,7 +410,7 @@ int autofs4_wait(struct autofs_sb_info *sbi,
 		return -EINTR;
 	}
 
-	ret = validate_request(&wq, sbi, &qstr, dentry, notify);
+	ret = validate_request(&wq, sbi, &qstr, path, notify);
 	if (ret <= 0) {
 		if (ret != -EINTR)
 			mutex_unlock(&sbi->wq_mutex);

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

* [RFC PATCH 8/8] vfs - remove unused have_submounts() function
  2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
                   ` (6 preceding siblings ...)
  2016-10-03  0:46 ` [RFC PATCH 7/8] autofs - use path_has_submounts() to fix unreliable have_submount() checks Ian Kent
@ 2016-10-03  0:47 ` Ian Kent
  7 siblings, 0 replies; 9+ messages in thread
From: Ian Kent @ 2016-10-03  0:47 UTC (permalink / raw)
  To: Kernel Mailing List
  Cc: autofs mailing list, Al Viro, linux-fsdevel, Omar Sandoval,
	Andrew Morton, Eric W. Biederman

Now that path_has_submounts() has been added have_submounts() is no
longer used so remove it.

Signed-off-by: Ian Kent <raven@themaw.net>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Omar Sandoval <osandov@osandov.com>
---
 fs/dcache.c            |   33 ---------------------------------
 include/linux/dcache.h |    1 -
 2 files changed, 34 deletions(-)

diff --git a/fs/dcache.c b/fs/dcache.c
index 872f04e..719d8b4 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1273,39 +1273,6 @@ rename_retry:
 	goto again;
 }
 
-/*
- * Search for at least 1 mount point in the dentry's subdirs.
- * We descend to the next level whenever the d_subdirs
- * list is non-empty and continue searching.
- */
-
-static enum d_walk_ret check_mount(void *data, struct dentry *dentry)
-{
-	int *ret = data;
-	if (d_mountpoint(dentry)) {
-		*ret = 1;
-		return D_WALK_QUIT;
-	}
-	return D_WALK_CONTINUE;
-}
-
-/**
- * have_submounts - check for mounts over a dentry
- * @parent: dentry to check.
- *
- * Return true if the parent or its subdirectories contain
- * a mount point
- */
-int have_submounts(struct dentry *parent)
-{
-	int ret = 0;
-
-	d_walk(parent, &ret, check_mount, NULL);
-
-	return ret;
-}
-EXPORT_SYMBOL(have_submounts);
-
 struct check_mount {
 	struct vfsmount *mnt;
 	unsigned int mounted;
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 5c5d197..384c87c 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -254,7 +254,6 @@ extern struct dentry *d_find_alias(struct inode *);
 extern void d_prune_aliases(struct inode *);
 
 /* test whether we have any submounts in a subdir tree */
-extern int have_submounts(struct dentry *);
 extern int path_has_submounts(struct path *);
 
 /*

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

end of thread, other threads:[~2016-10-03  0:51 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-03  0:46 [RFC PATCH 0/8] Patch series to fix autofs unreliable usage of d_mountpoint() Ian Kent
2016-10-03  0:46 ` [RFC PATCH 1/8] vfs - change d_manage() to take a struct path Ian Kent
2016-10-03  0:46 ` [RFC PATCH 2/8] vfs - add path_is_mountpoint() helper Ian Kent
2016-10-03  0:46 ` [RFC PATCH 3/8] vfs - add path_has_submounts() Ian Kent
2016-10-03  0:46 ` [RFC PATCH 4/8] autofs - change autofs4_expire_wait() to take struct path Ian Kent
2016-10-03  0:46 ` [RFC PATCH 5/8] autofs - change autofs4_wait() " Ian Kent
2016-10-03  0:46 ` [RFC PATCH 6/8] autofs - use path_is_mountpoint() to fix unreliable d_mountpoint() checks Ian Kent
2016-10-03  0:46 ` [RFC PATCH 7/8] autofs - use path_has_submounts() to fix unreliable have_submount() checks Ian Kent
2016-10-03  0:47 ` [RFC PATCH 8/8] vfs - remove unused have_submounts() function Ian Kent

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).