Skip to content

Commit

Permalink
Squashfs: Compute expected length from inode size rather than block l…
Browse files Browse the repository at this point in the history
…ength

[ Upstream commit a3f94cb ]

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 <[email protected]>
Tested-by: Willy Tarreau <[email protected]>
Cc: Анатолий Тросиненко <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
plougher authored and gregkh committed Sep 5, 2018
1 parent 249778d commit 28013ee
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 23 deletions.
25 changes: 10 additions & 15 deletions fs/squashfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,9 @@ void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
}

/* 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);
Expand All @@ -445,23 +444,16 @@ static int squashfs_readpage_fragment(struct page *page)
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;
}

Expand All @@ -471,6 +463,9 @@ static int squashfs_readpage(struct file *file, struct page *page)
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;

Expand All @@ -489,11 +484,11 @@ static int squashfs_readpage(struct file *file, struct page *page)
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;
Expand Down
4 changes: 2 additions & 2 deletions fs/squashfs/file_cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -31,7 +31,7 @@ int squashfs_readpage_block(struct page *page, u64 block, int bsize)
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;
Expand Down
16 changes: 11 additions & 5 deletions fs/squashfs/file_direct.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -83,7 +84,7 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
* using an intermediate buffer.
*/
res = squashfs_read_cache(target_page, block, bsize, pages,
page);
page, expected);
if (res < 0)
goto mark_errored;

Expand All @@ -95,6 +96,11 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
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) {
Expand Down Expand Up @@ -138,12 +144,12 @@ int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)


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,
Expand Down
2 changes: 1 addition & 1 deletion fs/squashfs/squashfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
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 *);
Expand Down

0 comments on commit 28013ee

Please sign in to comment.