[PATCH] fs/namei.c: Call to file_permission() under a spinlock in do_lookup_path()
From: Trond Myklebust <Trond.Myklebust@netapp.com> We're presently running lock_kernel() under fs_lock via nfs's ->permission handler. That's a ranking bug and sometimes a sleep-in-spinlock bug. This problem was introduced in the openat() patchset. We should not need to hold the current->fs->lock for a codepath that doesn't use current->fs. [vsu@altlinux.ru: fix error path] Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Cc: Al Viro <viro@ftp.linux.org.uk> Signed-off-by: Sergey Vlasov <vsu@altlinux.ru> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
c7d2d28b98
commit
6d09bb627d
19
fs/namei.c
19
fs/namei.c
|
@ -1080,8 +1080,8 @@ static int fastcall do_path_lookup(int dfd, const char *name,
|
||||||
nd->flags = flags;
|
nd->flags = flags;
|
||||||
nd->depth = 0;
|
nd->depth = 0;
|
||||||
|
|
||||||
read_lock(¤t->fs->lock);
|
|
||||||
if (*name=='/') {
|
if (*name=='/') {
|
||||||
|
read_lock(¤t->fs->lock);
|
||||||
if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
|
if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
|
||||||
nd->mnt = mntget(current->fs->altrootmnt);
|
nd->mnt = mntget(current->fs->altrootmnt);
|
||||||
nd->dentry = dget(current->fs->altroot);
|
nd->dentry = dget(current->fs->altroot);
|
||||||
|
@ -1092,33 +1092,35 @@ static int fastcall do_path_lookup(int dfd, const char *name,
|
||||||
}
|
}
|
||||||
nd->mnt = mntget(current->fs->rootmnt);
|
nd->mnt = mntget(current->fs->rootmnt);
|
||||||
nd->dentry = dget(current->fs->root);
|
nd->dentry = dget(current->fs->root);
|
||||||
|
read_unlock(¤t->fs->lock);
|
||||||
} else if (dfd == AT_FDCWD) {
|
} else if (dfd == AT_FDCWD) {
|
||||||
|
read_lock(¤t->fs->lock);
|
||||||
nd->mnt = mntget(current->fs->pwdmnt);
|
nd->mnt = mntget(current->fs->pwdmnt);
|
||||||
nd->dentry = dget(current->fs->pwd);
|
nd->dentry = dget(current->fs->pwd);
|
||||||
|
read_unlock(¤t->fs->lock);
|
||||||
} else {
|
} else {
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
|
|
||||||
file = fget_light(dfd, &fput_needed);
|
file = fget_light(dfd, &fput_needed);
|
||||||
retval = -EBADF;
|
retval = -EBADF;
|
||||||
if (!file)
|
if (!file)
|
||||||
goto unlock_fail;
|
goto out_fail;
|
||||||
|
|
||||||
dentry = file->f_dentry;
|
dentry = file->f_dentry;
|
||||||
|
|
||||||
retval = -ENOTDIR;
|
retval = -ENOTDIR;
|
||||||
if (!S_ISDIR(dentry->d_inode->i_mode))
|
if (!S_ISDIR(dentry->d_inode->i_mode))
|
||||||
goto fput_unlock_fail;
|
goto fput_fail;
|
||||||
|
|
||||||
retval = file_permission(file, MAY_EXEC);
|
retval = file_permission(file, MAY_EXEC);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto fput_unlock_fail;
|
goto fput_fail;
|
||||||
|
|
||||||
nd->mnt = mntget(file->f_vfsmnt);
|
nd->mnt = mntget(file->f_vfsmnt);
|
||||||
nd->dentry = dget(dentry);
|
nd->dentry = dget(dentry);
|
||||||
|
|
||||||
fput_light(file, fput_needed);
|
fput_light(file, fput_needed);
|
||||||
}
|
}
|
||||||
read_unlock(¤t->fs->lock);
|
|
||||||
current->total_link_count = 0;
|
current->total_link_count = 0;
|
||||||
retval = link_path_walk(name, nd);
|
retval = link_path_walk(name, nd);
|
||||||
out:
|
out:
|
||||||
|
@ -1127,13 +1129,12 @@ out:
|
||||||
nd->dentry->d_inode))
|
nd->dentry->d_inode))
|
||||||
audit_inode(name, nd->dentry->d_inode, flags);
|
audit_inode(name, nd->dentry->d_inode, flags);
|
||||||
}
|
}
|
||||||
|
out_fail:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
fput_unlock_fail:
|
fput_fail:
|
||||||
fput_light(file, fput_needed);
|
fput_light(file, fput_needed);
|
||||||
unlock_fail:
|
goto out_fail;
|
||||||
read_unlock(¤t->fs->lock);
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int fastcall path_lookup(const char *name, unsigned int flags,
|
int fastcall path_lookup(const char *name, unsigned int flags,
|
||||||
|
|
Reference in New Issue