fuse: fix the ->direct_IO() treatment of iov_iter
authorAl Viro <viro@zeniv.linux.org.uk>
Thu, 17 Sep 2020 21:26:56 +0000 (17:26 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 17 Sep 2020 21:26:56 +0000 (17:26 -0400)
the callers rely upon having any iov_iter_truncate() done inside
->direct_IO() countered by iov_iter_reexpand().

Reported-by: Qian Cai <cai@redhat.com>
Tested-by: Qian Cai <cai@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/fuse/file.c

index 6611ef3..43c165e 100644 (file)
@@ -3091,11 +3091,10 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
        ssize_t ret = 0;
        struct file *file = iocb->ki_filp;
        struct fuse_file *ff = file->private_data;
-       bool async_dio = ff->fc->async_dio;
        loff_t pos = 0;
        struct inode *inode;
        loff_t i_size;
-       size_t count = iov_iter_count(iter);
+       size_t count = iov_iter_count(iter), shortened = 0;
        loff_t offset = iocb->ki_pos;
        struct fuse_io_priv *io;
 
@@ -3103,17 +3102,9 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
        inode = file->f_mapping->host;
        i_size = i_size_read(inode);
 
-       if ((iov_iter_rw(iter) == READ) && (offset > i_size))
+       if ((iov_iter_rw(iter) == READ) && (offset >= i_size))
                return 0;
 
-       /* optimization for short read */
-       if (async_dio && iov_iter_rw(iter) != WRITE && offset + count > i_size) {
-               if (offset >= i_size)
-                       return 0;
-               iov_iter_truncate(iter, fuse_round_up(ff->fc, i_size - offset));
-               count = iov_iter_count(iter);
-       }
-
        io = kmalloc(sizeof(struct fuse_io_priv), GFP_KERNEL);
        if (!io)
                return -ENOMEM;
@@ -3129,15 +3120,22 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
         * By default, we want to optimize all I/Os with async request
         * submission to the client filesystem if supported.
         */
-       io->async = async_dio;
+       io->async = ff->fc->async_dio;
        io->iocb = iocb;
        io->blocking = is_sync_kiocb(iocb);
 
+       /* optimization for short read */
+       if (io->async && !io->write && offset + count > i_size) {
+               iov_iter_truncate(iter, fuse_round_up(ff->fc, i_size - offset));
+               shortened = count - iov_iter_count(iter);
+               count -= shortened;
+       }
+
        /*
         * We cannot asynchronously extend the size of a file.
         * In such case the aio will behave exactly like sync io.
         */
-       if ((offset + count > i_size) && iov_iter_rw(iter) == WRITE)
+       if ((offset + count > i_size) && io->write)
                io->blocking = true;
 
        if (io->async && io->blocking) {
@@ -3155,6 +3153,7 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
        } else {
                ret = __fuse_direct_read(io, iter, &pos);
        }
+       iov_iter_reexpand(iter, iov_iter_count(iter) + shortened);
 
        if (io->async) {
                bool blocking = io->blocking;