|
@@ -812,6 +812,11 @@ static int follow_automount(struct path *path, unsigned flags,
|
|
|
if (!mnt) /* mount collision */
|
|
|
return 0;
|
|
|
|
|
|
+ if (!*need_mntput) {
|
|
|
+ /* lock_mount() may release path->mnt on error */
|
|
|
+ mntget(path->mnt);
|
|
|
+ *need_mntput = true;
|
|
|
+ }
|
|
|
err = finish_automount(mnt, path);
|
|
|
|
|
|
switch (err) {
|
|
@@ -819,12 +824,9 @@ static int follow_automount(struct path *path, unsigned flags,
|
|
|
/* Someone else made a mount here whilst we were busy */
|
|
|
return 0;
|
|
|
case 0:
|
|
|
- dput(path->dentry);
|
|
|
- if (*need_mntput)
|
|
|
- mntput(path->mnt);
|
|
|
+ path_put(path);
|
|
|
path->mnt = mnt;
|
|
|
path->dentry = dget(mnt->mnt_root);
|
|
|
- *need_mntput = true;
|
|
|
return 0;
|
|
|
default:
|
|
|
return err;
|
|
@@ -844,9 +846,10 @@ static int follow_automount(struct path *path, unsigned flags,
|
|
|
*/
|
|
|
static int follow_managed(struct path *path, unsigned flags)
|
|
|
{
|
|
|
+ struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */
|
|
|
unsigned managed;
|
|
|
bool need_mntput = false;
|
|
|
- int ret;
|
|
|
+ int ret = 0;
|
|
|
|
|
|
/* Given that we're not holding a lock here, we retain the value in a
|
|
|
* local variable for each dentry as we look at it so that we don't see
|
|
@@ -861,7 +864,7 @@ static int follow_managed(struct path *path, unsigned flags)
|
|
|
BUG_ON(!path->dentry->d_op->d_manage);
|
|
|
ret = path->dentry->d_op->d_manage(path->dentry, false);
|
|
|
if (ret < 0)
|
|
|
- return ret == -EISDIR ? 0 : ret;
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
/* Transit to a mounted filesystem. */
|
|
@@ -887,14 +890,19 @@ static int follow_managed(struct path *path, unsigned flags)
|
|
|
if (managed & DCACHE_NEED_AUTOMOUNT) {
|
|
|
ret = follow_automount(path, flags, &need_mntput);
|
|
|
if (ret < 0)
|
|
|
- return ret == -EISDIR ? 0 : ret;
|
|
|
+ break;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
/* We didn't change the current path point */
|
|
|
break;
|
|
|
}
|
|
|
- return 0;
|
|
|
+
|
|
|
+ if (need_mntput && path->mnt == mnt)
|
|
|
+ mntput(path->mnt);
|
|
|
+ if (ret == -EISDIR)
|
|
|
+ ret = 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
int follow_down_one(struct path *path)
|