When reconnecting to automounts at startup an autofs ioctl is used
to find the device and inode of existing mounts so they can be used
to open a file descriptor of possibly covered mounts.

At this time the the caller might not yet "own" the mount so it can
trigger calling ->d_automount(). This causes automount to hang when
trying to reconnect to direct or offset mount types.

Consequently kern_path() can't be used.

Signed-off-by: Ian Kent <ra...@themaw.net>
---
 fs/autofs4/dev-ioctl.c |   72 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 62 insertions(+), 10 deletions(-)

diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
index 743c7c2..1d24e42 100644
--- a/fs/autofs4/dev-ioctl.c
+++ b/fs/autofs4/dev-ioctl.c
@@ -183,13 +183,67 @@ static int autofs_dev_ioctl_protosubver(struct file *fp,
        return 0;
 }
 
+/*
+ * Lookup the the topmost path of a (possible) mount stack.
+ *
+ * kern_path() can't be used here because the caller might not
+ * "own" the automount dentry yet and we would end up calling
+ * back to ourself.
+ */
+static int kern_path_top(const char *pathname,
+                        unsigned int flags, struct path *path)
+{
+       struct dentry *dentry;
+       struct qstr name;
+       const char *tmp;
+       unsigned int len;
+       int err;
+
+       len = strlen(pathname);
+       if (len <= 1)
+               return -EINVAL;
+
+       tmp = pathname + len - 1;
+       len = 0;
+       if (*tmp == '/')
+               tmp--;
+       do {
+               if (*tmp == '/')
+                       break;
+               len++;
+       } while (--tmp >= pathname);
+       tmp++;
+
+       err = kern_path(pathname, flags | LOOKUP_PARENT, path);
+       if (err)
+               return err;
+
+       name.name = tmp;
+       name.len = len;
+       name.hash = full_name_hash(tmp, len);
+
+       dentry = d_lookup(path->dentry, &name);
+       if (!dentry) {
+               path_put(path);
+               return -ENOENT;
+       }
+       dput(path->dentry);
+       path->dentry = dentry;
+
+       while (follow_down_one(path))
+               ;
+
+       return 0;
+}
+
+/* Find the topmost mount satisfying test() */
 static int find_autofs_mount(const char *pathname,
                             struct path *res,
                             int test(struct path *path, void *data),
                             void *data)
 {
        struct path path;
-       int err = kern_path(pathname, 0, &path);
+       int err = kern_path_top(pathname, 0, &path);
        if (err)
                return err;
        err = -ENOENT;
@@ -197,10 +251,9 @@ static int find_autofs_mount(const char *pathname,
                if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) {
                        if (test(&path, data)) {
                                path_get(&path);
-                               if (!err) /* already found some */
-                                       path_put(res);
                                *res = path;
                                err = 0;
+                               break;
                        }
                }
                if (!follow_up(&path))
@@ -486,12 +539,11 @@ static int autofs_dev_ioctl_askumount(struct file *fp,
  * mount if there is one or 0 if it isn't a mountpoint.
  *
  * If we aren't supplied with a file descriptor then we
- * lookup the nameidata of the path and check if it is the
- * root of a mount. If a type is given we are looking for
- * a particular autofs mount and if we don't find a match
- * we return fail. If the located nameidata path is the
- * root of a mount we return 1 along with the super magic
- * of the mount or 0 otherwise.
+ * lookup the path and check if it is the root of a mount.
+ * If a type is given we are looking for a particular autofs
+ * mount and if we don't find a match we return fail. If the
+ * located path is the root of a mount we return 1 along with
+ * the super magic of the mount or 0 otherwise.
  *
  * In both cases the the device number (as returned by
  * new_encode_dev()) is also returned.
@@ -519,7 +571,7 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
 
        if (!fp || param->ioctlfd == -1) {
                if (autofs_type_any(type))
-                       err = kern_path(name, LOOKUP_FOLLOW, &path);
+                       err = kern_path_top(name, LOOKUP_FOLLOW, &path);
                else
                        err = find_autofs_mount(name, &path, test_by_type, 
&type);
                if (err)

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@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 via email to