[patch 4/5] fuse: fix truncation on short read

Miklos Szeredi
2008-04-25T17:57:02Z

[-- Attachment #1: fuse_truncate_file_on_short_read_fix.patch --]
[-- Type: text/plain, Size: 4925 bytes --]

From: Miklos Szeredi <mszeredi@suse.cz>

fuse-update-file-size-on-short-read.patch introduced a bug, where a
read could truncate off the part recently extended by a parallel
write.

Fix by using the attribute versioning already used by getattr().

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
fs/fuse/dir.c | 2 +-
fs/fuse/file.c | 22 ++++++++++++++--------
fs/fuse/fuse_i.h | 7 ++++++-
3 files changed, 21 insertions(+), 10 deletions(-)

Index: linux/fs/fuse/dir.c
===================================================================
--- linux.orig/fs/fuse/dir.c 2008-04-25 17:10:02.000000000 +0200
+++ linux/fs/fuse/dir.c 2008-04-25 17:11:02.000000000 +0200
@@ -132,7 +132,7 @@ static void fuse_lookup_init(struct fuse
req->out.args[0].value = outarg;
}

-static u64 fuse_get_attr_version(struct fuse_conn *fc)
+u64 fuse_get_attr_version(struct fuse_conn *fc)
{
u64 curr_version;

Index: linux/fs/fuse/file.c
===================================================================
--- linux.orig/fs/fuse/file.c 2008-04-25 17:10:47.000000000 +0200
+++ linux/fs/fuse/file.c 2008-04-25 17:11:02.000000000 +0200
@@ -363,7 +363,7 @@ static int fuse_fsync(struct file *file,
void fuse_read_fill(struct fuse_req *req, struct file *file,
struct inode *inode, loff_t pos, size_t count, int opcode)
{
- struct fuse_read_in *inarg = &req->misc.read_in;
+ struct fuse_read_in *inarg = &req->misc.read.in;
struct fuse_file *ff = file->private_data;

inarg->fh = ff->fh;
@@ -389,7 +389,7 @@ static size_t fuse_send_read(struct fuse

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

inarg->read_flags |= FUSE_READ_LOCKOWNER;
inarg->lock_owner = fuse_lock_owner_id(fc, owner);
@@ -398,15 +398,17 @@ static size_t fuse_send_read(struct fuse
return req->out.args[0].size;
}

-static void fuse_read_update_size(struct inode *inode, loff_t size)
+static void fuse_read_update_size(struct inode *inode, loff_t size,
+ u64 attr_ver)
{
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);

spin_lock(&fc->lock);
- fi->attr_version = ++fc->attr_version;
- if (size < inode->i_size)
+ if (attr_ver == fi->attr_version && size < inode->i_size) {
+ fi->attr_version = ++fc->attr_version;
i_size_write(inode, size);
+ }
spin_unlock(&fc->lock);
}

@@ -418,6 +420,7 @@ static int fuse_readpage(struct file *fi
size_t num_read;
loff_t pos = page_offset(page);
size_t count = PAGE_CACHE_SIZE;
+ u64 attr_ver;
int err;

err = -EIO;
@@ -436,6 +439,8 @@ static int fuse_readpage(struct file *fi
if (IS_ERR(req))
goto out;

+ attr_ver = fuse_get_attr_version(fc);
+
req->out.page_zeroing = 1;
req->num_pages = 1;
req->pages[0] = page;
@@ -448,7 +453,7 @@ static int fuse_readpage(struct file *fi
* Short read means EOF. If file size is larger, truncate it
*/
if (num_read < count)
- fuse_read_update_size(inode, pos + num_read);
+ fuse_read_update_size(inode, pos + num_read, attr_ver);

SetPageUptodate(page);
}
@@ -462,7 +467,7 @@ static int fuse_readpage(struct file *fi
static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
{
int i;
- size_t count = req->misc.read_in.size;
+ size_t count = req->misc.read.in.size;
size_t num_read = req->out.args[0].size;
struct inode *inode = req->pages[0]->mapping->host;

@@ -471,7 +476,7 @@ static void fuse_readpages_end(struct fu
*/
if (!req->out.h.error && num_read < count) {
loff_t pos = page_offset(req->pages[0]) + num_read;
- fuse_read_update_size(inode, pos);
+ fuse_read_update_size(inode, pos, req->misc.read.attr_ver);
}

fuse_invalidate_attr(inode); /* atime changed */
@@ -497,6 +502,7 @@ static void fuse_send_readpages(struct f
size_t count = req->num_pages << PAGE_CACHE_SHIFT;
req->out.page_zeroing = 1;
fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
+ req->misc.read.attr_ver = fuse_get_attr_version(fc);
if (fc->async_read) {
struct fuse_file *ff = file->private_data;
req->ff = fuse_file_get(ff);
Index: linux/fs/fuse/fuse_i.h
===================================================================
--- linux.orig/fs/fuse/fuse_i.h 2008-04-25 17:09:29.000000000 +0200
+++ linux/fs/fuse/fuse_i.h 2008-04-25 17:11:02.000000000 +0200
@@ -239,7 +239,10 @@ struct fuse_req {
} release;
struct fuse_init_in init_in;
struct fuse_init_out init_out;
- struct fuse_read_in read_in;
+ struct {
+ struct fuse_read_in in;
+ u64 attr_ver;
+ } read;
struct {
struct fuse_write_in in;
struct fuse_write_out out;
@@ -637,3 +640,5 @@ void fuse_flush_writepages(struct inode

void fuse_set_nowrite(struct inode *inode);
void fuse_release_nowrite(struct inode *inode);
+
+u64 fuse_get_attr_version(struct fuse_conn *fc);

--