| From foo@baz Mon Apr 9 17:09:24 CEST 2018 |
| From: Eryu Guan <eguan@redhat.com> |
| Date: Wed, 24 May 2017 18:02:20 -0400 |
| Subject: ext4: fix off-by-one on max nr_pages in ext4_find_unwritten_pgoff() |
| |
| From: Eryu Guan <eguan@redhat.com> |
| |
| |
| [ Upstream commit 624327f8794704c5066b11a52f9da6a09dce7f9a ] |
| |
| ext4_find_unwritten_pgoff() is used to search for offset of hole or |
| data in page range [index, end] (both inclusive), and the max number |
| of pages to search should be at least one, if end == index. |
| Otherwise the only page is missed and no hole or data is found, |
| which is not correct. |
| |
| When block size is smaller than page size, this can be demonstrated |
| by preallocating a file with size smaller than page size and writing |
| data to the last block. E.g. run this xfs_io command on a 1k block |
| size ext4 on x86_64 host. |
| |
| # xfs_io -fc "falloc 0 3k" -c "pwrite 2k 1k" \ |
| -c "seek -d 0" /mnt/ext4/testfile |
| wrote 1024/1024 bytes at offset 2048 |
| 1 KiB, 1 ops; 0.0000 sec (42.459 MiB/sec and 43478.2609 ops/sec) |
| Whence Result |
| DATA EOF |
| |
| Data at offset 2k was missed, and lseek(2) returned ENXIO. |
| |
| This is unconvered by generic/285 subtest 07 and 08 on ppc64 host, |
| where pagesize is 64k. Because a recent change to generic/285 |
| reduced the preallocated file size to smaller than 64k. |
| |
| Signed-off-by: Eryu Guan <eguan@redhat.com> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Reviewed-by: Jan Kara <jack@suse.cz> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ext4/file.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/fs/ext4/file.c |
| +++ b/fs/ext4/file.c |
| @@ -429,7 +429,7 @@ static int ext4_find_unwritten_pgoff(str |
| int i, num; |
| unsigned long nr_pages; |
| |
| - num = min_t(pgoff_t, end - index, PAGEVEC_SIZE); |
| + num = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1; |
| nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, |
| (pgoff_t)num); |
| if (nr_pages == 0) |