Commit 463ffb2e9d39c2a3fd8c3c1d4a34e01f2078f972
Committed by
Linus Torvalds
1 parent
d671d5e514
Exists in
master
and in
20 other branches
[PATCH] namei fixes (9/19)
New helper: __follow_mount(struct path *path). Same as follow_mount(), except that we do *not* do mntput() after the first lookup_mnt(). IOW, original path->mnt stays pinned down. We also take care to do dput() before mntput() in the loop body (follow_mount() also needs that reordering, but that will be done later in the series). The following are equivalent, assuming that path.mnt == x: (1) follow_mount(&path.mnt, &path.dentry) (2) __follow_mount(&path); if (path->mnt != x) mntput(x); (3) if (__follow_mount(&path)) mntput(x); Callers of follow_mount() in __link_path_walk() converted to (2). Equivalent transformation + fix for too-late-mntput() race in __follow_mount() loop. 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>
Showing 1 changed file with 23 additions and 2 deletions Side-by-side Diff
fs/namei.c
... | ... | @@ -576,6 +576,23 @@ |
576 | 576 | /* no need for dcache_lock, as serialization is taken care in |
577 | 577 | * namespace.c |
578 | 578 | */ |
579 | +static int __follow_mount(struct path *path) | |
580 | +{ | |
581 | + int res = 0; | |
582 | + while (d_mountpoint(path->dentry)) { | |
583 | + struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry); | |
584 | + if (!mounted) | |
585 | + break; | |
586 | + dput(path->dentry); | |
587 | + if (res) | |
588 | + mntput(path->mnt); | |
589 | + path->mnt = mounted; | |
590 | + path->dentry = dget(mounted->mnt_root); | |
591 | + res = 1; | |
592 | + } | |
593 | + return res; | |
594 | +} | |
595 | + | |
579 | 596 | static int follow_mount(struct vfsmount **mnt, struct dentry **dentry) |
580 | 597 | { |
581 | 598 | int res = 0; |
... | ... | @@ -778,7 +795,9 @@ |
778 | 795 | if (err) |
779 | 796 | break; |
780 | 797 | /* Check mountpoints.. */ |
781 | - follow_mount(&next.mnt, &next.dentry); | |
798 | + __follow_mount(&next); | |
799 | + if (nd->mnt != next.mnt) | |
800 | + mntput(nd->mnt); | |
782 | 801 | |
783 | 802 | err = -ENOENT; |
784 | 803 | inode = next.dentry->d_inode; |
... | ... | @@ -836,7 +855,9 @@ |
836 | 855 | err = do_lookup(nd, &this, &next); |
837 | 856 | if (err) |
838 | 857 | break; |
839 | - follow_mount(&next.mnt, &next.dentry); | |
858 | + __follow_mount(&next); | |
859 | + if (nd->mnt != next.mnt) | |
860 | + mntput(nd->mnt); | |
840 | 861 | inode = next.dentry->d_inode; |
841 | 862 | if ((lookup_flags & LOOKUP_FOLLOW) |
842 | 863 | && inode && inode->i_op && inode->i_op->follow_link) { |