diff options
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 43 |
1 files changed, 37 insertions, 6 deletions
diff --git a/fs/splice.c b/fs/splice.c index 8d57e89924a6..7e8585574726 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -145,8 +145,8 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = { | |||
145 | * pipe buffer operations. Otherwise very similar to the regular pipe_writev(). | 145 | * pipe buffer operations. Otherwise very similar to the regular pipe_writev(). |
146 | */ | 146 | */ |
147 | static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages, | 147 | static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages, |
148 | int nr_pages, unsigned long offset, | 148 | int nr_pages, unsigned long len, |
149 | unsigned long len, unsigned int flags) | 149 | unsigned int offset, unsigned int flags) |
150 | { | 150 | { |
151 | int ret, do_wakeup, i; | 151 | int ret, do_wakeup, i; |
152 | 152 | ||
@@ -243,14 +243,16 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, | |||
243 | unsigned int flags) | 243 | unsigned int flags) |
244 | { | 244 | { |
245 | struct address_space *mapping = in->f_mapping; | 245 | struct address_space *mapping = in->f_mapping; |
246 | unsigned int offset, nr_pages; | 246 | unsigned int loff, offset, nr_pages; |
247 | struct page *pages[PIPE_BUFFERS]; | 247 | struct page *pages[PIPE_BUFFERS]; |
248 | struct page *page; | 248 | struct page *page; |
249 | pgoff_t index; | 249 | pgoff_t index, end_index; |
250 | loff_t isize; | ||
251 | size_t bytes; | ||
250 | int i, error; | 252 | int i, error; |
251 | 253 | ||
252 | index = *ppos >> PAGE_CACHE_SHIFT; | 254 | index = *ppos >> PAGE_CACHE_SHIFT; |
253 | offset = *ppos & ~PAGE_CACHE_MASK; | 255 | loff = offset = *ppos & ~PAGE_CACHE_MASK; |
254 | nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | 256 | nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; |
255 | 257 | ||
256 | if (nr_pages > PIPE_BUFFERS) | 258 | if (nr_pages > PIPE_BUFFERS) |
@@ -268,6 +270,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, | |||
268 | * Now fill in the holes: | 270 | * Now fill in the holes: |
269 | */ | 271 | */ |
270 | error = 0; | 272 | error = 0; |
273 | bytes = 0; | ||
271 | for (i = 0; i < nr_pages; i++, index++) { | 274 | for (i = 0; i < nr_pages; i++, index++) { |
272 | find_page: | 275 | find_page: |
273 | /* | 276 | /* |
@@ -336,13 +339,41 @@ readpage: | |||
336 | goto find_page; | 339 | goto find_page; |
337 | break; | 340 | break; |
338 | } | 341 | } |
342 | |||
343 | /* | ||
344 | * i_size must be checked after ->readpage(). | ||
345 | */ | ||
346 | isize = i_size_read(mapping->host); | ||
347 | end_index = (isize - 1) >> PAGE_CACHE_SHIFT; | ||
348 | if (unlikely(!isize || index > end_index)) { | ||
349 | page_cache_release(page); | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * if this is the last page, see if we need to shrink | ||
355 | * the length and stop | ||
356 | */ | ||
357 | if (end_index == index) { | ||
358 | loff = PAGE_CACHE_SIZE - (isize & ~PAGE_CACHE_MASK); | ||
359 | if (bytes + loff > isize) { | ||
360 | page_cache_release(page); | ||
361 | break; | ||
362 | } | ||
363 | /* | ||
364 | * force quit after adding this page | ||
365 | */ | ||
366 | nr_pages = i; | ||
367 | } | ||
339 | } | 368 | } |
340 | fill_it: | 369 | fill_it: |
341 | pages[i] = page; | 370 | pages[i] = page; |
371 | bytes += PAGE_CACHE_SIZE - loff; | ||
372 | loff = 0; | ||
342 | } | 373 | } |
343 | 374 | ||
344 | if (i) | 375 | if (i) |
345 | return move_to_pipe(pipe, pages, i, offset, len, flags); | 376 | return move_to_pipe(pipe, pages, i, bytes, offset, flags); |
346 | 377 | ||
347 | return error; | 378 | return error; |
348 | } | 379 | } |