string: factorize skip_spaces and export it to be generally available
[safe/jmp/linux-2.6] / fs / splice.c
index c5e3c79..3920866 100644 (file)
@@ -502,8 +502,10 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
                len = left;
 
        ret = __generic_file_splice_read(in, ppos, pipe, len, flags);
-       if (ret > 0)
+       if (ret > 0) {
                *ppos += ret;
+               file_accessed(in);
+       }
 
        return ret;
 }
@@ -535,8 +537,8 @@ static ssize_t kernel_readv(struct file *file, const struct iovec *vec,
        return res;
 }
 
-static ssize_t kernel_writev(struct file *file, const struct iovec *vec,
-                           unsigned long vlen, loff_t *ppos)
+static ssize_t kernel_write(struct file *file, const char *buf, size_t count,
+                           loff_t pos)
 {
        mm_segment_t old_fs;
        ssize_t res;
@@ -544,7 +546,7 @@ static ssize_t kernel_writev(struct file *file, const struct iovec *vec,
        old_fs = get_fs();
        set_fs(get_ds());
        /* The cast to a user pointer is valid due to the set_fs() */
-       res = vfs_writev(file, (const struct iovec __user *)vec, vlen, ppos);
+       res = vfs_write(file, (const char __user *)buf, count, &pos);
        set_fs(old_fs);
 
        return res;
@@ -595,8 +597,10 @@ ssize_t default_file_splice_read(struct file *in, loff_t *ppos,
        }
 
        res = kernel_readv(in, vec, spd.nr_pages, *ppos);
-       if (res < 0)
+       if (res < 0) {
+               error = res;
                goto err;
+       }
 
        error = 0;
        if (!res)
@@ -644,9 +648,11 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe,
        ret = buf->ops->confirm(pipe, buf);
        if (!ret) {
                more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;
-
-               ret = file->f_op->sendpage(file, buf->page, buf->offset,
-                                          sd->len, &pos, more);
+               if (file->f_op && file->f_op->sendpage)
+                       ret = file->f_op->sendpage(file, buf->page, buf->offset,
+                                                  sd->len, &pos, more);
+               else
+                       ret = -EINVAL;
        }
 
        return ret;
@@ -961,8 +967,10 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
 
                mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
                ret = file_remove_suid(out);
-               if (!ret)
+               if (!ret) {
+                       file_update_time(out);
                        ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file);
+               }
                mutex_unlock(&inode->i_mutex);
        } while (ret > 0);
        splice_from_pipe_end(pipe, &sd);
@@ -974,25 +982,15 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
 
        if (ret > 0) {
                unsigned long nr_pages;
+               int err;
 
-               *ppos += ret;
                nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 
-               /*
-                * If file or inode is SYNC and we actually wrote some data,
-                * sync it.
-                */
-               if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) {
-                       int err;
-
-                       mutex_lock(&inode->i_mutex);
-                       err = generic_osync_inode(inode, mapping,
-                                                 OSYNC_METADATA|OSYNC_DATA);
-                       mutex_unlock(&inode->i_mutex);
-
-                       if (err)
-                               ret = err;
-               }
+               err = generic_write_sync(out, *ppos, ret);
+               if (err)
+                       ret = err;
+               else
+                       *ppos += ret;
                balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
        }
 
@@ -1001,120 +999,34 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
 
 EXPORT_SYMBOL(generic_file_splice_write);
 
