Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs fixes from Al Viro: "stable fodder; assorted deadlock fixes" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: vt: synchronize_rcu() under spinlock is not nice... Nest rename_lock inside vfsmount_lock Don't bother with redoing rw_verify_area() from default_file_splice_from()
This commit is contained in:
@@ -93,7 +93,7 @@ vcs_poll_data_free(struct vcs_poll_data *poll)
|
|||||||
static struct vcs_poll_data *
|
static struct vcs_poll_data *
|
||||||
vcs_poll_data_get(struct file *file)
|
vcs_poll_data_get(struct file *file)
|
||||||
{
|
{
|
||||||
struct vcs_poll_data *poll = file->private_data;
|
struct vcs_poll_data *poll = file->private_data, *kill = NULL;
|
||||||
|
|
||||||
if (poll)
|
if (poll)
|
||||||
return poll;
|
return poll;
|
||||||
@@ -122,10 +122,12 @@ vcs_poll_data_get(struct file *file)
|
|||||||
file->private_data = poll;
|
file->private_data = poll;
|
||||||
} else {
|
} else {
|
||||||
/* someone else raced ahead of us */
|
/* someone else raced ahead of us */
|
||||||
vcs_poll_data_free(poll);
|
kill = poll;
|
||||||
poll = file->private_data;
|
poll = file->private_data;
|
||||||
}
|
}
|
||||||
spin_unlock(&file->f_lock);
|
spin_unlock(&file->f_lock);
|
||||||
|
if (kill)
|
||||||
|
vcs_poll_data_free(kill);
|
||||||
|
|
||||||
return poll;
|
return poll;
|
||||||
}
|
}
|
||||||
|
16
fs/dcache.c
16
fs/dcache.c
@@ -2542,7 +2542,6 @@ static int prepend_path(const struct path *path,
|
|||||||
bool slash = false;
|
bool slash = false;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
br_read_lock(&vfsmount_lock);
|
|
||||||
while (dentry != root->dentry || vfsmnt != root->mnt) {
|
while (dentry != root->dentry || vfsmnt != root->mnt) {
|
||||||
struct dentry * parent;
|
struct dentry * parent;
|
||||||
|
|
||||||
@@ -2572,8 +2571,6 @@ static int prepend_path(const struct path *path,
|
|||||||
if (!error && !slash)
|
if (!error && !slash)
|
||||||
error = prepend(buffer, buflen, "/", 1);
|
error = prepend(buffer, buflen, "/", 1);
|
||||||
|
|
||||||
out:
|
|
||||||
br_read_unlock(&vfsmount_lock);
|
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
global_root:
|
global_root:
|
||||||
@@ -2590,7 +2587,7 @@ global_root:
|
|||||||
error = prepend(buffer, buflen, "/", 1);
|
error = prepend(buffer, buflen, "/", 1);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = is_mounted(vfsmnt) ? 1 : 2;
|
error = is_mounted(vfsmnt) ? 1 : 2;
|
||||||
goto out;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2617,9 +2614,11 @@ char *__d_path(const struct path *path,
|
|||||||
int error;
|
int error;
|
||||||
|
|
||||||
prepend(&res, &buflen, "\0", 1);
|
prepend(&res, &buflen, "\0", 1);
|
||||||
|
br_read_lock(&vfsmount_lock);
|
||||||
write_seqlock(&rename_lock);
|
write_seqlock(&rename_lock);
|
||||||
error = prepend_path(path, root, &res, &buflen);
|
error = prepend_path(path, root, &res, &buflen);
|
||||||
write_sequnlock(&rename_lock);
|
write_sequnlock(&rename_lock);
|
||||||
|
br_read_unlock(&vfsmount_lock);
|
||||||
|
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
return ERR_PTR(error);
|
return ERR_PTR(error);
|
||||||
@@ -2636,9 +2635,11 @@ char *d_absolute_path(const struct path *path,
|
|||||||
int error;
|
int error;
|
||||||
|
|
||||||
prepend(&res, &buflen, "\0", 1);
|
prepend(&res, &buflen, "\0", 1);
|
||||||
|
br_read_lock(&vfsmount_lock);
|
||||||
write_seqlock(&rename_lock);
|
write_seqlock(&rename_lock);
|
||||||
error = prepend_path(path, &root, &res, &buflen);
|
error = prepend_path(path, &root, &res, &buflen);
|
||||||
write_sequnlock(&rename_lock);
|
write_sequnlock(&rename_lock);
|
||||||
|
br_read_unlock(&vfsmount_lock);
|
||||||
|
|
||||||
if (error > 1)
|
if (error > 1)
|
||||||
error = -EINVAL;
|
error = -EINVAL;
|
||||||
@@ -2702,11 +2703,13 @@ char *d_path(const struct path *path, char *buf, int buflen)
|
|||||||
return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
|
return path->dentry->d_op->d_dname(path->dentry, buf, buflen);
|
||||||
|
|
||||||
get_fs_root(current->fs, &root);
|
get_fs_root(current->fs, &root);
|
||||||
|
br_read_lock(&vfsmount_lock);
|
||||||
write_seqlock(&rename_lock);
|
write_seqlock(&rename_lock);
|
||||||
error = path_with_deleted(path, &root, &res, &buflen);
|
error = path_with_deleted(path, &root, &res, &buflen);
|
||||||
|
write_sequnlock(&rename_lock);
|
||||||
|
br_read_unlock(&vfsmount_lock);
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
res = ERR_PTR(error);
|
res = ERR_PTR(error);
|
||||||
write_sequnlock(&rename_lock);
|
|
||||||
path_put(&root);
|
path_put(&root);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -2830,6 +2833,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
|
|||||||
get_fs_root_and_pwd(current->fs, &root, &pwd);
|
get_fs_root_and_pwd(current->fs, &root, &pwd);
|
||||||
|
|
||||||
error = -ENOENT;
|
error = -ENOENT;
|
||||||
|
br_read_lock(&vfsmount_lock);
|
||||||
write_seqlock(&rename_lock);
|
write_seqlock(&rename_lock);
|
||||||
if (!d_unlinked(pwd.dentry)) {
|
if (!d_unlinked(pwd.dentry)) {
|
||||||
unsigned long len;
|
unsigned long len;
|
||||||
@@ -2839,6 +2843,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
|
|||||||
prepend(&cwd, &buflen, "\0", 1);
|
prepend(&cwd, &buflen, "\0", 1);
|
||||||
error = prepend_path(&pwd, &root, &cwd, &buflen);
|
error = prepend_path(&pwd, &root, &cwd, &buflen);
|
||||||
write_sequnlock(&rename_lock);
|
write_sequnlock(&rename_lock);
|
||||||
|
br_read_unlock(&vfsmount_lock);
|
||||||
|
|
||||||
if (error < 0)
|
if (error < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -2859,6 +2864,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
write_sequnlock(&rename_lock);
|
write_sequnlock(&rename_lock);
|
||||||
|
br_read_unlock(&vfsmount_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@@ -125,3 +125,8 @@ extern int invalidate_inodes(struct super_block *, bool);
|
|||||||
* dcache.c
|
* dcache.c
|
||||||
*/
|
*/
|
||||||
extern struct dentry *__d_alloc(struct super_block *, const struct qstr *);
|
extern struct dentry *__d_alloc(struct super_block *, const struct qstr *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* read_write.c
|
||||||
|
*/
|
||||||
|
extern ssize_t __kernel_write(struct file *, const char *, size_t, loff_t *);
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
#include <linux/splice.h>
|
#include <linux/splice.h>
|
||||||
#include <linux/compat.h>
|
#include <linux/compat.h>
|
||||||
#include "read_write.h"
|
#include "read_write.h"
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
@@ -417,6 +418,30 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof
|
|||||||
|
|
||||||
EXPORT_SYMBOL(do_sync_write);
|
EXPORT_SYMBOL(do_sync_write);
|
||||||
|
|
||||||
|
ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos)
|
||||||
|
{
|
||||||
|
mm_segment_t old_fs;
|
||||||
|
const char __user *p;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
old_fs = get_fs();
|
||||||
|
set_fs(get_ds());
|
||||||
|
p = (__force const char __user *)buf;
|
||||||
|
if (count > MAX_RW_COUNT)
|
||||||
|
count = MAX_RW_COUNT;
|
||||||
|
if (file->f_op->write)
|
||||||
|
ret = file->f_op->write(file, p, count, pos);
|
||||||
|
else
|
||||||
|
ret = do_sync_write(file, p, count, pos);
|
||||||
|
set_fs(old_fs);
|
||||||
|
if (ret > 0) {
|
||||||
|
fsnotify_modify(file);
|
||||||
|
add_wchar(current, ret);
|
||||||
|
}
|
||||||
|
inc_syscw(current);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
|
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
|
||||||
{
|
{
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/socket.h>
|
#include <linux/socket.h>
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attempt to steal a page from a pipe buffer. This should perhaps go into
|
* Attempt to steal a page from a pipe buffer. This should perhaps go into
|
||||||
@@ -1048,9 +1049,10 @@ static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
void *data;
|
void *data;
|
||||||
|
loff_t tmp = sd->pos;
|
||||||
|
|
||||||
data = buf->ops->map(pipe, buf, 0);
|
data = buf->ops->map(pipe, buf, 0);
|
||||||
ret = kernel_write(sd->u.file, data + buf->offset, sd->len, sd->pos);
|
ret = __kernel_write(sd->u.file, data + buf->offset, sd->len, &tmp);
|
||||||
buf->ops->unmap(pipe, buf, data);
|
buf->ops->unmap(pipe, buf, data);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
Reference in New Issue
Block a user