[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>
This commit is contained in:
20
fs/namei.c
20
fs/namei.c
@@ -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 @@ do_last:
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user