-static struct pipe_buffer *nth_pipe_buf(struct pipe_inode_info *pipe, int n)
+static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
+                         struct splice_desc *sd)
 {
-       return &pipe->bufs[(pipe->curbuf + n) % PIPE_BUFFERS];
+       int ret;
+       void *data;
+
+       ret = buf->ops->confirm(pipe, buf);
+       if (ret)
+               return ret;
+
+       data = buf->ops->map(pipe, buf, 0);
+       ret = kernel_write(sd->u.file, data + buf->offset, sd->len, sd->pos);
+       buf->ops->unmap(pipe, buf, data);
+
+       return ret;
 }
 
 static ssize_t default_file_splice_write(struct pipe_inode_info *pipe,
                                         struct file *out, loff_t *ppos,
                                         size_t len, unsigned int flags)
 {
-       ssize_t ret = 0;
-       ssize_t total_len = 0;
-       int do_wakeup = 0;
-
-       pipe_lock(pipe);
-       while (len) {
-               struct pipe_buffer *buf;
-               void *data[PIPE_BUFFERS];
-               struct iovec vec[PIPE_BUFFERS];
-               unsigned int nr_pages = 0;
-               unsigned int write_len = 0;
-               unsigned int now_len = len;
-               unsigned int this_len;
-               int i;
-
-               BUG_ON(pipe->nrbufs > PIPE_BUFFERS);
-               for (i = 0; i < pipe->nrbufs && now_len; i++) {
-                       buf = nth_pipe_buf(pipe, i);
-
-                       ret = buf->ops->confirm(pipe, buf);
-                       if (ret)
-                               break;
-
-                       data[i] = buf->ops->map(pipe, buf, 0);
-                       this_len = min(buf->len, now_len);
-                       vec[i].iov_base = (void __user *) data[i] + buf->offset;
-                       vec[i].iov_len = this_len;
-                       now_len -= this_len;
-                       write_len += this_len;
-                       nr_pages++;
-               }
-
-               if (nr_pages) {
-                       ret = kernel_writev(out, vec, nr_pages, ppos);
-                       if (ret == 0)
-                               ret = -EIO;
-                       if (ret > 0) {
-                               len -= ret;
-                               total_len += ret;
-                       }
-               }
-
-               for (i = 0; i < nr_pages; i++) {
-                       buf = nth_pipe_buf(pipe, i);
-                       buf->ops->unmap(pipe, buf, data[i]);
-
-                       if (ret > 0) {
-                               this_len = min_t(unsigned, vec[i].iov_len, ret);
-                               buf->offset += this_len;
-                               buf->len -= this_len;
-                               ret -= this_len;
-                       }
-               }
-
-               if (ret < 0)
-                       break;
-
-               while (pipe->nrbufs) {
-                       const struct pipe_buf_operations *ops;
-
-                       buf = nth_pipe_buf(pipe, 0);
-                       if (buf->len)
-                               break;
-
-                       ops = buf->ops;
-                       buf->ops = NULL;
-                       ops->release(pipe, buf);
-                       pipe->curbuf = (pipe->curbuf + 1) % PIPE_BUFFERS;
-                       pipe->nrbufs--;
-                       if (pipe->inode)
-                               do_wakeup = 1;
-               }
-
-               if (pipe->nrbufs)
-                       continue;
-               if (!pipe->writers)
-                       break;
-               if (!pipe->waiting_writers) {
-                       if (total_len)
-                               break;
-               }
-
-               if (flags & SPLICE_F_NONBLOCK) {
-                       ret = -EAGAIN;
-                       break;
-               }
-
-               if (signal_pending(current)) {
-                       ret = -ERESTARTSYS;
-                       break;
-               }
-
-               if (do_wakeup) {
-                       wakeup_pipe_writers(pipe);
-                       do_wakeup = 0;
-               }
-
-               pipe_wait(pipe);
-       }
-       pipe_unlock(pipe);
+       ssize_t ret;
 
-       if (do_wakeup)
-               wakeup_pipe_writers(pipe);
+       ret = splice_from_pipe(pipe, out, ppos, len, flags, write_pipe_buf);
+       if (ret > 0)
+               *ppos += ret;
 
-       return total_len ? total_len : ret;
+       return ret;
 }
 
 /**
@@ -1158,8 +1070,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
        if (unlikely(ret < 0))
                return ret;
 
-       splice_write = out->f_op->splice_write;
-       if (!splice_write)
+       if (out->f_op && out->f_op->splice_write)
+               splice_write = out->f_op->splice_write;
+       else
                splice_write = default_file_splice_write;
 
        return splice_write(pipe, out, ppos, len, flags);
@@ -1183,8 +1096,9 @@ static long do_splice_to(struct file *in, loff_t *ppos,
        if (unlikely(ret < 0))
                return ret;
 
-       splice_read = in->f_op->splice_read;
-       if (!splice_read)
+       if (in->f_op && in->f_op->splice_read)
+               splice_read = in->f_op->splice_read;
+       else
                splice_read = default_file_splice_read;
 
        return splice_read(in, ppos, pipe, len, flags);
@@ -1406,7 +1320,8 @@ static long do_splice(struct file *in, loff_t __user *off_in,
                if (off_in)
                        return -ESPIPE;
                if (off_out) {
-                       if (out->f_op->llseek == no_llseek)
+                       if (!out->f_op || !out->f_op->llseek ||
+                           out->f_op->llseek == no_llseek)
                                return -EINVAL;
                        if (copy_from_user(&offset, off_out, sizeof(loff_t)))
                                return -EFAULT;
@@ -1426,7 +1341,8 @@ static long do_splice(struct file *in, loff_t __user *off_in,
                if (off_out)
                        return -ESPIPE;
                if (off_in) {
-                       if (in->f_op->llseek == no_llseek)
+                       if (!in->f_op || !in->f_op->llseek ||
+                           in->f_op->llseek == no_llseek)
                                return -EINVAL;
                        if (copy_from_user(&offset, off_in, sizeof(loff_t)))
                                return -EFAULT;