fuse: fix fsync on directory
authorMiklos Szeredi <mszeredi@redhat.com>
Mon, 3 Dec 2018 09:14:43 +0000 (10:14 +0100)
committerMiklos Szeredi <mszeredi@redhat.com>
Mon, 3 Dec 2018 09:14:43 +0000 (10:14 +0100)
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>
fs/fuse/dir.c
fs/fuse/file.c
fs/fuse/fuse_i.h

index 47395b0c3b35e5fde0110eb142414010f06b126a..56931dfdcc46a1a5fe8c0d186f202482e327bc67 100644 (file)
@@ -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,
                          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,
index b52f9baaa3e7b9c98478a8c115748ae71fb7b0e1..677c51341e96f80b9536356171ea1383004f24fc 100644 (file)
@@ -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 datasync, int isdir)
+                     int datasync, int opcode)
 {
        struct inode *inode = file->f_mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
        struct fuse_file *ff = file->private_data;
        FUSE_ARGS(args);
        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;
 
        if (is_bad_inode(inode))
@@ -479,34 +496,18 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
        if (err)
                goto out;
 
-       if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir))
+       if (fc->no_fsync)
                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 (isdir)
-                       fc->no_fsyncdir = 1;
-               else
-                       fc->no_fsync = 1;
+               fc->no_fsync = 1;
                err = 0;
        }
 out:
        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,
index e9f712e81c7d9e188ae174d2bb3f0ba3b853d90f..afe1f231c758a67cb6faf2793ec4f11f5ba88da2 100644 (file)
@@ -828,7 +828,7 @@ void fuse_release_common(struct file *file, int opcode);
  * Send FSYNC or FSYNCDIR request
  */
 int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
-                     int datasync, int isdir);
+                     int datasync, int opcode);
 
 /**
  * Notify poll wakeup