VFS: allow ->d_manage() to declare -EISDIR in rcu_walk mode.
In REF-walk mode, ->d_manage can return -EISDIR to indicate that the dentry is not really a mount trap (or even a mount point) and that any mounts or any DCACHE_NEED_AUTOMOUNT flag should be ignored. RCU-walk mode doesn't currently support this, so if there is a dentry with DCACHE_NEED_AUTOMOUNT set but which shouldn't be a mount-trap, lookup_fast() will always drop in REF-walk mode. With this patch, an -EISDIR from ->d_manage will always cause mounts and automounts to be ignored, both in REF-walk and RCU-walk. Bug-fixed-by: Dan Carpenter <dan.carpenter@oracle.com> Cc: Ian Kent <raven@themaw.net> Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
7c33d5972c
commit
b8faf035ea
|
@ -1053,7 +1053,8 @@ struct dentry_operations {
|
|||
If the 'rcu_walk' parameter is true, then the caller is doing a
|
||||
pathwalk in RCU-walk mode. Sleeping is not permitted in this mode,
|
||||
and the caller can be asked to leave it and call again by returning
|
||||
-ECHILD.
|
||||
-ECHILD. -EISDIR may also be returned to tell pathwalk to
|
||||
ignore d_automount or any mounts.
|
||||
|
||||
This function is only used if DCACHE_MANAGE_TRANSIT is set on the
|
||||
dentry being transited from.
|
||||
|
|
25
fs/namei.c
25
fs/namei.c
|
@ -1091,10 +1091,10 @@ int follow_down_one(struct path *path)
|
|||
}
|
||||
EXPORT_SYMBOL(follow_down_one);
|
||||
|
||||
static inline bool managed_dentry_might_block(struct dentry *dentry)
|
||||
static inline int managed_dentry_rcu(struct dentry *dentry)
|
||||
{
|
||||
return (dentry->d_flags & DCACHE_MANAGE_TRANSIT &&
|
||||
dentry->d_op->d_manage(dentry, true) < 0);
|
||||
return (dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
|
||||
dentry->d_op->d_manage(dentry, true) : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1110,11 +1110,18 @@ 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.
|
||||
*/
|
||||
if (unlikely(managed_dentry_might_block(path->dentry)))
|
||||
switch (managed_dentry_rcu(path->dentry)) {
|
||||
case -ECHILD:
|
||||
default:
|
||||
return false;
|
||||
case -EISDIR:
|
||||
return true;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!d_mountpoint(path->dentry))
|
||||
return true;
|
||||
return !(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);
|
||||
|
||||
mounted = __lookup_mnt(path->mnt, path->dentry);
|
||||
if (!mounted)
|
||||
|
@ -1130,7 +1137,8 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
|
|||
*/
|
||||
*inode = path->dentry->d_inode;
|
||||
}
|
||||
return read_seqretry(&mount_lock, nd->m_seq);
|
||||
return read_seqretry(&mount_lock, nd->m_seq) &&
|
||||
!(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);
|
||||
}
|
||||
|
||||
static int follow_dotdot_rcu(struct nameidata *nd)
|
||||
|
@ -1402,10 +1410,7 @@ static int lookup_fast(struct nameidata *nd,
|
|||
}
|
||||
path->mnt = mnt;
|
||||
path->dentry = dentry;
|
||||
if (unlikely(!__follow_mount_rcu(nd, path, inode)))
|
||||
goto unlazy;
|
||||
if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
|
||||
goto unlazy;
|
||||
if (likely(__follow_mount_rcu(nd, path, inode)))
|
||||
return 0;
|
||||
unlazy:
|
||||
if (unlazy_walk(nd, dentry))
|
||||
|
|
Loading…
Reference in New Issue