diff options
author | Tejun Heo <tj@kernel.org> | 2011-05-24 09:59:36 +0200 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2011-05-24 09:59:36 +0200 |
commit | 6988f20fe04e9ef3aea488cb8ab57fbeb78e12f0 (patch) | |
tree | c9d7fc50a2e2147a5ca07e3096e7eeb916ad2da9 /fs/namei.c | |
parent | 0415b00d175e0d8945e6785aad21b5f157976ce0 (diff) | |
parent | 6ea0c34dac89611126455537552cffe6c7e832ad (diff) |
Merge branch 'fixes-2.6.39' into for-2.6.40
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 d0066e17d45..3cb616d38d9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -992,6 +992,12 @@ int follow_down_one(struct path *path) return 0; } +static inline bool managed_dentry_might_block(struct dentry *dentry) +{ + return (dentry->d_flags & DCACHE_MANAGE_TRANSIT && + dentry->d_op->d_manage(dentry, true) < 0); +} + /* * Skip to top of mountpoint pile in rcuwalk mode. We abort the rcu-walk if we * 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) static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, struct inode **inode, bool reverse_transit) { - while (d_mountpoint(path->dentry)) { + for (;;) { struct vfsmount *mounted; - if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) && - !reverse_transit && - path->dentry->d_op->d_manage(path->dentry, true) < 0) + /* + * Don't forget we might have a non-mountpoint managed dentry + * that wants to block transit. + */ + *inode = path->dentry->d_inode; + if (!reverse_transit && + unlikely(managed_dentry_might_block(path->dentry))) return false; + + if (!d_mountpoint(path->dentry)) + break; + mounted = __lookup_mnt(path->mnt, path->dentry, 1); if (!mounted) break; path->mnt = mounted; path->dentry = mounted->mnt_root; nd->seq = read_seqcount_begin(&path->dentry->d_seq); - *inode = path->dentry->d_inode; } if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT)) |