| From foo@baz Sun Aug 26 09:13:00 CEST 2018 |
| From: Phillip Lougher <phillip@squashfs.org.uk> |
| Date: Thu, 2 Aug 2018 16:45:15 +0100 |
| Subject: Squashfs: Compute expected length from inode size rather than block length |
| |
| From: Phillip Lougher <phillip@squashfs.org.uk> |
| |
| [ Upstream commit a3f94cb99a854fa381fe7fadd97c4f61633717a5 ] |
| |
| Previously in squashfs_readpage() when copying data into the page |
| cache, it used the length of the datablock read from the filesystem |
| (after decompression). However, if the filesystem has been corrupted |
| this data block may be short, which will leave pages unfilled. |
| |
| The fix for this is to compute the expected number of bytes to copy |
| from the inode size, and use this to detect if the block is short. |
| |
| Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk> |
| Tested-by: Willy Tarreau <w@1wt.eu> |
| Cc: Анатолий Тросиненко <anatoly.trosinenko@gmail.com> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/squashfs/file.c | 25 ++++++++++--------------- |
| fs/squashfs/file_cache.c | 4 ++-- |
| fs/squashfs/file_direct.c | 16 +++++++++++----- |
| fs/squashfs/squashfs.h | 2 +- |
| 4 files changed, 24 insertions(+), 23 deletions(-) |
| |
| --- a/fs/squashfs/file.c |
| +++ b/fs/squashfs/file.c |
| @@ -431,10 +431,9 @@ skip_page: |
| } |
| |
| /* Read datablock stored packed inside a fragment (tail-end packed block) */ |
| -static int squashfs_readpage_fragment(struct page *page) |
| +static int squashfs_readpage_fragment(struct page *page, int expected) |
| { |
| struct inode *inode = page->mapping->host; |
| - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; |
| struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, |
| squashfs_i(inode)->fragment_block, |
| squashfs_i(inode)->fragment_size); |
| @@ -445,23 +444,16 @@ static int squashfs_readpage_fragment(st |
| squashfs_i(inode)->fragment_block, |
| squashfs_i(inode)->fragment_size); |
| else |
| - squashfs_copy_cache(page, buffer, i_size_read(inode) & |
| - (msblk->block_size - 1), |
| + squashfs_copy_cache(page, buffer, expected, |
| squashfs_i(inode)->fragment_offset); |
| |
| squashfs_cache_put(buffer); |
| return res; |
| } |
| |
| -static int squashfs_readpage_sparse(struct page *page, int index, int file_end) |
| +static int squashfs_readpage_sparse(struct page *page, int expected) |
| { |
| - struct inode *inode = page->mapping->host; |
| - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; |
| - int bytes = index == file_end ? |
| - (i_size_read(inode) & (msblk->block_size - 1)) : |
| - msblk->block_size; |
| - |
| - squashfs_copy_cache(page, NULL, bytes, 0); |
| + squashfs_copy_cache(page, NULL, expected, 0); |
| return 0; |
| } |
| |
| @@ -471,6 +463,9 @@ static int squashfs_readpage(struct file |
| struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; |
| int index = page->index >> (msblk->block_log - PAGE_SHIFT); |
| int file_end = i_size_read(inode) >> msblk->block_log; |
| + int expected = index == file_end ? |
| + (i_size_read(inode) & (msblk->block_size - 1)) : |
| + msblk->block_size; |
| int res; |
| void *pageaddr; |
| |
| @@ -489,11 +484,11 @@ static int squashfs_readpage(struct file |
| goto error_out; |
| |
| if (bsize == 0) |
| - res = squashfs_readpage_sparse(page, index, file_end); |
| + res = squashfs_readpage_sparse(page, expected); |
| else |
| - res = squashfs_readpage_block(page, block, bsize); |
| + res = squashfs_readpage_block(page, block, bsize, expected); |
| } else |
| - res = squashfs_readpage_fragment(page); |
| + res = squashfs_readpage_fragment(page, expected); |
| |
| if (!res) |
| return 0; |
| --- a/fs/squashfs/file_cache.c |
| +++ b/fs/squashfs/file_cache.c |
| @@ -20,7 +20,7 @@ |
| #include "squashfs.h" |
| |
| /* Read separately compressed datablock and memcopy into page cache */ |
| -int squashfs_readpage_block(struct page *page, u64 block, int bsize) |
| +int squashfs_readpage_block(struct page *page, u64 block, int bsize, int expected) |
| { |
| struct inode *i = page->mapping->host; |
| struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, |
| @@ -31,7 +31,7 @@ int squashfs_readpage_block(struct page |
| ERROR("Unable to read page, block %llx, size %x\n", block, |
| bsize); |
| else |
| - squashfs_copy_cache(page, buffer, buffer->length, 0); |
| + squashfs_copy_cache(page, buffer, expected, 0); |
| |
| squashfs_cache_put(buffer); |
| return res; |
| --- a/fs/squashfs/file_direct.c |
| +++ b/fs/squashfs/file_direct.c |
| @@ -21,10 +21,11 @@ |
| #include "page_actor.h" |
| |
| static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, |
| - int pages, struct page **page); |
| + int pages, struct page **page, int bytes); |
| |
| /* Read separately compressed datablock directly into page cache */ |
| -int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) |
| +int squashfs_readpage_block(struct page *target_page, u64 block, int bsize, |
| + int expected) |
| |
| { |
| struct inode *inode = target_page->mapping->host; |
| @@ -83,7 +84,7 @@ int squashfs_readpage_block(struct page |
| * using an intermediate buffer. |
| */ |
| res = squashfs_read_cache(target_page, block, bsize, pages, |
| - page); |
| + page, expected); |
| if (res < 0) |
| goto mark_errored; |
| |
| @@ -95,6 +96,11 @@ int squashfs_readpage_block(struct page |
| if (res < 0) |
| goto mark_errored; |
| |
| + if (res != expected) { |
| + res = -EIO; |
| + goto mark_errored; |
| + } |
| + |
| /* Last page may have trailing bytes not filled */ |
| bytes = res % PAGE_SIZE; |
| if (bytes) { |
| @@ -138,12 +144,12 @@ out: |
| |
| |
| static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, |
| - int pages, struct page **page) |
| + int pages, struct page **page, int bytes) |
| { |
| struct inode *i = target_page->mapping->host; |
| struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, |
| block, bsize); |
| - int bytes = buffer->length, res = buffer->error, n, offset = 0; |
| + int res = buffer->error, n, offset = 0; |
| |
| if (res) { |
| ERROR("Unable to read page, block %llx, size %x\n", block, |
| --- a/fs/squashfs/squashfs.h |
| +++ b/fs/squashfs/squashfs.h |
| @@ -72,7 +72,7 @@ void squashfs_copy_cache(struct page *, |
| int); |
| |
| /* file_xxx.c */ |
| -extern int squashfs_readpage_block(struct page *, u64, int); |
| +extern int squashfs_readpage_block(struct page *, u64, int, int); |
| |
| /* id.c */ |
| extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); |