Commit d41efb52 authored by Al Viro's avatar Al Viro

fs/namei.c: pull positivity check into follow_managed()

There are 4 callers; two proceed to check if result is positive and
fail with ENOENT if it isn't; one (in handle_lookup_down()) is
guaranteed to yield positive and one (in lookup_fast()) is _preceded_
by positivity check.

However, follow_managed() on a negative dentry is a (fairly cheap)
no-op on anything other than autofs.  And negative autofs dentries
are never hashed, so lookup_fast() is not going to run into one
of those.  Moreover, successful follow_managed() on a _positive_
dentry never yields a negative one (and we significantly rely upon
that in callers of lookup_fast()).

In other words, we can easily transpose the positivity check and
the call of follow_managed() in lookup_fast().  And that allows
to fold the positivity check *into* follow_managed(), simplifying
life for the code downstream of its calls.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 3e5aeec0
...@@ -1206,25 +1206,25 @@ static int follow_automount(struct path *path, struct nameidata *nd, ...@@ -1206,25 +1206,25 @@ static int follow_automount(struct path *path, struct nameidata *nd,
* - Flagged as automount point * - Flagged as automount point
* *
* This may only be called in refwalk mode. * This may only be called in refwalk mode.
* On success path->dentry is known positive.
* *
* Serialization is taken care of in namespace.c * Serialization is taken care of in namespace.c
*/ */
static int follow_managed(struct path *path, struct nameidata *nd) static int follow_managed(struct path *path, struct nameidata *nd)
{ {
struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */ struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */
unsigned managed; unsigned flags;
bool need_mntput = false; bool need_mntput = false;
int ret = 0; int ret = 0;
/* Given that we're not holding a lock here, we retain the value in a /* 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 * local variable for each dentry as we look at it so that we don't see
* the components of that value change under us */ * the components of that value change under us */
while (managed = READ_ONCE(path->dentry->d_flags), while (flags = READ_ONCE(path->dentry->d_flags),
managed &= DCACHE_MANAGED_DENTRY, unlikely(flags & DCACHE_MANAGED_DENTRY)) {
unlikely(managed != 0)) {
/* Allow the filesystem to manage the transit without i_mutex /* Allow the filesystem to manage the transit without i_mutex
* being held. */ * being held. */
if (managed & DCACHE_MANAGE_TRANSIT) { if (flags & DCACHE_MANAGE_TRANSIT) {
BUG_ON(!path->dentry->d_op); BUG_ON(!path->dentry->d_op);
BUG_ON(!path->dentry->d_op->d_manage); BUG_ON(!path->dentry->d_op->d_manage);
ret = path->dentry->d_op->d_manage(path, false); ret = path->dentry->d_op->d_manage(path, false);
...@@ -1233,7 +1233,7 @@ static int follow_managed(struct path *path, struct nameidata *nd) ...@@ -1233,7 +1233,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
} }
/* Transit to a mounted filesystem. */ /* Transit to a mounted filesystem. */
if (managed & DCACHE_MOUNTED) { if (flags & DCACHE_MOUNTED) {
struct vfsmount *mounted = lookup_mnt(path); struct vfsmount *mounted = lookup_mnt(path);
if (mounted) { if (mounted) {
dput(path->dentry); dput(path->dentry);
...@@ -1252,7 +1252,7 @@ static int follow_managed(struct path *path, struct nameidata *nd) ...@@ -1252,7 +1252,7 @@ static int follow_managed(struct path *path, struct nameidata *nd)
} }
/* Handle an automount point */ /* Handle an automount point */
if (managed & DCACHE_NEED_AUTOMOUNT) { if (flags & DCACHE_NEED_AUTOMOUNT) {
ret = follow_automount(path, nd, &need_mntput); ret = follow_automount(path, nd, &need_mntput);
if (ret < 0) if (ret < 0)
break; break;
...@@ -1265,10 +1265,12 @@ static int follow_managed(struct path *path, struct nameidata *nd) ...@@ -1265,10 +1265,12 @@ static int follow_managed(struct path *path, struct nameidata *nd)
if (need_mntput && path->mnt == mnt) if (need_mntput && path->mnt == mnt)
mntput(path->mnt); mntput(path->mnt);
if (ret == -EISDIR || !ret)
ret = 1;
if (need_mntput) if (need_mntput)
nd->flags |= LOOKUP_JUMPED; nd->flags |= LOOKUP_JUMPED;
if (ret == -EISDIR || !ret)
ret = 1;
if (ret > 0 && unlikely(d_flags_negative(flags)))
ret = -ENOENT;
if (unlikely(ret < 0)) if (unlikely(ret < 0))
path_put_conditional(path, nd); path_put_conditional(path, nd);
return ret; return ret;
...@@ -1617,10 +1619,6 @@ static int lookup_fast(struct nameidata *nd, ...@@ -1617,10 +1619,6 @@ static int lookup_fast(struct nameidata *nd,
dput(dentry); dput(dentry);
return status; return status;
} }
if (unlikely(d_is_negative(dentry))) {
dput(dentry);
return -ENOENT;
}
path->mnt = mnt; path->mnt = mnt;
path->dentry = dentry; path->dentry = dentry;
...@@ -1807,11 +1805,6 @@ static int walk_component(struct nameidata *nd, int flags) ...@@ -1807,11 +1805,6 @@ static int walk_component(struct nameidata *nd, int flags)
if (unlikely(err < 0)) if (unlikely(err < 0))
return err; return err;
if (unlikely(d_is_negative(path.dentry))) {
path_to_nameidata(&path, nd);
return -ENOENT;
}
seq = 0; /* we are already out of RCU mode */ seq = 0; /* we are already out of RCU mode */
inode = d_backing_inode(path.dentry); inode = d_backing_inode(path.dentry);
} }
...@@ -3352,11 +3345,6 @@ static int do_last(struct nameidata *nd, ...@@ -3352,11 +3345,6 @@ static int do_last(struct nameidata *nd,
if (unlikely(error < 0)) if (unlikely(error < 0))
return error; return error;
if (unlikely(d_is_negative(path.dentry))) {
path_to_nameidata(&path, nd);
return -ENOENT;
}
/* /*
* create/update audit record if it already exists. * create/update audit record if it already exists.
*/ */
......
...@@ -440,6 +440,11 @@ static inline bool d_is_negative(const struct dentry *dentry) ...@@ -440,6 +440,11 @@ static inline bool d_is_negative(const struct dentry *dentry)
return d_is_miss(dentry); return d_is_miss(dentry);
} }
static inline bool d_flags_negative(unsigned flags)
{
return (flags & DCACHE_ENTRY_TYPE) == DCACHE_MISS_TYPE;
}
static inline bool d_is_positive(const struct dentry *dentry) static inline bool d_is_positive(const struct dentry *dentry)
{ {
return !d_is_negative(dentry); return !d_is_negative(dentry);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment