diff options
author | Hugh Dickins <hugh@veritas.com> | 2007-06-07 03:36:00 -0400 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2007-06-08 02:34:05 -0400 |
commit | 475ecade683566b19ebb84972de864039ac5fce3 (patch) | |
tree | cd2043c6c951c440988be8057380dfaab1ceb481 /fs/splice.c | |
parent | 20d698db67059a63d217030dfd02872cb5f88dfb (diff) |
splice: __generic_file_splice_read: fix i_size_read() length checks
__generic_file_splice_read's partial page check, at eof after readpage,
not only got its calculations wrong, but also reused the loff variable:
causing data corruption when splicing from a non-0 offset in the file's
last page (revealed by ext2 -b 1024 testing on a loop of a tmpfs file).
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 18 |
1 files changed, 10 insertions, 8 deletions
diff --git a/fs/splice.c b/fs/splice.c index 6349d3189e3f..123fcdb2e4d9 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -272,7 +272,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, | |||
272 | struct page *page; | 272 | struct page *page; |
273 | pgoff_t index, end_index; | 273 | pgoff_t index, end_index; |
274 | loff_t isize; | 274 | loff_t isize; |
275 | size_t total_len; | ||
276 | int error, page_nr; | 275 | int error, page_nr; |
277 | struct splice_pipe_desc spd = { | 276 | struct splice_pipe_desc spd = { |
278 | .pages = pages, | 277 | .pages = pages, |
@@ -298,7 +297,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, | |||
298 | * Now fill in the holes: | 297 | * Now fill in the holes: |
299 | */ | 298 | */ |
300 | error = 0; | 299 | error = 0; |
301 | total_len = 0; | ||
302 | 300 | ||
303 | /* | 301 | /* |
304 | * Lookup the (hopefully) full range of pages we need. | 302 | * Lookup the (hopefully) full range of pages we need. |
@@ -429,29 +427,33 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, | |||
429 | * the length and stop | 427 | * the length and stop |
430 | */ | 428 | */ |
431 | if (end_index == index) { | 429 | if (end_index == index) { |
432 | loff = PAGE_CACHE_SIZE - (isize & ~PAGE_CACHE_MASK); | 430 | unsigned int plen; |
433 | if (total_len + loff > isize) | 431 | |
432 | /* | ||
433 | * max good bytes in this page | ||
434 | */ | ||
435 | plen = ((isize - 1) & ~PAGE_CACHE_MASK) + 1; | ||
436 | if (plen <= loff) | ||
434 | break; | 437 | break; |
438 | |||
435 | /* | 439 | /* |
436 | * force quit after adding this page | 440 | * force quit after adding this page |
437 | */ | 441 | */ |
442 | this_len = min(this_len, plen - loff); | ||
438 | len = this_len; | 443 | len = this_len; |
439 | this_len = min(this_len, loff); | ||
440 | loff = 0; | ||
441 | } | 444 | } |
442 | } | 445 | } |
443 | fill_it: | 446 | fill_it: |
444 | partial[page_nr].offset = loff; | 447 | partial[page_nr].offset = loff; |
445 | partial[page_nr].len = this_len; | 448 | partial[page_nr].len = this_len; |
446 | len -= this_len; | 449 | len -= this_len; |
447 | total_len += this_len; | ||
448 | loff = 0; | 450 | loff = 0; |
449 | spd.nr_pages++; | 451 | spd.nr_pages++; |
450 | index++; | 452 | index++; |
451 | } | 453 | } |
452 | 454 | ||
453 | /* | 455 | /* |
454 | * Release any pages at the end, if we quit early. 'i' is how far | 456 | * Release any pages at the end, if we quit early. 'page_nr' is how far |
455 | * we got, 'nr_pages' is how many pages are in the map. | 457 | * we got, 'nr_pages' is how many pages are in the map. |
456 | */ | 458 | */ |
457 | while (page_nr < nr_pages) | 459 | while (page_nr < nr_pages) |