summaryrefslogtreecommitdiff
path: root/fs/squashfs
diff options
context:
space:
mode:
authorVincent Whitchurch <vincent.whitchurch@axis.com>2023-06-29 16:17:57 +0200
committerAndrew Morton <akpm@linux-foundation.org>2023-07-08 09:29:30 -0700
commit08bab74ae653b57bb2bfcec7d499bfe7ff0efe4f (patch)
tree3c9262c96a5067ca13b7ef83e8c251e80a8d0041 /fs/squashfs
parent191fcdb6c9cf8b738b1628cbcf3af63d545c825c (diff)
squashfs: fix cache race with migration
Migration replaces the page in the mapping before copying the contents and the flags over from the old page, so check that the page in the page cache is really up to date before using it. Without this, stressing squashfs reads with parallel compaction sometimes results in squashfs reporting data corruption. Link: https://lkml.kernel.org/r/20230629-squashfs-cache-migration-v1-1-d50ebe55099d@axis.com Fixes: e994f5b677ee ("squashfs: cache partial compressed blocks") Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Phillip Lougher <phillip@squashfs.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'fs/squashfs')
-rw-r--r--fs/squashfs/block.c27
1 files changed, 23 insertions, 4 deletions
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index 6aa9c2e1e8eb..581ce9519339 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -166,6 +166,26 @@ static int squashfs_bio_read_cached(struct bio *fullbio,
return 0;
}
+static struct page *squashfs_get_cache_page(struct address_space *mapping,
+ pgoff_t index)
+{
+ struct page *page;
+
+ if (!mapping)
+ return NULL;
+
+ page = find_get_page(mapping, index);
+ if (!page)
+ return NULL;
+
+ if (!PageUptodate(page)) {
+ put_page(page);
+ return NULL;
+ }
+
+ return page;
+}
+
static int squashfs_bio_read(struct super_block *sb, u64 index, int length,
struct bio **biop, int *block_offset)
{
@@ -190,11 +210,10 @@ static int squashfs_bio_read(struct super_block *sb, u64 index, int length,
for (i = 0; i < page_count; ++i) {
unsigned int len =
min_t(unsigned int, PAGE_SIZE - offset, total_len);
- struct page *page = NULL;
+ pgoff_t index = (read_start >> PAGE_SHIFT) + i;
+ struct page *page;
- if (cache_mapping)
- page = find_get_page(cache_mapping,
- (read_start >> PAGE_SHIFT) + i);
+ page = squashfs_get_cache_page(cache_mapping, index);
if (!page)
page = alloc_page(GFP_NOIO);