162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Squashfs - a compressed read only filesystem for Linux
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
662306a36Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * file.c
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * This file contains code for handling regular files.  A regular file
1362306a36Sopenharmony_ci * consists of a sequence of contiguous compressed blocks, and/or a
1462306a36Sopenharmony_ci * compressed fragment block (tail-end packed block).   The compressed size
1562306a36Sopenharmony_ci * of each datablock is stored in a block list contained within the
1662306a36Sopenharmony_ci * file inode (itself stored in one or more compressed metadata blocks).
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * To speed up access to datablocks when reading 'large' files (256 Mbytes or
1962306a36Sopenharmony_ci * larger), the code implements an index cache that caches the mapping from
2062306a36Sopenharmony_ci * block index to datablock location on disk.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
2362306a36Sopenharmony_ci * retaining a simple and space-efficient block list on disk.  The cache
2462306a36Sopenharmony_ci * is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
2562306a36Sopenharmony_ci * Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
2662306a36Sopenharmony_ci * The index cache is designed to be memory efficient, and by default uses
2762306a36Sopenharmony_ci * 16 KiB.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <linux/fs.h>
3162306a36Sopenharmony_ci#include <linux/vfs.h>
3262306a36Sopenharmony_ci#include <linux/kernel.h>
3362306a36Sopenharmony_ci#include <linux/slab.h>
3462306a36Sopenharmony_ci#include <linux/string.h>
3562306a36Sopenharmony_ci#include <linux/pagemap.h>
3662306a36Sopenharmony_ci#include <linux/mutex.h>
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include "squashfs_fs.h"
3962306a36Sopenharmony_ci#include "squashfs_fs_sb.h"
4062306a36Sopenharmony_ci#include "squashfs_fs_i.h"
4162306a36Sopenharmony_ci#include "squashfs.h"
4262306a36Sopenharmony_ci#include "page_actor.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci * Locate cache slot in range [offset, index] for specified inode.  If
4662306a36Sopenharmony_ci * there's more than one return the slot closest to index.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic struct meta_index *locate_meta_index(struct inode *inode, int offset,
4962306a36Sopenharmony_ci				int index)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct meta_index *meta = NULL;
5262306a36Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
5362306a36Sopenharmony_ci	int i;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	mutex_lock(&msblk->meta_index_mutex);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (msblk->meta_index == NULL)
6062306a36Sopenharmony_ci		goto not_allocated;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
6362306a36Sopenharmony_ci		if (msblk->meta_index[i].inode_number == inode->i_ino &&
6462306a36Sopenharmony_ci				msblk->meta_index[i].offset >= offset &&
6562306a36Sopenharmony_ci				msblk->meta_index[i].offset <= index &&
6662306a36Sopenharmony_ci				msblk->meta_index[i].locked == 0) {
6762306a36Sopenharmony_ci			TRACE("locate_meta_index: entry %d, offset %d\n", i,
6862306a36Sopenharmony_ci					msblk->meta_index[i].offset);
6962306a36Sopenharmony_ci			meta = &msblk->meta_index[i];
7062306a36Sopenharmony_ci			offset = meta->offset;
7162306a36Sopenharmony_ci		}
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (meta)
7562306a36Sopenharmony_ci		meta->locked = 1;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cinot_allocated:
7862306a36Sopenharmony_ci	mutex_unlock(&msblk->meta_index_mutex);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return meta;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*
8562306a36Sopenharmony_ci * Find and initialise an empty cache slot for index offset.
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistatic struct meta_index *empty_meta_index(struct inode *inode, int offset,
8862306a36Sopenharmony_ci				int skip)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
9162306a36Sopenharmony_ci	struct meta_index *meta = NULL;
9262306a36Sopenharmony_ci	int i;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	mutex_lock(&msblk->meta_index_mutex);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (msblk->meta_index == NULL) {
9962306a36Sopenharmony_ci		/*
10062306a36Sopenharmony_ci		 * First time cache index has been used, allocate and
10162306a36Sopenharmony_ci		 * initialise.  The cache index could be allocated at
10262306a36Sopenharmony_ci		 * mount time but doing it here means it is allocated only
10362306a36Sopenharmony_ci		 * if a 'large' file is read.
10462306a36Sopenharmony_ci		 */
10562306a36Sopenharmony_ci		msblk->meta_index = kcalloc(SQUASHFS_META_SLOTS,
10662306a36Sopenharmony_ci			sizeof(*(msblk->meta_index)), GFP_KERNEL);
10762306a36Sopenharmony_ci		if (msblk->meta_index == NULL) {
10862306a36Sopenharmony_ci			ERROR("Failed to allocate meta_index\n");
10962306a36Sopenharmony_ci			goto failed;
11062306a36Sopenharmony_ci		}
11162306a36Sopenharmony_ci		for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
11262306a36Sopenharmony_ci			msblk->meta_index[i].inode_number = 0;
11362306a36Sopenharmony_ci			msblk->meta_index[i].locked = 0;
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci		msblk->next_meta_index = 0;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	for (i = SQUASHFS_META_SLOTS; i &&
11962306a36Sopenharmony_ci			msblk->meta_index[msblk->next_meta_index].locked; i--)
12062306a36Sopenharmony_ci		msblk->next_meta_index = (msblk->next_meta_index + 1) %
12162306a36Sopenharmony_ci			SQUASHFS_META_SLOTS;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (i == 0) {
12462306a36Sopenharmony_ci		TRACE("empty_meta_index: failed!\n");
12562306a36Sopenharmony_ci		goto failed;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	TRACE("empty_meta_index: returned meta entry %d, %p\n",
12962306a36Sopenharmony_ci			msblk->next_meta_index,
13062306a36Sopenharmony_ci			&msblk->meta_index[msblk->next_meta_index]);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	meta = &msblk->meta_index[msblk->next_meta_index];
13362306a36Sopenharmony_ci	msblk->next_meta_index = (msblk->next_meta_index + 1) %
13462306a36Sopenharmony_ci			SQUASHFS_META_SLOTS;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	meta->inode_number = inode->i_ino;
13762306a36Sopenharmony_ci	meta->offset = offset;
13862306a36Sopenharmony_ci	meta->skip = skip;
13962306a36Sopenharmony_ci	meta->entries = 0;
14062306a36Sopenharmony_ci	meta->locked = 1;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cifailed:
14362306a36Sopenharmony_ci	mutex_unlock(&msblk->meta_index_mutex);
14462306a36Sopenharmony_ci	return meta;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic void release_meta_index(struct inode *inode, struct meta_index *meta)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
15162306a36Sopenharmony_ci	mutex_lock(&msblk->meta_index_mutex);
15262306a36Sopenharmony_ci	meta->locked = 0;
15362306a36Sopenharmony_ci	mutex_unlock(&msblk->meta_index_mutex);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/*
15862306a36Sopenharmony_ci * Read the next n blocks from the block list, starting from
15962306a36Sopenharmony_ci * metadata block <start_block, offset>.
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic long long read_indexes(struct super_block *sb, int n,
16262306a36Sopenharmony_ci				u64 *start_block, int *offset)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	int err, i;
16562306a36Sopenharmony_ci	long long block = 0;
16662306a36Sopenharmony_ci	__le32 *blist = kmalloc(PAGE_SIZE, GFP_KERNEL);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (blist == NULL) {
16962306a36Sopenharmony_ci		ERROR("read_indexes: Failed to allocate block_list\n");
17062306a36Sopenharmony_ci		return -ENOMEM;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	while (n) {
17462306a36Sopenharmony_ci		int blocks = min_t(int, n, PAGE_SIZE >> 2);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		err = squashfs_read_metadata(sb, blist, start_block,
17762306a36Sopenharmony_ci				offset, blocks << 2);
17862306a36Sopenharmony_ci		if (err < 0) {
17962306a36Sopenharmony_ci			ERROR("read_indexes: reading block [%llx:%x]\n",
18062306a36Sopenharmony_ci				*start_block, *offset);
18162306a36Sopenharmony_ci			goto failure;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		for (i = 0; i < blocks; i++) {
18562306a36Sopenharmony_ci			int size = squashfs_block_size(blist[i]);
18662306a36Sopenharmony_ci			if (size < 0) {
18762306a36Sopenharmony_ci				err = size;
18862306a36Sopenharmony_ci				goto failure;
18962306a36Sopenharmony_ci			}
19062306a36Sopenharmony_ci			block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci		n -= blocks;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	kfree(blist);
19662306a36Sopenharmony_ci	return block;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cifailure:
19962306a36Sopenharmony_ci	kfree(blist);
20062306a36Sopenharmony_ci	return err;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/*
20562306a36Sopenharmony_ci * Each cache index slot has SQUASHFS_META_ENTRIES, each of which
20662306a36Sopenharmony_ci * can cache one index -> datablock/blocklist-block mapping.  We wish
20762306a36Sopenharmony_ci * to distribute these over the length of the file, entry[0] maps index x,
20862306a36Sopenharmony_ci * entry[1] maps index x + skip, entry[2] maps index x + 2 * skip, and so on.
20962306a36Sopenharmony_ci * The larger the file, the greater the skip factor.  The skip factor is
21062306a36Sopenharmony_ci * limited to the size of the metadata cache (SQUASHFS_CACHED_BLKS) to ensure
21162306a36Sopenharmony_ci * the number of metadata blocks that need to be read fits into the cache.
21262306a36Sopenharmony_ci * If the skip factor is limited in this way then the file will use multiple
21362306a36Sopenharmony_ci * slots.
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_cistatic inline int calculate_skip(u64 blocks)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	u64 skip = blocks / ((SQUASHFS_META_ENTRIES + 1)
21862306a36Sopenharmony_ci		 * SQUASHFS_META_INDEXES);
21962306a36Sopenharmony_ci	return min((u64) SQUASHFS_CACHED_BLKS - 1, skip + 1);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/*
22462306a36Sopenharmony_ci * Search and grow the index cache for the specified inode, returning the
22562306a36Sopenharmony_ci * on-disk locations of the datablock and block list metadata block
22662306a36Sopenharmony_ci * <index_block, index_offset> for index (scaled to nearest cache index).
22762306a36Sopenharmony_ci */
22862306a36Sopenharmony_cistatic int fill_meta_index(struct inode *inode, int index,
22962306a36Sopenharmony_ci		u64 *index_block, int *index_offset, u64 *data_block)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
23262306a36Sopenharmony_ci	int skip = calculate_skip(i_size_read(inode) >> msblk->block_log);
23362306a36Sopenharmony_ci	int offset = 0;
23462306a36Sopenharmony_ci	struct meta_index *meta;
23562306a36Sopenharmony_ci	struct meta_entry *meta_entry;
23662306a36Sopenharmony_ci	u64 cur_index_block = squashfs_i(inode)->block_list_start;
23762306a36Sopenharmony_ci	int cur_offset = squashfs_i(inode)->offset;
23862306a36Sopenharmony_ci	u64 cur_data_block = squashfs_i(inode)->start;
23962306a36Sopenharmony_ci	int err, i;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/*
24262306a36Sopenharmony_ci	 * Scale index to cache index (cache slot entry)
24362306a36Sopenharmony_ci	 */
24462306a36Sopenharmony_ci	index /= SQUASHFS_META_INDEXES * skip;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	while (offset < index) {
24762306a36Sopenharmony_ci		meta = locate_meta_index(inode, offset + 1, index);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		if (meta == NULL) {
25062306a36Sopenharmony_ci			meta = empty_meta_index(inode, offset + 1, skip);
25162306a36Sopenharmony_ci			if (meta == NULL)
25262306a36Sopenharmony_ci				goto all_done;
25362306a36Sopenharmony_ci		} else {
25462306a36Sopenharmony_ci			offset = index < meta->offset + meta->entries ? index :
25562306a36Sopenharmony_ci				meta->offset + meta->entries - 1;
25662306a36Sopenharmony_ci			meta_entry = &meta->meta_entry[offset - meta->offset];
25762306a36Sopenharmony_ci			cur_index_block = meta_entry->index_block +
25862306a36Sopenharmony_ci				msblk->inode_table;
25962306a36Sopenharmony_ci			cur_offset = meta_entry->offset;
26062306a36Sopenharmony_ci			cur_data_block = meta_entry->data_block;
26162306a36Sopenharmony_ci			TRACE("get_meta_index: offset %d, meta->offset %d, "
26262306a36Sopenharmony_ci				"meta->entries %d\n", offset, meta->offset,
26362306a36Sopenharmony_ci				meta->entries);
26462306a36Sopenharmony_ci			TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
26562306a36Sopenharmony_ci				" data_block 0x%llx\n", cur_index_block,
26662306a36Sopenharmony_ci				cur_offset, cur_data_block);
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		/*
27062306a36Sopenharmony_ci		 * If necessary grow cache slot by reading block list.  Cache
27162306a36Sopenharmony_ci		 * slot is extended up to index or to the end of the slot, in
27262306a36Sopenharmony_ci		 * which case further slots will be used.
27362306a36Sopenharmony_ci		 */
27462306a36Sopenharmony_ci		for (i = meta->offset + meta->entries; i <= index &&
27562306a36Sopenharmony_ci				i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
27662306a36Sopenharmony_ci			int blocks = skip * SQUASHFS_META_INDEXES;
27762306a36Sopenharmony_ci			long long res = read_indexes(inode->i_sb, blocks,
27862306a36Sopenharmony_ci					&cur_index_block, &cur_offset);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci			if (res < 0) {
28162306a36Sopenharmony_ci				if (meta->entries == 0)
28262306a36Sopenharmony_ci					/*
28362306a36Sopenharmony_ci					 * Don't leave an empty slot on read
28462306a36Sopenharmony_ci					 * error allocated to this inode...
28562306a36Sopenharmony_ci					 */
28662306a36Sopenharmony_ci					meta->inode_number = 0;
28762306a36Sopenharmony_ci				err = res;
28862306a36Sopenharmony_ci				goto failed;
28962306a36Sopenharmony_ci			}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci			cur_data_block += res;
29262306a36Sopenharmony_ci			meta_entry = &meta->meta_entry[i - meta->offset];
29362306a36Sopenharmony_ci			meta_entry->index_block = cur_index_block -
29462306a36Sopenharmony_ci				msblk->inode_table;
29562306a36Sopenharmony_ci			meta_entry->offset = cur_offset;
29662306a36Sopenharmony_ci			meta_entry->data_block = cur_data_block;
29762306a36Sopenharmony_ci			meta->entries++;
29862306a36Sopenharmony_ci			offset++;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
30262306a36Sopenharmony_ci				meta->offset, meta->entries);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		release_meta_index(inode, meta);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ciall_done:
30862306a36Sopenharmony_ci	*index_block = cur_index_block;
30962306a36Sopenharmony_ci	*index_offset = cur_offset;
31062306a36Sopenharmony_ci	*data_block = cur_data_block;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/*
31362306a36Sopenharmony_ci	 * Scale cache index (cache slot entry) to index
31462306a36Sopenharmony_ci	 */
31562306a36Sopenharmony_ci	return offset * SQUASHFS_META_INDEXES * skip;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cifailed:
31862306a36Sopenharmony_ci	release_meta_index(inode, meta);
31962306a36Sopenharmony_ci	return err;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/*
32462306a36Sopenharmony_ci * Get the on-disk location and compressed size of the datablock
32562306a36Sopenharmony_ci * specified by index.  Fill_meta_index() does most of the work.
32662306a36Sopenharmony_ci */
32762306a36Sopenharmony_cistatic int read_blocklist(struct inode *inode, int index, u64 *block)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	u64 start;
33062306a36Sopenharmony_ci	long long blks;
33162306a36Sopenharmony_ci	int offset;
33262306a36Sopenharmony_ci	__le32 size;
33362306a36Sopenharmony_ci	int res = fill_meta_index(inode, index, &start, &offset, block);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset"
33662306a36Sopenharmony_ci		       " 0x%x, block 0x%llx\n", res, index, start, offset,
33762306a36Sopenharmony_ci			*block);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (res < 0)
34062306a36Sopenharmony_ci		return res;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * res contains the index of the mapping returned by fill_meta_index(),
34462306a36Sopenharmony_ci	 * this will likely be less than the desired index (because the
34562306a36Sopenharmony_ci	 * meta_index cache works at a higher granularity).  Read any
34662306a36Sopenharmony_ci	 * extra block indexes needed.
34762306a36Sopenharmony_ci	 */
34862306a36Sopenharmony_ci	if (res < index) {
34962306a36Sopenharmony_ci		blks = read_indexes(inode->i_sb, index - res, &start, &offset);
35062306a36Sopenharmony_ci		if (blks < 0)
35162306a36Sopenharmony_ci			return (int) blks;
35262306a36Sopenharmony_ci		*block += blks;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/*
35662306a36Sopenharmony_ci	 * Read length of block specified by index.
35762306a36Sopenharmony_ci	 */
35862306a36Sopenharmony_ci	res = squashfs_read_metadata(inode->i_sb, &size, &start, &offset,
35962306a36Sopenharmony_ci			sizeof(size));
36062306a36Sopenharmony_ci	if (res < 0)
36162306a36Sopenharmony_ci		return res;
36262306a36Sopenharmony_ci	return squashfs_block_size(size);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_civoid squashfs_fill_page(struct page *page, struct squashfs_cache_entry *buffer, int offset, int avail)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	int copied;
36862306a36Sopenharmony_ci	void *pageaddr;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	pageaddr = kmap_atomic(page);
37162306a36Sopenharmony_ci	copied = squashfs_copy_data(pageaddr, buffer, offset, avail);
37262306a36Sopenharmony_ci	memset(pageaddr + copied, 0, PAGE_SIZE - copied);
37362306a36Sopenharmony_ci	kunmap_atomic(pageaddr);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	flush_dcache_page(page);
37662306a36Sopenharmony_ci	if (copied == avail)
37762306a36Sopenharmony_ci		SetPageUptodate(page);
37862306a36Sopenharmony_ci	else
37962306a36Sopenharmony_ci		SetPageError(page);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/* Copy data into page cache  */
38362306a36Sopenharmony_civoid squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
38462306a36Sopenharmony_ci	int bytes, int offset)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct inode *inode = page->mapping->host;
38762306a36Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
38862306a36Sopenharmony_ci	int i, mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
38962306a36Sopenharmony_ci	int start_index = page->index & ~mask, end_index = start_index | mask;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/*
39262306a36Sopenharmony_ci	 * Loop copying datablock into pages.  As the datablock likely covers
39362306a36Sopenharmony_ci	 * many PAGE_SIZE pages (default block size is 128 KiB) explicitly
39462306a36Sopenharmony_ci	 * grab the pages from the page cache, except for the page that we've
39562306a36Sopenharmony_ci	 * been called to fill.
39662306a36Sopenharmony_ci	 */
39762306a36Sopenharmony_ci	for (i = start_index; i <= end_index && bytes > 0; i++,
39862306a36Sopenharmony_ci			bytes -= PAGE_SIZE, offset += PAGE_SIZE) {
39962306a36Sopenharmony_ci		struct page *push_page;
40062306a36Sopenharmony_ci		int avail = buffer ? min_t(int, bytes, PAGE_SIZE) : 0;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		push_page = (i == page->index) ? page :
40562306a36Sopenharmony_ci			grab_cache_page_nowait(page->mapping, i);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		if (!push_page)
40862306a36Sopenharmony_ci			continue;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		if (PageUptodate(push_page))
41162306a36Sopenharmony_ci			goto skip_page;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		squashfs_fill_page(push_page, buffer, offset, avail);
41462306a36Sopenharmony_ciskip_page:
41562306a36Sopenharmony_ci		unlock_page(push_page);
41662306a36Sopenharmony_ci		if (i != page->index)
41762306a36Sopenharmony_ci			put_page(push_page);
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci/* Read datablock stored packed inside a fragment (tail-end packed block) */
42262306a36Sopenharmony_cistatic int squashfs_readpage_fragment(struct page *page, int expected)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct inode *inode = page->mapping->host;
42562306a36Sopenharmony_ci	struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb,
42662306a36Sopenharmony_ci		squashfs_i(inode)->fragment_block,
42762306a36Sopenharmony_ci		squashfs_i(inode)->fragment_size);
42862306a36Sopenharmony_ci	int res = buffer->error;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (res)
43162306a36Sopenharmony_ci		ERROR("Unable to read page, block %llx, size %x\n",
43262306a36Sopenharmony_ci			squashfs_i(inode)->fragment_block,
43362306a36Sopenharmony_ci			squashfs_i(inode)->fragment_size);
43462306a36Sopenharmony_ci	else
43562306a36Sopenharmony_ci		squashfs_copy_cache(page, buffer, expected,
43662306a36Sopenharmony_ci			squashfs_i(inode)->fragment_offset);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	squashfs_cache_put(buffer);
43962306a36Sopenharmony_ci	return res;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int squashfs_readpage_sparse(struct page *page, int expected)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	squashfs_copy_cache(page, NULL, expected, 0);
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int squashfs_read_folio(struct file *file, struct folio *folio)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct page *page = &folio->page;
45162306a36Sopenharmony_ci	struct inode *inode = page->mapping->host;
45262306a36Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
45362306a36Sopenharmony_ci	int index = page->index >> (msblk->block_log - PAGE_SHIFT);
45462306a36Sopenharmony_ci	int file_end = i_size_read(inode) >> msblk->block_log;
45562306a36Sopenharmony_ci	int expected = index == file_end ?
45662306a36Sopenharmony_ci			(i_size_read(inode) & (msblk->block_size - 1)) :
45762306a36Sopenharmony_ci			 msblk->block_size;
45862306a36Sopenharmony_ci	int res = 0;
45962306a36Sopenharmony_ci	void *pageaddr;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
46262306a36Sopenharmony_ci				page->index, squashfs_i(inode)->start);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (page->index >= ((i_size_read(inode) + PAGE_SIZE - 1) >>
46562306a36Sopenharmony_ci					PAGE_SHIFT))
46662306a36Sopenharmony_ci		goto out;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	if (index < file_end || squashfs_i(inode)->fragment_block ==
46962306a36Sopenharmony_ci					SQUASHFS_INVALID_BLK) {
47062306a36Sopenharmony_ci		u64 block = 0;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		res = read_blocklist(inode, index, &block);
47362306a36Sopenharmony_ci		if (res < 0)
47462306a36Sopenharmony_ci			goto error_out;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		if (res == 0)
47762306a36Sopenharmony_ci			res = squashfs_readpage_sparse(page, expected);
47862306a36Sopenharmony_ci		else
47962306a36Sopenharmony_ci			res = squashfs_readpage_block(page, block, res, expected);
48062306a36Sopenharmony_ci	} else
48162306a36Sopenharmony_ci		res = squashfs_readpage_fragment(page, expected);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (!res)
48462306a36Sopenharmony_ci		return 0;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cierror_out:
48762306a36Sopenharmony_ci	SetPageError(page);
48862306a36Sopenharmony_ciout:
48962306a36Sopenharmony_ci	pageaddr = kmap_atomic(page);
49062306a36Sopenharmony_ci	memset(pageaddr, 0, PAGE_SIZE);
49162306a36Sopenharmony_ci	kunmap_atomic(pageaddr);
49262306a36Sopenharmony_ci	flush_dcache_page(page);
49362306a36Sopenharmony_ci	if (res == 0)
49462306a36Sopenharmony_ci		SetPageUptodate(page);
49562306a36Sopenharmony_ci	unlock_page(page);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return res;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int squashfs_readahead_fragment(struct page **page,
50162306a36Sopenharmony_ci	unsigned int pages, unsigned int expected)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct inode *inode = page[0]->mapping->host;
50462306a36Sopenharmony_ci	struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb,
50562306a36Sopenharmony_ci		squashfs_i(inode)->fragment_block,
50662306a36Sopenharmony_ci		squashfs_i(inode)->fragment_size);
50762306a36Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
50862306a36Sopenharmony_ci	unsigned int n, mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
50962306a36Sopenharmony_ci	int error = buffer->error;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (error)
51262306a36Sopenharmony_ci		goto out;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	expected += squashfs_i(inode)->fragment_offset;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	for (n = 0; n < pages; n++) {
51762306a36Sopenharmony_ci		unsigned int base = (page[n]->index & mask) << PAGE_SHIFT;
51862306a36Sopenharmony_ci		unsigned int offset = base + squashfs_i(inode)->fragment_offset;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (expected > offset) {
52162306a36Sopenharmony_ci			unsigned int avail = min_t(unsigned int, expected -
52262306a36Sopenharmony_ci				offset, PAGE_SIZE);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci			squashfs_fill_page(page[n], buffer, offset, avail);
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		unlock_page(page[n]);
52862306a36Sopenharmony_ci		put_page(page[n]);
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ciout:
53262306a36Sopenharmony_ci	squashfs_cache_put(buffer);
53362306a36Sopenharmony_ci	return error;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void squashfs_readahead(struct readahead_control *ractl)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct inode *inode = ractl->mapping->host;
53962306a36Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
54062306a36Sopenharmony_ci	size_t mask = (1UL << msblk->block_log) - 1;
54162306a36Sopenharmony_ci	unsigned short shift = msblk->block_log - PAGE_SHIFT;
54262306a36Sopenharmony_ci	loff_t start = readahead_pos(ractl) & ~mask;
54362306a36Sopenharmony_ci	size_t len = readahead_length(ractl) + readahead_pos(ractl) - start;
54462306a36Sopenharmony_ci	struct squashfs_page_actor *actor;
54562306a36Sopenharmony_ci	unsigned int nr_pages = 0;
54662306a36Sopenharmony_ci	struct page **pages;
54762306a36Sopenharmony_ci	int i, file_end = i_size_read(inode) >> msblk->block_log;
54862306a36Sopenharmony_ci	unsigned int max_pages = 1UL << shift;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	readahead_expand(ractl, start, (len | mask) + 1);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	pages = kmalloc_array(max_pages, sizeof(void *), GFP_KERNEL);
55362306a36Sopenharmony_ci	if (!pages)
55462306a36Sopenharmony_ci		return;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	for (;;) {
55762306a36Sopenharmony_ci		pgoff_t index;
55862306a36Sopenharmony_ci		int res, bsize;
55962306a36Sopenharmony_ci		u64 block = 0;
56062306a36Sopenharmony_ci		unsigned int expected;
56162306a36Sopenharmony_ci		struct page *last_page;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		expected = start >> msblk->block_log == file_end ?
56462306a36Sopenharmony_ci			   (i_size_read(inode) & (msblk->block_size - 1)) :
56562306a36Sopenharmony_ci			    msblk->block_size;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		max_pages = (expected + PAGE_SIZE - 1) >> PAGE_SHIFT;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		nr_pages = __readahead_batch(ractl, pages, max_pages);
57062306a36Sopenharmony_ci		if (!nr_pages)
57162306a36Sopenharmony_ci			break;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (readahead_pos(ractl) >= i_size_read(inode))
57462306a36Sopenharmony_ci			goto skip_pages;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		index = pages[0]->index >> shift;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		if ((pages[nr_pages - 1]->index >> shift) != index)
57962306a36Sopenharmony_ci			goto skip_pages;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		if (index == file_end && squashfs_i(inode)->fragment_block !=
58262306a36Sopenharmony_ci						SQUASHFS_INVALID_BLK) {
58362306a36Sopenharmony_ci			res = squashfs_readahead_fragment(pages, nr_pages,
58462306a36Sopenharmony_ci							  expected);
58562306a36Sopenharmony_ci			if (res)
58662306a36Sopenharmony_ci				goto skip_pages;
58762306a36Sopenharmony_ci			continue;
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		bsize = read_blocklist(inode, index, &block);
59162306a36Sopenharmony_ci		if (bsize == 0)
59262306a36Sopenharmony_ci			goto skip_pages;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		actor = squashfs_page_actor_init_special(msblk, pages, nr_pages,
59562306a36Sopenharmony_ci							 expected);
59662306a36Sopenharmony_ci		if (!actor)
59762306a36Sopenharmony_ci			goto skip_pages;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		last_page = squashfs_page_actor_free(actor);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		if (res == expected) {
60462306a36Sopenharmony_ci			int bytes;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci			/* Last page (if present) may have trailing bytes not filled */
60762306a36Sopenharmony_ci			bytes = res % PAGE_SIZE;
60862306a36Sopenharmony_ci			if (index == file_end && bytes && last_page)
60962306a36Sopenharmony_ci				memzero_page(last_page, bytes,
61062306a36Sopenharmony_ci					     PAGE_SIZE - bytes);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci			for (i = 0; i < nr_pages; i++) {
61362306a36Sopenharmony_ci				flush_dcache_page(pages[i]);
61462306a36Sopenharmony_ci				SetPageUptodate(pages[i]);
61562306a36Sopenharmony_ci			}
61662306a36Sopenharmony_ci		}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		for (i = 0; i < nr_pages; i++) {
61962306a36Sopenharmony_ci			unlock_page(pages[i]);
62062306a36Sopenharmony_ci			put_page(pages[i]);
62162306a36Sopenharmony_ci		}
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	kfree(pages);
62562306a36Sopenharmony_ci	return;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ciskip_pages:
62862306a36Sopenharmony_ci	for (i = 0; i < nr_pages; i++) {
62962306a36Sopenharmony_ci		unlock_page(pages[i]);
63062306a36Sopenharmony_ci		put_page(pages[i]);
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci	kfree(pages);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ciconst struct address_space_operations squashfs_aops = {
63662306a36Sopenharmony_ci	.read_folio = squashfs_read_folio,
63762306a36Sopenharmony_ci	.readahead = squashfs_readahead
63862306a36Sopenharmony_ci};
639