Skip to content

Commit

Permalink
splice: fix offset mangling with direct splicing (sendfile)
Browse files Browse the repository at this point in the history
If the output actor doesn't transfer the full amount of data, we will
increment ppos too much. Two related bugs in there:

- We need to break out and return actor() retval if it is shorted than
  what we spliced into the pipe.

- Adjust ppos only according to actor() return.

Also fix loop problem in generic_file_splice_read(), it should not keep
going when data has already been transferred.

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
  • Loading branch information
Jens Axboe committed Jul 13, 2007
1 parent 29ce205 commit 51a92c0
Showing 1 changed file with 11 additions and 23 deletions.
34 changes: 11 additions & 23 deletions fs/splice.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,

ret = 0;
spliced = 0;
while (len) {
while (len && !spliced) {
ret = __generic_file_splice_read(in, ppos, pipe, len, flags);

if (ret < 0)
Expand Down Expand Up @@ -1060,15 +1060,10 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
sd->flags &= ~SPLICE_F_NONBLOCK;

while (len) {
size_t read_len, max_read_len;

/*
* Do at most PIPE_BUFFERS pages worth of transfer:
*/
max_read_len = min(len, (size_t)(PIPE_BUFFERS*PAGE_SIZE));
size_t read_len;

ret = do_splice_to(in, &sd->pos, pipe, max_read_len, flags);
if (unlikely(ret < 0))
ret = do_splice_to(in, &sd->pos, pipe, len, flags);
if (unlikely(ret <= 0))
goto out_release;

read_len = ret;
Expand All @@ -1080,26 +1075,17 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
* could get stuck data in the internal pipe:
*/
ret = actor(pipe, sd);
if (unlikely(ret < 0))
if (unlikely(ret <= 0))
goto out_release;

bytes += ret;
len -= ret;

/*
* In nonblocking mode, if we got back a short read then
* that was due to either an IO error or due to the
* pagecache entry not being there. In the IO error case
* the _next_ splice attempt will produce a clean IO error
* return value (not a short read), so in both cases it's
* correct to break out of the loop here:
*/
if ((flags & SPLICE_F_NONBLOCK) && (read_len < max_read_len))
break;
if (ret < read_len)
goto out_release;
}

pipe->nrbufs = pipe->curbuf = 0;

return bytes;

out_release:
Expand Down Expand Up @@ -1161,10 +1147,12 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
.pos = *ppos,
.u.file = out,
};
size_t ret;
long ret;

ret = splice_direct_to_actor(in, &sd, direct_splice_actor);
*ppos = sd.pos;
if (ret > 0)
*ppos += ret;

return ret;
}

Expand Down

0 comments on commit 51a92c0

Please sign in to comment.