iomap_dio_actor(): fix iov_iter bugs
authorAl Viro <viro@ZenIV.linux.org.uk>
Mon, 11 Sep 2017 20:17:09 +0000 (21:17 +0100)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 12 Oct 2017 02:38:02 +0000 (22:38 -0400)
1) Ignoring return value from iov_iter_zero() is wrong
for iovec-backed case as well as for pipes - it can fail.

2) Failure to fault destination pages in 25Mb into a 50Mb iovec
should not act as if nothing in the area had been read, nevermind
that the first 25Mb might have *already* been read by that point.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/iomap.c

index be61cf7..9c41008 100644 (file)
@@ -848,6 +848,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
        struct bio *bio;
        bool need_zeroout = false;
        int nr_pages, ret;
+       size_t copied = 0;
 
        if ((pos | length | align) & ((1 << blkbits) - 1))
                return -EINVAL;
@@ -859,7 +860,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
                /*FALLTHRU*/
        case IOMAP_UNWRITTEN:
                if (!(dio->flags & IOMAP_DIO_WRITE)) {
-                       iov_iter_zero(length, dio->submit.iter);
+                       length = iov_iter_zero(length, dio->submit.iter);
                        dio->size += length;
                        return length;
                }
@@ -896,8 +897,11 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
        }
 
        do {
-               if (dio->error)
+               size_t n;
+               if (dio->error) {
+                       iov_iter_revert(dio->submit.iter, copied);
                        return 0;
+               }
 
                bio = bio_alloc(GFP_KERNEL, nr_pages);
                bio_set_dev(bio, iomap->bdev);
@@ -910,20 +914,24 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
                ret = bio_iov_iter_get_pages(bio, &iter);
                if (unlikely(ret)) {
                        bio_put(bio);
-                       return ret;
+                       return copied ? copied : ret;
                }
 
+               n = bio->bi_iter.bi_size;
                if (dio->flags & IOMAP_DIO_WRITE) {
                        bio_set_op_attrs(bio, REQ_OP_WRITE, REQ_SYNC | REQ_IDLE);
-                       task_io_account_write(bio->bi_iter.bi_size);
+                       task_io_account_write(n);
                } else {
                        bio_set_op_attrs(bio, REQ_OP_READ, 0);
                        if (dio->flags & IOMAP_DIO_DIRTY)
                                bio_set_pages_dirty(bio);
                }
 
-               dio->size += bio->bi_iter.bi_size;
-               pos += bio->bi_iter.bi_size;
+               iov_iter_advance(dio->submit.iter, n);
+
+               dio->size += n;
+               pos += n;
+               copied += n;
 
                nr_pages = iov_iter_npages(&iter, BIO_MAX_PAGES);
 
@@ -939,9 +947,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length,
                if (pad)
                        iomap_dio_zero(dio, iomap, pos, fs_block_size - pad);
        }
-
-       iov_iter_advance(dio->submit.iter, length);
-       return length;
+       return copied;
 }
 
 ssize_t