vfs: add path_has_submounts()
authorIan Kent <ikent@redhat.com>
Wed, 23 Nov 2016 21:03:41 +0000 (08:03 +1100)
committerAl Viro <viro@zeniv.linux.org.uk>
Sun, 4 Dec 2016 01:51:47 +0000 (20:51 -0500)
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.

Link: http://lkml.kernel.org/r/20161011053403.27645.55242.stgit@pluto.themaw.net
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>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/dcache.c
include/linux/dcache.h

index 5c7cc953ac8191d80bc899f5bb510e9d71d7d69d..8515875854b6dd91907e39e5c8b7b4773b6876b1 100644 (file)
@@ -1306,6 +1306,45 @@ 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 (likely(!d_mountpoint(dentry)))
+               return D_WALK_CONTINUE;
+       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(const struct path *parent)
+{
+       struct check_mount data = { .mnt = parent->mnt, .mounted = 0 };
+
+       read_seqlock_excl(&mount_lock);
+       d_walk(parent->dentry, &data, path_check_mount, NULL);
+       read_sequnlock_excl(&mount_lock);
+
+       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
index bc529ce88ed0924f49c25c5fbc34fa655b5590f2..0ffaf7aef9aece9a99a01973ca219e521d0a7289 100644 (file)
@@ -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(const struct path *);
 
 /*
  * This adds the entry to the hash queues.