aboutsummaryrefslogtreecommitdiffstats
path: root/fs/splice.c
diff options
context:
space:
mode:
authorJens Axboe <axboe@suse.de>2006-04-19 09:55:10 -0400
committerJens Axboe <axboe@suse.de>2006-04-19 09:55:10 -0400
commit91ad66ef4469cb631ec0ccd131b07f16770773f7 (patch)
tree89db609ce4d9673ad1f586abe87c76d6f38ab0d0 /fs/splice.c
parent8bbde0e6d52265158ee9625f383500c1a7d09ba9 (diff)
[PATCH] splice: close i_size truncate races on read
We need to check i_size after doing a blocking readpage. Signed-off-by: Jens Axboe <axboe@suse.de>
Diffstat (limited to 'fs/splice.c')
-rw-r--r--fs/splice.c43
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 */
147static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages, 147static 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++) {
272find_page: 275find_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 }
340fill_it: 369fill_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}