Skip to content

Commit 5c5c5e5

Browse files
Miklos Szereditorvalds
authored andcommitted
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 <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent ea9b990 commit 5c5c5e5

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

fs/fuse/dir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ static void fuse_lookup_init(struct fuse_req *req, struct inode *dir,
132132
req->out.args[0].value = outarg;
133133
}
134134

135-
static u64 fuse_get_attr_version(struct fuse_conn *fc)
135+
u64 fuse_get_attr_version(struct fuse_conn *fc)
136136
{
137137
u64 curr_version;
138138

fs/fuse/file.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
363363
void fuse_read_fill(struct fuse_req *req, struct file *file,
364364
struct inode *inode, loff_t pos, size_t count, int opcode)
365365
{
366-
struct fuse_read_in *inarg = &req->misc.read_in;
366+
struct fuse_read_in *inarg = &req->misc.read.in;
367367
struct fuse_file *ff = file->private_data;
368368

369369
inarg->fh = ff->fh;
@@ -389,7 +389,7 @@ static size_t fuse_send_read(struct fuse_req *req, struct file *file,
389389

390390
fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
391391
if (owner != NULL) {
392-
struct fuse_read_in *inarg = &req->misc.read_in;
392+
struct fuse_read_in *inarg = &req->misc.read.in;
393393

394394
inarg->read_flags |= FUSE_READ_LOCKOWNER;
395395
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,
398398
return req->out.args[0].size;
399399
}
400400

401+
static 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+
401415
static int fuse_readpage(struct file *file, struct page *page)
402416
{
403417
struct inode *inode = page->mapping->host;
404418
struct fuse_conn *fc = get_fuse_conn(inode);
405419
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;
406424
int err;
407425

408426
err = -EIO;
@@ -421,15 +439,25 @@ static int fuse_readpage(struct file *file, struct page *page)
421439
if (IS_ERR(req))
422440
goto out;
423441

442+
attr_ver = fuse_get_attr_version(fc);
443+
424444
req->out.page_zeroing = 1;
425445
req->num_pages = 1;
426446
req->pages[0] = page;
427-
fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE,
428-
NULL);
447+
num_read = fuse_send_read(req, file, inode, pos, count, NULL);
429448
err = req->out.h.error;
430449
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+
432458
SetPageUptodate(page);
459+
}
460+
433461
fuse_invalidate_attr(inode); /* atime changed */
434462
out:
435463
unlock_page(page);
@@ -439,8 +467,19 @@ static int fuse_readpage(struct file *file, struct page *page)
439467
static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
440468
{
441469
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;
442473

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 */
444483

445484
for (i = 0; i < req->num_pages; i++) {
446485
struct page *page = req->pages[i];
@@ -463,6 +502,7 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file,
463502
size_t count = req->num_pages << PAGE_CACHE_SHIFT;
464503
req->out.page_zeroing = 1;
465504
fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
505+
req->misc.read.attr_ver = fuse_get_attr_version(fc);
466506
if (fc->async_read) {
467507
struct fuse_file *ff = file->private_data;
468508
req->ff = fuse_file_get(ff);

fs/fuse/fuse_i.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,10 @@ struct fuse_req {
239239
} release;
240240
struct fuse_init_in init_in;
241241
struct fuse_init_out init_out;
242-
struct fuse_read_in read_in;
242+
struct {
243+
struct fuse_read_in in;
244+
u64 attr_ver;
245+
} read;
243246
struct {
244247
struct fuse_write_in in;
245248
struct fuse_write_out out;
@@ -637,3 +640,5 @@ void fuse_flush_writepages(struct inode *inode);
637640

638641
void fuse_set_nowrite(struct inode *inode);
639642
void fuse_release_nowrite(struct inode *inode);
643+
644+
u64 fuse_get_attr_version(struct fuse_conn *fc);

0 commit comments

Comments
 (0)