Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux.git] / fs / splice.c
index 12028fa41def9c007b0217b6c4bc481868d93792..9bc07d2b53cf3a0e66605912386f9d2367601585 100644 (file)
@@ -136,8 +136,6 @@ error:
 
 const struct pipe_buf_operations page_cache_pipe_buf_ops = {
        .can_merge = 0,
-       .map = generic_pipe_buf_map,
-       .unmap = generic_pipe_buf_unmap,
        .confirm = page_cache_pipe_buf_confirm,
        .release = page_cache_pipe_buf_release,
        .steal = page_cache_pipe_buf_steal,
@@ -156,8 +154,6 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe,
 
 static const struct pipe_buf_operations user_page_pipe_buf_ops = {
        .can_merge = 0,
-       .map = generic_pipe_buf_map,
-       .unmap = generic_pipe_buf_unmap,
        .confirm = generic_pipe_buf_confirm,
        .release = page_cache_pipe_buf_release,
        .steal = user_page_pipe_buf_steal,
@@ -547,8 +543,6 @@ EXPORT_SYMBOL(generic_file_splice_read);
 
 static const struct pipe_buf_operations default_pipe_buf_ops = {
        .can_merge = 0,
-       .map = generic_pipe_buf_map,
-       .unmap = generic_pipe_buf_unmap,
        .confirm = generic_pipe_buf_confirm,
        .release = generic_pipe_buf_release,
        .steal = generic_pipe_buf_steal,
@@ -564,8 +558,6 @@ static int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe,
 /* Pipe buffer operations for a socket and similar. */
 const struct pipe_buf_operations nosteal_pipe_buf_ops = {
        .can_merge = 0,
-       .map = generic_pipe_buf_map,
-       .unmap = generic_pipe_buf_unmap,
        .confirm = generic_pipe_buf_confirm,
        .release = generic_pipe_buf_release,
        .steal = generic_pipe_buf_nosteal,
@@ -767,13 +759,13 @@ int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
                goto out;
 
        if (buf->page != page) {
-               char *src = buf->ops->map(pipe, buf, 1);
+               char *src = kmap_atomic(buf->page);
                char *dst = kmap_atomic(page);
 
                memcpy(dst + offset, src + buf->offset, this_len);
                flush_dcache_page(page);
                kunmap_atomic(dst);
-               buf->ops->unmap(pipe, buf, src);
+               kunmap_atomic(src);
        }
        ret = pagecache_write_end(file, mapping, sd->pos, this_len, this_len,
                                page, fsdata);
@@ -1067,9 +1059,9 @@ static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
        void *data;
        loff_t tmp = sd->pos;
 
-       data = buf->ops->map(pipe, buf, 0);
+       data = kmap(buf->page);
        ret = __kernel_write(sd->u.file, data + buf->offset, sd->len, &tmp);
-       buf->ops->unmap(pipe, buf, data);
+       kunmap(buf->page);
 
        return ret;
 }
@@ -1528,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,
                        struct splice_desc *sd)
 {
-       char *src;
-       int ret;
-
-       /*
-        * 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 = buf->ops->map(pipe, buf, 1);
-               ret = __copy_to_user_inatomic(sd->u.userptr, src + buf->offset,
-                                                       sd->len);
-               buf->ops->unmap(pipe, buf, src);
-               if (!ret) {
-                       ret = sd->len;
-                       goto out;
-               }
-       }
-
-       /*
-        * No dice, use slow non-atomic map and copy
-        */
-       src = buf->ops->map(pipe, buf, 0);
-
-       ret = sd->len;
-       if (copy_to_user(sd->u.userptr, src + buf->offset, sd->len))
-               ret = -EFAULT;
-
-       buf->ops->unmap(pipe, buf, src);
-out:
-       if (ret > 0)
-               sd->u.userptr += ret;
-       return ret;
+       int n = copy_page_to_iter(buf->page, buf->offset, sd->len, sd->u.data);
+       return n == sd->len ? n : -EFAULT;
 }
 
 /*
  * For lack of a better implementation, implement vmsplice() to userspace
  * 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)
 {
        struct pipe_inode_info *pipe;
        struct splice_desc sd;
-       ssize_t size;
-       int error;
        long ret;
+       struct iovec iovstack[UIO_FASTIOV];
+       struct iovec *iov = iovstack;
+       struct iov_iter iter;
+       ssize_t count = 0;
 
        pipe = get_pipe_info(file);
        if (!pipe)
                return -EBADF;
 
-       pipe_lock(pipe);
-
-       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;
+       ret = rw_copy_check_uvector(READ, uiov, nr_segs,
+                                   ARRAY_SIZE(iovstack), iovstack, &iov);
+       if (ret <= 0)
+               return ret;
 
-               if (size < len)
-                       break;
+       iov_iter_init(&iter, iov, nr_segs, count, 0);
 
-               nr_segs--;
-               iov++;
-       }
+       sd.len = 0;
+       sd.total_len = count;
+       sd.flags = flags;
+       sd.u.data = &iter;
+       sd.pos = 0;
 
+       pipe_lock(pipe);
+       ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
        pipe_unlock(pipe);
 
-       if (!ret)
-               ret = error;
+       if (iov != iovstack)
+               kfree(iov);
 
        return ret;
 }