diff options
author | Ian Kent <raven@themaw.net> | 2011-03-24 13:51:02 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-24 14:24:32 -0400 |
commit | 62a7375e5d77d654695297c4b39d5d740d901184 (patch) | |
tree | b479c60a43f22e3bb99d9ebf1af89de2a7020673 /fs/namei.c | |
parent | b81a618dcd3ea99de292dbe624f41ca68f464376 (diff) |
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>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/fs/namei.c b/fs/namei.c index d0066e17d45d..3cb616d38d9c 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -992,6 +992,12 @@ int follow_down_one(struct path *path) | |||
992 | return 0; | 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 | * Skip to top of mountpoint pile in rcuwalk mode. We abort the rcu-walk if we | 1002 | * Skip to top of mountpoint pile in rcuwalk mode. We abort the rcu-walk if we |
997 | * meet a managed dentry and we're not walking to "..". True is returned to | 1003 | * meet a managed dentry and we're not walking to "..". True is returned to |
@@ -1000,19 +1006,26 @@ int follow_down_one(struct path *path) | |||
1000 | static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, | 1006 | static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, |
1001 | struct inode **inode, bool reverse_transit) | 1007 | struct inode **inode, bool reverse_transit) |
1002 | { | 1008 | { |
1003 | while (d_mountpoint(path->dentry)) { | 1009 | for (;;) { |
1004 | struct vfsmount *mounted; | 1010 | struct vfsmount *mounted; |
1005 | if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) && | 1011 | /* |
1006 | !reverse_transit && | 1012 | * Don't forget we might have a non-mountpoint managed dentry |
1007 | path->dentry->d_op->d_manage(path->dentry, true) < 0) | 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 | return false; | 1018 | return false; |
1019 | |||
1020 | if (!d_mountpoint(path->dentry)) | ||
1021 | break; | ||
1022 | |||
1009 | mounted = __lookup_mnt(path->mnt, path->dentry, 1); | 1023 | mounted = __lookup_mnt(path->mnt, path->dentry, 1); |
1010 | if (!mounted) | 1024 | if (!mounted) |
1011 | break; | 1025 | break; |
1012 | path->mnt = mounted; | 1026 | path->mnt = mounted; |
1013 | path->dentry = mounted->mnt_root; | 1027 | path->dentry = mounted->mnt_root; |
1014 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); | 1028 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); |
1015 | *inode = path->dentry->d_inode; | ||
1016 | } | 1029 | } |
1017 | 1030 | ||
1018 | if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT)) | 1031 | if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT)) |