Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Squashfs: Compute expected length from inode size rather than block length

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>

authored by

Phillip Lougher and committed by
Linus Torvalds
a3f94cb9 71755ee5

+24 -23
+10 -15
fs/squashfs/file.c
··· 431 431 } 432 432 433 433 /* Read datablock stored packed inside a fragment (tail-end packed block) */ 434 - static int squashfs_readpage_fragment(struct page *page) 434 + static int squashfs_readpage_fragment(struct page *page, int expected) 435 435 { 436 436 struct inode *inode = page->mapping->host; 437 - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 438 437 struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, 439 438 squashfs_i(inode)->fragment_block, 440 439 squashfs_i(inode)->fragment_size); ··· 444 445 squashfs_i(inode)->fragment_block, 445 446 squashfs_i(inode)->fragment_size); 446 447 else 447 - squashfs_copy_cache(page, buffer, i_size_read(inode) & 448 - (msblk->block_size - 1), 448 + squashfs_copy_cache(page, buffer, expected, 449 449 squashfs_i(inode)->fragment_offset); 450 450 451 451 squashfs_cache_put(buffer); 452 452 return res; 453 453 } 454 454 455 - static int squashfs_readpage_sparse(struct page *page, int index, int file_end) 455 + static int squashfs_readpage_sparse(struct page *page, int expected) 456 456 { 457 - struct inode *inode = page->mapping->host; 458 - struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 459 - int bytes = index == file_end ? 460 - (i_size_read(inode) & (msblk->block_size - 1)) : 461 - msblk->block_size; 462 - 463 - squashfs_copy_cache(page, NULL, bytes, 0); 457 + squashfs_copy_cache(page, NULL, expected, 0); 464 458 return 0; 465 459 } 466 460 ··· 463 471 struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 464 472 int index = page->index >> (msblk->block_log - PAGE_SHIFT); 465 473 int file_end = i_size_read(inode) >> msblk->block_log; 474 + int expected = index == file_end ? 475 + (i_size_read(inode) & (msblk->block_size - 1)) : 476 + msblk->block_size; 466 477 int res; 467 478 void *pageaddr; 468 479 ··· 484 489 goto error_out; 485 490 486 491 if (bsize == 0) 487 - res = squashfs_readpage_sparse(page, index, file_end); 492 + res = squashfs_readpage_sparse(page, expected); 488 493 else 489 - res = squashfs_readpage_block(page, block, bsize); 494 + res = squashfs_readpage_block(page, block, bsize, expected); 490 495 } else 491 - res = squashfs_readpage_fragment(page); 496 + res = squashfs_readpage_fragment(page, expected); 492 497 493 498 if (!res) 494 499 return 0;
+2 -2
fs/squashfs/file_cache.c
··· 20 20 #include "squashfs.h" 21 21 22 22 /* Read separately compressed datablock and memcopy into page cache */ 23 - int squashfs_readpage_block(struct page *page, u64 block, int bsize) 23 + int squashfs_readpage_block(struct page *page, u64 block, int bsize, int expected) 24 24 { 25 25 struct inode *i = page->mapping->host; 26 26 struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, ··· 31 31 ERROR("Unable to read page, block %llx, size %x\n", block, 32 32 bsize); 33 33 else 34 - squashfs_copy_cache(page, buffer, buffer->length, 0); 34 + squashfs_copy_cache(page, buffer, expected, 0); 35 35 36 36 squashfs_cache_put(buffer); 37 37 return res;
+11 -5
fs/squashfs/file_direct.c
··· 21 21 #include "page_actor.h" 22 22 23 23 static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, 24 - int pages, struct page **page); 24 + int pages, struct page **page, int bytes); 25 25 26 26 /* Read separately compressed datablock directly into page cache */ 27 - int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) 27 + int squashfs_readpage_block(struct page *target_page, u64 block, int bsize, 28 + int expected) 28 29 29 30 { 30 31 struct inode *inode = target_page->mapping->host; ··· 84 83 * using an intermediate buffer. 85 84 */ 86 85 res = squashfs_read_cache(target_page, block, bsize, pages, 87 - page); 86 + page, expected); 88 87 if (res < 0) 89 88 goto mark_errored; 90 89 ··· 95 94 res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); 96 95 if (res < 0) 97 96 goto mark_errored; 97 + 98 + if (res != expected) { 99 + res = -EIO; 100 + goto mark_errored; 101 + } 98 102 99 103 /* Last page may have trailing bytes not filled */ 100 104 bytes = res % PAGE_SIZE; ··· 144 138 145 139 146 140 static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, 147 - int pages, struct page **page) 141 + int pages, struct page **page, int bytes) 148 142 { 149 143 struct inode *i = target_page->mapping->host; 150 144 struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, 151 145 block, bsize); 152 - int bytes = buffer->length, res = buffer->error, n, offset = 0; 146 + int res = buffer->error, n, offset = 0; 153 147 154 148 if (res) { 155 149 ERROR("Unable to read page, block %llx, size %x\n", block,
+1 -1
fs/squashfs/squashfs.h
··· 72 72 int); 73 73 74 74 /* file_xxx.c */ 75 - extern int squashfs_readpage_block(struct page *, u64, int); 75 + extern int squashfs_readpage_block(struct page *, u64, int, int); 76 76 77 77 /* id.c */ 78 78 extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);