aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse/file.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2008-04-30 03:54:43 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-30 11:29:50 -0400
commit5c5c5e51b26413d50a9efae2ca7d6c5c6cd453ac (patch)
tree4f78209ad05e429a0b89658e495fca9b9262d81d /fs/fuse/file.c
parentea9b9907b82a09bd1a708004454f7065de77c5b0 (diff)
fuse: update file size on short read
If the READ request returned a short count, then either - cached size is incorrect - filesystem is buggy, as short reads are only allowed on EOF So assume that the size is wrong and refresh it, so that cached read() doesn't zero fill the missing chunk. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r--fs/fuse/file.c52
1 files changed, 46 insertions, 6 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index c5b5982bf386..a02418c89d4b 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -363,7 +363,7 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
363void fuse_read_fill(struct fuse_req *req, struct file *file, 363void fuse_read_fill(struct fuse_req *req, struct file *file,
364 struct inode *inode, loff_t pos, size_t count, int opcode) 364 struct inode *inode, loff_t pos, size_t count, int opcode)
365{ 365{
366 struct fuse_read_in *inarg = &req->misc.read_in; 366 struct fuse_read_in *inarg = &req->misc.read.in;
367 struct fuse_file *ff = file->private_data; 367 struct fuse_file *ff = file->private_data;
368 368
369 inarg->fh = ff->fh; 369 inarg->fh = ff->fh;
@@ -389,7 +389,7 @@ static size_t fuse_send_read(struct fuse_req *req, struct file *file,
389 389
390 fuse_read_fill(req, file, inode, pos, count, FUSE_READ); 390 fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
391 if (owner != NULL) { 391 if (owner != NULL) {
392 struct fuse_read_in *inarg = &req->misc.read_in; 392 struct fuse_read_in *inarg = &req->misc.read.in;
393 393
394 inarg->read_flags |= FUSE_READ_LOCKOWNER; 394 inarg->read_flags |= FUSE_READ_LOCKOWNER;
395 inarg->lock_owner = fuse_lock_owner_id(fc, owner); 395 inarg->lock_owner = fuse_lock_owner_id(fc, owner);
@@ -398,11 +398,29 @@ static size_t fuse_send_read(struct fuse_req *req, struct file *file,
398 return req->out.args[0].size; 398 return req->out.args[0].size;
399} 399}
400 400
401static void fuse_read_update_size(struct inode *inode, loff_t size,
402 u64 attr_ver)
403{
404 struct fuse_conn *fc = get_fuse_conn(inode);
405 struct fuse_inode *fi = get_fuse_inode(inode);
406
407 spin_lock(&fc->lock);
408 if (attr_ver == fi->attr_version && size < inode->i_size) {
409 fi->attr_version = ++fc->attr_version;
410 i_size_write(inode, size);
411 }
412 spin_unlock(&fc->lock);
413}
414
401static int fuse_readpage(struct file *file, struct page *page) 415static int fuse_readpage(struct file *file, struct page *page)
402{ 416{
403 struct inode *inode = page->mapping->host; 417 struct inode *inode = page->mapping->host;
404 struct fuse_conn *fc = get_fuse_conn(inode); 418 struct fuse_conn *fc = get_fuse_conn(inode);
405 struct fuse_req *req; 419 struct fuse_req *req;
420 size_t num_read;
421 loff_t pos = page_offset(page);
422 size_t count = PAGE_CACHE_SIZE;
423 u64 attr_ver;
406 int err; 424 int err;
407 425
408 err = -EIO; 426 err = -EIO;
@@ -421,15 +439,25 @@ static int fuse_readpage(struct file *file, struct page *page)
421 if (IS_ERR(req)) 439 if (IS_ERR(req))
422 goto out; 440 goto out;
423 441
442 attr_ver = fuse_get_attr_version(fc);
443
424 req->out.page_zeroing = 1; 444 req->out.page_zeroing = 1;
425 req->num_pages = 1; 445 req->num_pages = 1;
426 req->pages[0] = page; 446 req->pages[0] = page;
427 fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE, 447 num_read = fuse_send_read(req, file, inode, pos, count, NULL);
428 NULL);
429 err = req->out.h.error; 448 err = req->out.h.error;
430 fuse_put_request(fc, req); 449 fuse_put_request(fc, req);
431 if (!err) 450
451 if (!err) {
452 /*
453 * Short read means EOF. If file size is larger, truncate it
454 */
455 if (num_read < count)
456 fuse_read_update_size(inode, pos + num_read, attr_ver);
457
432 SetPageUptodate(page); 458 SetPageUptodate(page);
459 }
460
433 fuse_invalidate_attr(inode); /* atime changed */ 461 fuse_invalidate_attr(inode); /* atime changed */
434 out: 462 out:
435 unlock_page(page); 463 unlock_page(page);
@@ -439,8 +467,19 @@ static int fuse_readpage(struct file *file, struct page *page)
439static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req) 467static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
440{ 468{
441 int i; 469 int i;
470 size_t count = req->misc.read.in.size;
471 size_t num_read = req->out.args[0].size;
472 struct inode *inode = req->pages[0]->mapping->host;
442 473
443 fuse_invalidate_attr(req->pages[0]->mapping->host); /* atime changed */ 474 /*
475 * Short read means EOF. If file size is larger, truncate it
476 */
477 if (!req->out.h.error && num_read < count) {
478 loff_t pos = page_offset(req->pages[0]) + num_read;
479 fuse_read_update_size(inode, pos, req->misc.read.attr_ver);
480 }
481
482 fuse_invalidate_attr(inode); /* atime changed */
444 483
445 for (i = 0; i < req->num_pages; i++) { 484 for (i = 0; i < req->num_pages; i++) {
446 struct page *page = req->pages[i]; 485 struct page *page = req->pages[i];
@@ -463,6 +502,7 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file,
463 size_t count = req->num_pages << PAGE_CACHE_SHIFT; 502 size_t count = req->num_pages << PAGE_CACHE_SHIFT;
464 req->out.page_zeroing = 1; 503 req->out.page_zeroing = 1;
465 fuse_read_fill(req, file, inode, pos, count, FUSE_READ); 504 fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
505 req->misc.read.attr_ver = fuse_get_attr_version(fc);
466 if (fc->async_read) { 506 if (fc->async_read) {
467 struct fuse_file *ff = file->private_data; 507 struct fuse_file *ff = file->private_data;
468 req->ff = fuse_file_get(ff); 508 req->ff = fuse_file_get(ff);