Commit 62a7375e5d77d654695297c4b39d5d740d901184
Committed by
Al Viro
1 parent
b81a618dcd
Exists in
master
and in
4 other branches
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>
Showing 1 changed file with 18 additions and 5 deletions Side-by-side Diff
fs/namei.c
... | ... | @@ -992,6 +992,12 @@ |
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 |
998 | 1004 | |
999 | 1005 | |
1000 | 1006 | |
... | ... | @@ -1000,19 +1006,26 @@ |
1000 | 1006 | static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, |
1001 | 1007 | struct inode **inode, bool reverse_transit) |
1002 | 1008 | { |
1003 | - while (d_mountpoint(path->dentry)) { | |
1009 | + for (;;) { | |
1004 | 1010 | struct vfsmount *mounted; |
1005 | - if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) && | |
1006 | - !reverse_transit && | |
1007 | - path->dentry->d_op->d_manage(path->dentry, true) < 0) | |
1011 | + /* | |
1012 | + * Don't forget we might have a non-mountpoint managed dentry | |
1013 | + * that wants to block transit. | |
1014 | + */ | |
1015 | + *inode = path->dentry->d_inode; | |
1016 | + if (!reverse_transit && | |
1017 | + unlikely(managed_dentry_might_block(path->dentry))) | |
1008 | 1018 | return false; |
1019 | + | |
1020 | + if (!d_mountpoint(path->dentry)) | |
1021 | + break; | |
1022 | + | |
1009 | 1023 | mounted = __lookup_mnt(path->mnt, path->dentry, 1); |
1010 | 1024 | if (!mounted) |
1011 | 1025 | break; |
1012 | 1026 | path->mnt = mounted; |
1013 | 1027 | path->dentry = mounted->mnt_root; |
1014 | 1028 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); |
1015 | - *inode = path->dentry->d_inode; | |
1016 | 1029 | } |
1017 | 1030 | |
1018 | 1031 | if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT)) |