diff options
author | Al Viro <viro@ZenIV.linux.org.uk> | 2017-09-11 21:17:09 +0100 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2017-10-11 22:38:02 -0400 |
commit | cfe057f7db1ff026c8db75469a3f9ba9736e1975 (patch) | |
tree | 95f92a36480525b3918b6e1a6d9b26d236160565 /fs/iomap.c | |
parent | 8ececffa12f5555171075ce52e1226f570836b26 (diff) | |
download | lwn-cfe057f7db1ff026c8db75469a3f9ba9736e1975.tar.gz lwn-cfe057f7db1ff026c8db75469a3f9ba9736e1975.zip |
iomap_dio_actor(): fix iov_iter bugs
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>
Diffstat (limited to 'fs/iomap.c')
-rw-r--r-- | fs/iomap.c | 24 |
1 files changed, 15 insertions, 9 deletions
diff --git a/fs/iomap.c b/fs/iomap.c index be61cf742b5e..9c41008833ac 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -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 |