vfs - check non-mountpoint dentry might block in __follow_mount_rcu()

When following a mount in rcu-walk mode we must check if the incoming dentry
is telling us it may need to block, even if it isn't actually a mountpoint.

Signed-off-by: Ian Kent <raven@themaw.net>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

authored by Ian Kent and committed by Al Viro 62a7375e b81a618d

+18 -5
+18 -5
fs/namei.c
··· 992 992 return 0; 993 993 } 994 994 995 + static inline bool managed_dentry_might_block(struct dentry *dentry) 996 + { 997 + return (dentry->d_flags & DCACHE_MANAGE_TRANSIT && 998 + dentry->d_op->d_manage(dentry, true) < 0); 999 + } 1000 + 995 1001 /* 996 1002 * Skip to top of mountpoint pile in rcuwalk mode. We abort the rcu-walk if we 997 1003 * meet a managed dentry and we're not walking to "..". True is returned to ··· 1006 1000 static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, 1007 1001 struct inode **inode, bool reverse_transit) 1008 1002 { 1009 - while (d_mountpoint(path->dentry)) { 1003 + for (;;) { 1010 1004 struct vfsmount *mounted; 1011 - if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) && 1012 - !reverse_transit && 1013 - path->dentry->d_op->d_manage(path->dentry, true) < 0) 1005 + /* 1006 + * Don't forget we might have a non-mountpoint managed dentry 1007 + * that wants to block transit. 1008 + */ 1009 + *inode = path->dentry->d_inode; 1010 + if (!reverse_transit && 1011 + unlikely(managed_dentry_might_block(path->dentry))) 1014 1012 return false; 1013 + 1014 + if (!d_mountpoint(path->dentry)) 1015 + break; 1016 + 1015 1017 mounted = __lookup_mnt(path->mnt, path->dentry, 1); 1016 1018 if (!mounted) 1017 1019 break; 1018 1020 path->mnt = mounted; 1019 1021 path->dentry = mounted->mnt_root; 1020 1022 nd->seq = read_seqcount_begin(&path->dentry->d_seq); 1021 - *inode = path->dentry->d_inode; 1022 1023 } 1023 1024 1024 1025 if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))