switch vmsplice_to_user() to copy_page_to_iter()

I've switched the sanity checks on iovec to rw_copy_check_uvector();
we might need to do a local analog, if any behaviour differences are
not actually bugfixes here...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2014-02-03 18:19:51 -05:00
parent 637b58c288
commit 6130f5315e
1 changed files with 23 additions and 91 deletions

View File

@ -1520,116 +1520,48 @@ static int get_iovec_page_array(const struct iovec __user *iov,
static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf, static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
struct splice_desc *sd) struct splice_desc *sd)
{ {
char *src; int n = copy_page_to_iter(buf->page, buf->offset, sd->len, sd->u.data);
int ret; return n == sd->len ? n : -EFAULT;
/*
* See if we can use the atomic maps, by prefaulting in the
* pages and doing an atomic copy
*/
if (!fault_in_pages_writeable(sd->u.userptr, sd->len)) {
src = kmap_atomic(buf->page);
ret = __copy_to_user_inatomic(sd->u.userptr, src + buf->offset,
sd->len);
kunmap_atomic(src);
if (!ret) {
ret = sd->len;
goto out;
}
}
/*
* No dice, use slow non-atomic map and copy
*/
src = kmap(buf->page);
ret = sd->len;
if (copy_to_user(sd->u.userptr, src + buf->offset, sd->len))
ret = -EFAULT;
kunmap(buf->page);
out:
if (ret > 0)
sd->u.userptr += ret;
return ret;
} }
/* /*
* For lack of a better implementation, implement vmsplice() to userspace * For lack of a better implementation, implement vmsplice() to userspace
* as a simple copy of the pipes pages to the user iov. * as a simple copy of the pipes pages to the user iov.
*/ */
static long vmsplice_to_user(struct file *file, const struct iovec __user *iov, static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov,
unsigned long nr_segs, unsigned int flags) unsigned long nr_segs, unsigned int flags)
{ {
struct pipe_inode_info *pipe; struct pipe_inode_info *pipe;
struct splice_desc sd; struct splice_desc sd;
ssize_t size;
int error;
long ret; long ret;
struct iovec iovstack[UIO_FASTIOV];
struct iovec *iov = iovstack;
struct iov_iter iter;
ssize_t count = 0;
pipe = get_pipe_info(file); pipe = get_pipe_info(file);
if (!pipe) if (!pipe)
return -EBADF; return -EBADF;
ret = rw_copy_check_uvector(READ, uiov, nr_segs,
ARRAY_SIZE(iovstack), iovstack, &iov);
if (ret <= 0)
return ret;
iov_iter_init(&iter, iov, nr_segs, count, 0);
sd.len = 0;
sd.total_len = count;
sd.flags = flags;
sd.u.data = &iter;
sd.pos = 0;
pipe_lock(pipe); pipe_lock(pipe);
ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
error = ret = 0;
while (nr_segs) {
void __user *base;
size_t len;
/*
* Get user address base and length for this iovec.
*/
error = get_user(base, &iov->iov_base);
if (unlikely(error))
break;
error = get_user(len, &iov->iov_len);
if (unlikely(error))
break;
/*
* Sanity check this iovec. 0 read succeeds.
*/
if (unlikely(!len))
break;
if (unlikely(!base)) {
error = -EFAULT;
break;
}
if (unlikely(!access_ok(VERIFY_WRITE, base, len))) {
error = -EFAULT;
break;
}
sd.len = 0;
sd.total_len = len;
sd.flags = flags;
sd.u.userptr = base;
sd.pos = 0;
size = __splice_from_pipe(pipe, &sd, pipe_to_user);
if (size < 0) {
if (!ret)
ret = size;
break;
}
ret += size;
if (size < len)
break;
nr_segs--;
iov++;
}
pipe_unlock(pipe); pipe_unlock(pipe);
if (!ret) if (iov != iovstack)
ret = error; kfree(iov);
return ret; return ret;
} }