瀏覽代碼

fuse: fix fsync on directory

Commit ab2257e9941b ("fuse: reduce size of struct fuse_inode") moved parts
of fields related to writeback on regular file and to directory caching
into a union.  However fuse_fsync_common() called from fuse_dir_fsync()
touches some writeback related fields, resulting in a crash.

Move writeback related parts from fuse_fsync_common() to fuse_fysnc().

Reported-by: Brett Girton <btgirton@gmail.com>
Tested-by: Brett Girton <btgirton@gmail.com>
Fixes: ab2257e9941b ("fuse: reduce size of struct fuse_inode")
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Miklos Szeredi 6 年之前
父節點
當前提交
a9c2d1e82f
共有 3 個文件被更改,包括 42 次插入23 次删除
  1. 19 1
      fs/fuse/dir.c
  2. 22 21
      fs/fuse/file.c
  3. 1 1
      fs/fuse/fuse_i.h

+ 19 - 1
fs/fuse/dir.c

@@ -1249,7 +1249,25 @@ static int fuse_dir_release(struct inode *inode, struct file *file)
 static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end,
 static int fuse_dir_fsync(struct file *file, loff_t start, loff_t end,
 			  int datasync)
 			  int datasync)
 {
 {
-	return fuse_fsync_common(file, start, end, datasync, 1);
+	struct inode *inode = file->f_mapping->host;
+	struct fuse_conn *fc = get_fuse_conn(inode);
+	int err;
+
+	if (is_bad_inode(inode))
+		return -EIO;
+
+	if (fc->no_fsyncdir)
+		return 0;
+
+	inode_lock(inode);
+	err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNCDIR);
+	if (err == -ENOSYS) {
+		fc->no_fsyncdir = 1;
+		err = 0;
+	}
+	inode_unlock(inode);
+
+	return err;
 }
 }
 
 
 static long fuse_dir_ioctl(struct file *file, unsigned int cmd,
 static long fuse_dir_ioctl(struct file *file, unsigned int cmd,

+ 22 - 21
fs/fuse/file.c

@@ -441,13 +441,30 @@ static int fuse_flush(struct file *file, fl_owner_t id)
 }
 }
 
 
 int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
 int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
-		      int datasync, int isdir)
+		      int datasync, int opcode)
 {
 {
 	struct inode *inode = file->f_mapping->host;
 	struct inode *inode = file->f_mapping->host;
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	struct fuse_conn *fc = get_fuse_conn(inode);
 	struct fuse_file *ff = file->private_data;
 	struct fuse_file *ff = file->private_data;
 	FUSE_ARGS(args);
 	FUSE_ARGS(args);
 	struct fuse_fsync_in inarg;
 	struct fuse_fsync_in inarg;
+
+	memset(&inarg, 0, sizeof(inarg));
+	inarg.fh = ff->fh;
+	inarg.fsync_flags = datasync ? 1 : 0;
+	args.in.h.opcode = opcode;
+	args.in.h.nodeid = get_node_id(inode);
+	args.in.numargs = 1;
+	args.in.args[0].size = sizeof(inarg);
+	args.in.args[0].value = &inarg;
+	return fuse_simple_request(fc, &args);
+}
+
+static int fuse_fsync(struct file *file, loff_t start, loff_t end,
+		      int datasync)
+{
+	struct inode *inode = file->f_mapping->host;
+	struct fuse_conn *fc = get_fuse_conn(inode);
 	int err;
 	int err;
 
 
 	if (is_bad_inode(inode))
 	if (is_bad_inode(inode))
@@ -479,34 +496,18 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
 	if (err)
 	if (err)
 		goto out;
 		goto out;
 
 
-	if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
+	if (fc->no_fsync)
 		goto out;
 		goto out;
 
 
-	memset(&inarg, 0, sizeof(inarg));
-	inarg.fh = ff->fh;
-	inarg.fsync_flags = datasync ? 1 : 0;
-	args.in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC;
-	args.in.h.nodeid = get_node_id(inode);
-	args.in.numargs = 1;
-	args.in.args[0].size = sizeof(inarg);
-	args.in.args[0].value = &inarg;
-	err = fuse_simple_request(fc, &args);
+	err = fuse_fsync_common(file, start, end, datasync, FUSE_FSYNC);
 	if (err == -ENOSYS) {
 	if (err == -ENOSYS) {
-		if (isdir)
-			fc->no_fsyncdir = 1;
-		else
-			fc->no_fsync = 1;
+		fc->no_fsync = 1;
 		err = 0;
 		err = 0;
 	}
 	}
 out:
 out:
 	inode_unlock(inode);
 	inode_unlock(inode);
-	return err;
-}
 
 
-static int fuse_fsync(struct file *file, loff_t start, loff_t end,
-		      int datasync)
-{
-	return fuse_fsync_common(file, start, end, datasync, 0);
+	return err;
 }
 }
 
 
 void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,
 void fuse_read_fill(struct fuse_req *req, struct file *file, loff_t pos,

+ 1 - 1
fs/fuse/fuse_i.h

@@ -828,7 +828,7 @@ void fuse_release_common(struct file *file, int opcode);
  * Send FSYNC or FSYNCDIR request
  * Send FSYNC or FSYNCDIR request
  */
  */
 int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
 int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
-		      int datasync, int isdir);
+		      int datasync, int opcode);
 
 
 /**
 /**
  * Notify poll wakeup
  * Notify poll wakeup