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>
This commit is contained in:
18
fs/splice.c
18
fs/splice.c
@@ -272,7 +272,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
|
|||||||
struct page *page;
|
struct page *page;
|
||||||
pgoff_t index, end_index;
|
pgoff_t index, end_index;
|
||||||
loff_t isize;
|
loff_t isize;
|
||||||
size_t total_len;
|
|
||||||
int error, page_nr;
|
int error, page_nr;
|
||||||
struct splice_pipe_desc spd = {
|
struct splice_pipe_desc spd = {
|
||||||
.pages = pages,
|
.pages = pages,
|
||||||
@@ -298,7 +297,6 @@ __generic_file_splice_read(struct file *in, loff_t *ppos,
|
|||||||
* Now fill in the holes:
|
* Now fill in the holes:
|
||||||
*/
|
*/
|
||||||
error = 0;
|
error = 0;
|
||||||
total_len = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lookup the (hopefully) full range of pages we need.
|
* 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
|
* the length and stop
|
||||||
*/
|
*/
|
||||||
if (end_index == index) {
|
if (end_index == index) {
|
||||||
loff = PAGE_CACHE_SIZE - (isize & ~PAGE_CACHE_MASK);
|
unsigned int plen;
|
||||||
if (total_len + loff > isize)
|
|
||||||
|
/*
|
||||||
|
* max good bytes in this page
|
||||||
|
*/
|
||||||
|
plen = ((isize - 1) & ~PAGE_CACHE_MASK) + 1;
|
||||||
|
if (plen <= loff)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* force quit after adding this page
|
* force quit after adding this page
|
||||||
*/
|
*/
|
||||||
|
this_len = min(this_len, plen - loff);
|
||||||
len = this_len;
|
len = this_len;
|
||||||
this_len = min(this_len, loff);
|
|
||||||
loff = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fill_it:
|
fill_it:
|
||||||
partial[page_nr].offset = loff;
|
partial[page_nr].offset = loff;
|
||||||
partial[page_nr].len = this_len;
|
partial[page_nr].len = this_len;
|
||||||
len -= this_len;
|
len -= this_len;
|
||||||
total_len += this_len;
|
|
||||||
loff = 0;
|
loff = 0;
|
||||||
spd.nr_pages++;
|
spd.nr_pages++;
|
||||||
index++;
|
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.
|
* we got, 'nr_pages' is how many pages are in the map.
|
||||||
*/
|
*/
|
||||||
while (page_nr < nr_pages)
|
while (page_nr < nr_pages)
|
||||||
|
Reference in New Issue
Block a user