diff options
author | Al Viro <viro@www.linux.org.uk> | 2005-06-06 16:36:06 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-06 17:42:26 -0400 |
commit | e13b210f6f7bdc44dfee0a9bbd633a32db0d6333 (patch) | |
tree | f957ec2fc24f8a9eb84dcffe76341262c1acedf0 /fs | |
parent | 463ffb2e9d39c2a3fd8c3c1d4a34e01f2078f972 (diff) |
[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: Al Viro <viro@parcelfarce.linux.theplanet.co.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namei.c | 20 |
1 files changed, 9 insertions, 11 deletions
diff --git a/fs/namei.c b/fs/namei.c index 23a1ad467976..935b08d8dcd8 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -612,26 +612,21 @@ static int follow_mount(struct vfsmount **mnt, struct dentry **dentry) | |||
612 | /* no need for dcache_lock, as serialization is taken care in | 612 | /* no need for dcache_lock, as serialization is taken care in |
613 | * namespace.c | 613 | * namespace.c |
614 | */ | 614 | */ |
615 | static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry) | 615 | int follow_down(struct vfsmount **mnt, struct dentry **dentry) |
616 | { | 616 | { |
617 | struct vfsmount *mounted; | 617 | struct vfsmount *mounted; |
618 | 618 | ||
619 | mounted = lookup_mnt(*mnt, *dentry); | 619 | mounted = lookup_mnt(*mnt, *dentry); |
620 | if (mounted) { | 620 | if (mounted) { |
621 | dput(*dentry); | ||
621 | mntput(*mnt); | 622 | mntput(*mnt); |
622 | *mnt = mounted; | 623 | *mnt = mounted; |
623 | dput(*dentry); | ||
624 | *dentry = dget(mounted->mnt_root); | 624 | *dentry = dget(mounted->mnt_root); |
625 | return 1; | 625 | return 1; |
626 | } | 626 | } |
627 | return 0; | 627 | return 0; |
628 | } | 628 | } |
629 | 629 | ||
630 | int follow_down(struct vfsmount **mnt, struct dentry **dentry) | ||
631 | { | ||
632 | return __follow_down(mnt,dentry); | ||
633 | } | ||
634 | |||
635 | static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry) | 630 | static inline void follow_dotdot(struct vfsmount **mnt, struct dentry **dentry) |
636 | { | 631 | { |
637 | while(1) { | 632 | while(1) { |
@@ -1498,11 +1493,14 @@ do_last: | |||
1498 | if (flag & O_EXCL) | 1493 | if (flag & O_EXCL) |
1499 | goto exit_dput; | 1494 | goto exit_dput; |
1500 | 1495 | ||
1501 | if (d_mountpoint(path.dentry)) { | 1496 | if (__follow_mount(&path)) { |
1502 | error = -ELOOP; | 1497 | error = -ELOOP; |
1503 | if (flag & O_NOFOLLOW) | 1498 | if (flag & O_NOFOLLOW) { |
1504 | goto exit_dput; | 1499 | dput(path.dentry); |
1505 | while (__follow_down(&path.mnt,&path.dentry) && d_mountpoint(path.dentry)); | 1500 | mntput(path.mnt); |
1501 | goto exit; | ||
1502 | } | ||
1503 | mntput(nd->mnt); | ||
1506 | nd->mnt = path.mnt; | 1504 | nd->mnt = path.mnt; |
1507 | } | 1505 | } |
1508 | error = -ENOENT; | 1506 | error = -ENOENT; |