Commit e13b210f authored by Al Viro's avatar Al Viro Committed by Linus Torvalds

[PATCH] namei fixes (10/19)

In open_namei(), __follow_down() loop turned into __follow_mount().
Instead of
	if we are on a mountpoint dentry
		if O_NOFOLLOW checks fail
			drop path.dentry
			drop nd
			return
		do equivalent of follow_mount(&path.mnt, &path.dentry)
		nd->mnt = path.mnt
we do
	if __follow_mount(path) had, indeed, traversed mountpoint
		/* now both nd->mnt and path.mnt are pinned down */
		if O_NOFOLLOW checks fail
			drop path.dentry
			drop path.mnt
			drop nd
			return
		mntput(nd->mnt)
		nd->mnt = path.mnt

Now __follow_down() can be folded into follow_down() - no other callers left.
We need to reorder dput()/mntput() there - same problem as in follow_mount().

Equivalent transformation + fix for a bug in O_NOFOLLOW handling - we used to
get -ELOOP if we had the same fs mounted on /foo and /bar, had something bound
on /bar/baz and tried to open /foo/baz with O_NOFOLLOW.  And fix of
too-early-mntput() race in follow_down()
Signed-off-by: default avatarAl Viro <viro@parcelfarce.linux.theplanet.co.uk>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 463ffb2e
...@@ -612,26 +612,21 @@ static int follow_mount(struct vfsmount **mnt, struct dentry **dentry) ...@@ -612,26 +612,21 @@ static int follow_mount(struct vfsmount **mnt, struct dentry **dentry)
/* no need for dcache_lock, as serialization is taken care in /* no need for dcache_lock, as serialization is taken care in
* namespace.c * namespace.c
*/ */
static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry) int follow_down(struct vfsmount **mnt, struct dentry **dentry)
{ {
struct vfsmount *mounted; struct vfsmount *mounted;
mounted = lookup_mnt(*mnt, *dentry); mounted = lookup_mnt(*mnt, *dentry);
if (mounted) { if (mounted) {
dput(*dentry);
mntput(*mnt); mntput(*mnt);
*mnt = mounted; *mnt = mounted;
dput(*dentry);
*dentry = dget(mounted->mnt_root); *dentry = dget(mounted->mnt_root);
return 1; return 1;
} }
return 0; return 0;
} }
int follow_down(struct vfsmount **mnt, struct dentry **dentry)
{
return __follow_down(mnt,dentry);
}
static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry) static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry)
{ {
while(1) { while(1) {
...@@ -1498,11 +1493,14 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd) ...@@ -1498,11 +1493,14 @@ int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
if (flag & O_EXCL) if (flag & O_EXCL)
goto exit_dput; goto exit_dput;
if (d_mountpoint(path.dentry)) { if (__follow_mount(&path)) {
error = -ELOOP; error = -ELOOP;
if (flag & O_NOFOLLOW) if (flag & O_NOFOLLOW) {
goto exit_dput; dput(path.dentry);
while (__follow_down(&path.mnt,&path.dentry) && d_mountpoint(path.dentry)); mntput(path.mnt);
goto exit;
}
mntput(nd->mnt);
nd->mnt = path.mnt; nd->mnt = path.mnt;
} }
error = -ENOENT; error = -ENOENT;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment