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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Miklos Szeredi <mszeredi@xxxxxxx>

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@xxxxxxx>
---
 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);

--
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux