summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugh Dickins <hugh@veritas.com>2007-06-07 09:36:00 +0200
committerJens Axboe <jens.axboe@oracle.com>2007-06-08 08:34:05 +0200
commit475ecade683566b19ebb84972de864039ac5fce3 (patch)
treecd2043c6c951c440988be8057380dfaab1ceb481
parent20d698db67059a63d217030dfd02872cb5f88dfb (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>
-rw-r--r--fs/splice.c18
1 files changed, 10 insertions, 8 deletions
diff --git a/fs/splice.c b/fs/splice.c
index 6349d3189e3..123fcdb2e4d 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -272,7 +272,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
struct page *page;
pgoff_t index, end_index;
loff_t isize;
- size_t total_len;
int error, page_nr;
struct splice_pipe_desc spd = {
.pages = pages,
@@ -298,7 +297,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
* Now fill in the holes:
*/
error = 0;
- total_len = 0;
/*
* Lookup the (hopefully) full range of pages we need.
@@ -429,29 +427,33 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
* the length and stop
*/
if (end_index == index) {
- loff = PAGE_CACHE_SIZE - (isize & ~PAGE_CACHE_MASK);
- if (total_len + loff > isize)
+ unsigned int plen;
+
+ /*
+ * max good bytes in this page
+ */
+ plen = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;
+ if (plen <= loff)
break;
+
/*
* force quit after adding this page
*/
+ this_len = min(this_len, plen - loff);
len = this_len;
- this_len = min(this_len, loff);
- loff = 0;
}
}
fill_it:
partial[page_nr].offset = loff;
partial[page_nr].len = this_len;
len -= this_len;
- total_len += this_len;
loff = 0;
spd.nr_pages++;
index++;
}
/*
- * Release any pages at the end, if we quit early. 'i' is how far
+ * Release any pages at the end, if we quit early. 'page_nr' is how far
* we got, 'nr_pages' is how many pages are in the map.
*/
while (page_nr < nr_pages)