[PATCH] namei fixes (10/19)
authorAl Viro <viro@www.linux.org.uk>
Mon, 6 Jun 2005 20:36:06 +0000 (13:36 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Mon, 6 Jun 2005 21:42:26 +0000 (14:42 -0700)
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: Al Viro <viro@parcelfarce.linux.theplanet.co.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/namei.c

index 23a1ad4..935b08d 100644 (file)
@@ -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
  * 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;
 
        mounted = lookup_mnt(*mnt, *dentry);
        if (mounted) {
+               dput(*dentry);
                mntput(*mnt);
                *mnt = mounted;
-               dput(*dentry);
                *dentry = dget(mounted->mnt_root);
                return 1;
        }
        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)
 {
        while(1) {
@@ -1498,11 +1493,14 @@ do_last:
        if (flag & O_EXCL)
                goto exit_dput;
 
-       if (d_mountpoint(path.dentry)) {
+       if (__follow_mount(&path)) {
                error = -ELOOP;
-               if (flag & O_NOFOLLOW)
-                       goto exit_dput;
-               while (__follow_down(&path.mnt,&path.dentry) && d_mountpoint(path.dentry));
+               if (flag & O_NOFOLLOW) {
+                       dput(path.dentry);
+                       mntput(path.mnt);
+                       goto exit;
+               }
+               mntput(nd->mnt);
                nd->mnt = path.mnt;
        }
        error = -ENOENT;