[readdir] introduce ->iterate(), ctx->pos, dir_emit()
[firefly-linux-kernel-4.4.55.git] / fs / splice.c
index 29e394e49ddda7c7721d3939d993632a29f4499a..9eca476227d535eb29c74dfa7aaf324167f7e93f 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/security.h>
 #include <linux/gfp.h>
 #include <linux/socket.h>
+#include <linux/compat.h>
 #include "internal.h"
 
 /*
@@ -218,7 +219,7 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
                        page_nr++;
                        ret += buf->len;
 
-                       if (pipe->inode)
+                       if (pipe->files)
                                do_wakeup = 1;
 
                        if (!--spd->nr_pages)
@@ -828,7 +829,7 @@ int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd,
                        ops->release(pipe, buf);
                        pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
                        pipe->nrbufs--;
-                       if (pipe->inode)
+                       if (pipe->files)
                                sd->need_wakeup = true;
                }
 
@@ -1000,8 +1001,6 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
        };
        ssize_t ret;
 
-       sb_start_write(inode->i_sb);
-
        pipe_lock(pipe);
 
        splice_from_pipe_begin(&sd);
@@ -1037,7 +1036,6 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
                        *ppos += ret;
                balance_dirty_pages_ratelimited(mapping);
        }
-       sb_end_write(inode->i_sb);
 
        return ret;
 }
@@ -1117,7 +1115,10 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
        else
                splice_write = default_file_splice_write;
 
-       return splice_write(pipe, out, ppos, len, flags);
+       file_start_write(out);
+       ret = splice_write(pipe, out, ppos, len, flags);
+       file_end_write(out);
+       return ret;
 }
 
 /*
@@ -1183,7 +1184,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
         */
        pipe = current->splice_pipe;
        if (unlikely(!pipe)) {
-               pipe = alloc_pipe_info(NULL);
+               pipe = alloc_pipe_info();
                if (!pipe)
                        return -ENOMEM;
 
@@ -1273,7 +1274,7 @@ static int direct_splice_actor(struct pipe_inode_info *pipe,
 {
        struct file *file = sd->u.file;
 
-       return do_splice_from(pipe, file, &file->f_pos, sd->total_len,
+       return do_splice_from(pipe, file, sd->opos, sd->total_len,
                              sd->flags);
 }
 
@@ -1293,7 +1294,7 @@ static int direct_splice_actor(struct pipe_inode_info *pipe,
  *
  */
 long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
-                     size_t len, unsigned int flags)
+                     loff_t *opos, size_t len, unsigned int flags)
 {
        struct splice_desc sd = {
                .len            = len,
@@ -1301,6 +1302,7 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
                .flags          = flags,
                .pos            = *ppos,
                .u.file         = out,
+               .opos           = opos,
        };
        long ret;
 
@@ -1324,7 +1326,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
 {
        struct pipe_inode_info *ipipe;
        struct pipe_inode_info *opipe;
-       loff_t offset, *off;
+       loff_t offset;
        long ret;
 
        ipipe = get_pipe_info(in);
@@ -1355,13 +1357,15 @@ static long do_splice(struct file *in, loff_t __user *off_in,
                                return -EINVAL;
                        if (copy_from_user(&offset, off_out, sizeof(loff_t)))
                                return -EFAULT;
-                       off = &offset;
-               } else
-                       off = &out->f_pos;
+               } else {
+                       offset = out->f_pos;
+               }
 
-               ret = do_splice_from(ipipe, out, off, len, flags);
+               ret = do_splice_from(ipipe, out, &offset, len, flags);
 
-               if (off_out && copy_to_user(off_out, off, sizeof(loff_t)))
+               if (!off_out)
+                       out->f_pos = offset;
+               else if (copy_to_user(off_out, &offset, sizeof(loff_t)))
                        ret = -EFAULT;
 
                return ret;
@@ -1375,13 +1379,15 @@ static long do_splice(struct file *in, loff_t __user *off_in,
                                return -EINVAL;
                        if (copy_from_user(&offset, off_in, sizeof(loff_t)))
                                return -EFAULT;
-                       off = &offset;
-               } else
-                       off = &in->f_pos;
+               } else {
+                       offset = in->f_pos;
+               }
 
-               ret = do_splice_to(in, off, opipe, len, flags);
+               ret = do_splice_to(in, &offset, opipe, len, flags);
 
-               if (off_in && copy_to_user(off_in, off, sizeof(loff_t)))
+               if (!off_in)
+                       in->f_pos = offset;
+               else if (copy_to_user(off_in, &offset, sizeof(loff_t)))
                        ret = -EFAULT;
 
                return ret;
@@ -1690,6 +1696,27 @@ SYSCALL_DEFINE4(vmsplice, int, fd, const struct iovec __user *, iov,
        return error;
 }
 
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE4(vmsplice, int, fd, const struct compat_iovec __user *, iov32,
+                   unsigned int, nr_segs, unsigned int, flags)
+{
+       unsigned i;
+       struct iovec __user *iov;
+       if (nr_segs > UIO_MAXIOV)
+               return -EINVAL;
+       iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec));
+       for (i = 0; i < nr_segs; i++) {
+               struct compat_iovec v;
+               if (get_user(v.iov_base, &iov32[i].iov_base) ||
+                   get_user(v.iov_len, &iov32[i].iov_len) ||
+                   put_user(compat_ptr(v.iov_base), &iov[i].iov_base) ||
+                   put_user(v.iov_len, &iov[i].iov_len))
+                       return -EFAULT;
+       }
+       return sys_vmsplice(fd, iov, nr_segs, flags);
+}
+#endif
+
 SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
                int, fd_out, loff_t __user *, off_out,
                size_t, len, unsigned int, flags)