[RHEL7,COMMIT] fs/fuse kio: avoid race condition at file expand

Submitted by Vasily Averin on March 2, 2021, 6:53 a.m.

Details

Message ID 202103020653.1226rEW4028228@vz7build.vvs.sw.ru
State New
Series "fs/fuse kio: avoid race condition at file expand"
Headers show

Commit Message

Vasily Averin March 2, 2021, 6:53 a.m.
The commit is pushed to "branch-rh7-3.10.0-1160.15.2.vz7.173.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-1160.15.2.vz7.173.1
------>
commit 2dd94da736011d88206a229508d94d3f96da1302
Author: Alexey Kuznetsov <kuznet@acronis.com>
Date:   Tue Mar 2 09:53:14 2021 +0300

    fs/fuse kio: avoid race condition at file expand
    
    Update inode size after mappings are invalidated, not before.
    
    It was possible that new write request arrives right after
    inode size increased, but before maps are invalidated.
    This will result in write using an orphaned map.
    
    https://pmc.acronis.com/browse/VSTOR-36154
    
    Signed-off-by: Alexey Kuznetsov <kuznet@acronis.com>
    Reviewed-by: Ildar Ismagilov <ildar.ismagilov@virtuozzo.com>
    Signed-off-by: Ildar Ismagilov <ildar.ismagilov@virtuozzo.com>
---
 fs/fuse/kio/pcs/pcs_fuse_kdirect.c | 13 ++++++-------
 fs/fuse/kio/pcs/pcs_map.c          |  4 ++--
 fs/fuse/kio/pcs/pcs_map.h          |  2 +-
 3 files changed, 9 insertions(+), 10 deletions(-)

Patch hide | download patch | download mbox

diff --git a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
index 438c3c1..2ea1faa 100644
--- a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
+++ b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
@@ -1038,7 +1038,6 @@  static void kpcs_setattr_end(struct fuse_conn *fc, struct fuse_req *req)
 	struct fuse_setattr_in *inarg = (void*) req->in.args[0].value;
 	struct fuse_attr_out *outarg = (void*) req->out.args[0].value;
 	struct pcs_dentry_info *di = pcs_inode_from_fuse(fi);
-	u64 old_size;
 
 	BUG_ON(req->in.h.opcode != FUSE_SETATTR);
 	TRACE("update size: ino:%lu old_sz:%lld new:%lld, error: %d\n",
@@ -1048,18 +1047,18 @@  static void kpcs_setattr_end(struct fuse_conn *fc, struct fuse_req *req)
 	if (req->out.h.error)
 		goto fail;
 
-	spin_lock(&di->lock);
-	old_size = di->fileinfo.attr.size;
-	di->fileinfo.attr.size = outarg->attr.size;
-	spin_unlock(&di->lock);
-
 	if (outarg->attr.size == inarg->size)
-		pcs_mapping_truncate(di, old_size);
+		pcs_mapping_truncate(di, outarg->attr.size);
 	else {
 		pr_err("kio: failed to set requested size: %llu %llu\n",
 			outarg->attr.size, inarg->size);
 		req->out.h.error = -EIO;
 	}
+
+	spin_lock(&di->lock);
+	di->fileinfo.attr.size = outarg->attr.size;
+	spin_unlock(&di->lock);
+
 fail:
 	if(r->end)
 		r->end(fc, req);
diff --git a/fs/fuse/kio/pcs/pcs_map.c b/fs/fuse/kio/pcs/pcs_map.c
index 0a787f0..a69c77f 100644
--- a/fs/fuse/kio/pcs/pcs_map.c
+++ b/fs/fuse/kio/pcs/pcs_map.c
@@ -2507,9 +2507,9 @@  truncate_tail:
  * user space already has good map with newer version it will return it
  * immediately and kernel/user will be in sync again.
  */
-noinline void pcs_mapping_truncate(struct pcs_dentry_info *di, u64 old_size)
+noinline void pcs_mapping_truncate(struct pcs_dentry_info *di, u64 new_size)
 {
-	u64 new_size = DENTRY_SIZE(di), offset;
+	u64 old_size = DENTRY_SIZE(di), offset;
 	struct pcs_map_entry *m;
 
 	di->local_mtime = get_real_time_ms();
diff --git a/fs/fuse/kio/pcs/pcs_map.h b/fs/fuse/kio/pcs/pcs_map.h
index 93cc041..a6a4ee8 100644
--- a/fs/fuse/kio/pcs/pcs_map.h
+++ b/fs/fuse/kio/pcs/pcs_map.h
@@ -164,7 +164,7 @@  void pcs_mapping_init(struct pcs_cluster_core *cc, struct pcs_mapping * mapping)
 void pcs_mapping_open(struct pcs_mapping * mapping);
 void pcs_mapping_invalidate(struct pcs_mapping * mapping);
 void pcs_mapping_deinit(struct pcs_mapping * mapping);
-void pcs_mapping_truncate(struct pcs_dentry_info *di, u64 old_size);
+void pcs_mapping_truncate(struct pcs_dentry_info *di, u64 new_size);
 void process_ireq_truncate(struct pcs_int_request *ireq);
 
 struct pcs_map_entry * pcs_find_get_map(struct pcs_dentry_info * de, u64 chunk);