18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2013
48c2ecf20Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/fs.h>
88c2ecf20Sopenharmony_ci#include <linux/vfs.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/string.h>
128c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
138c2ecf20Sopenharmony_ci#include <linux/mutex.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "squashfs_fs.h"
168c2ecf20Sopenharmony_ci#include "squashfs_fs_sb.h"
178c2ecf20Sopenharmony_ci#include "squashfs_fs_i.h"
188c2ecf20Sopenharmony_ci#include "squashfs.h"
198c2ecf20Sopenharmony_ci#include "page_actor.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
228c2ecf20Sopenharmony_ci	int pages, struct page **page, int bytes);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* Read separately compressed datablock directly into page cache */
258c2ecf20Sopenharmony_ciint squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
268c2ecf20Sopenharmony_ci	int expected)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct inode *inode = target_page->mapping->host;
308c2ecf20Sopenharmony_ci	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	int file_end = (i_size_read(inode) - 1) >> PAGE_SHIFT;
338c2ecf20Sopenharmony_ci	int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
348c2ecf20Sopenharmony_ci	int start_index = target_page->index & ~mask;
358c2ecf20Sopenharmony_ci	int end_index = start_index | mask;
368c2ecf20Sopenharmony_ci	int i, n, pages, missing_pages, bytes, res = -ENOMEM;
378c2ecf20Sopenharmony_ci	struct page **page;
388c2ecf20Sopenharmony_ci	struct squashfs_page_actor *actor;
398c2ecf20Sopenharmony_ci	void *pageaddr;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (end_index > file_end)
428c2ecf20Sopenharmony_ci		end_index = file_end;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	pages = end_index - start_index + 1;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL);
478c2ecf20Sopenharmony_ci	if (page == NULL)
488c2ecf20Sopenharmony_ci		return res;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/*
518c2ecf20Sopenharmony_ci	 * Create a "page actor" which will kmap and kunmap the
528c2ecf20Sopenharmony_ci	 * page cache pages appropriately within the decompressor
538c2ecf20Sopenharmony_ci	 */
548c2ecf20Sopenharmony_ci	actor = squashfs_page_actor_init_special(page, pages, 0);
558c2ecf20Sopenharmony_ci	if (actor == NULL)
568c2ecf20Sopenharmony_ci		goto out;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* Try to grab all the pages covered by the Squashfs block */
598c2ecf20Sopenharmony_ci	for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
608c2ecf20Sopenharmony_ci		page[i] = (n == target_page->index) ? target_page :
618c2ecf20Sopenharmony_ci			grab_cache_page_nowait(target_page->mapping, n);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		if (page[i] == NULL) {
648c2ecf20Sopenharmony_ci			missing_pages++;
658c2ecf20Sopenharmony_ci			continue;
668c2ecf20Sopenharmony_ci		}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		if (PageUptodate(page[i])) {
698c2ecf20Sopenharmony_ci			unlock_page(page[i]);
708c2ecf20Sopenharmony_ci			put_page(page[i]);
718c2ecf20Sopenharmony_ci			page[i] = NULL;
728c2ecf20Sopenharmony_ci			missing_pages++;
738c2ecf20Sopenharmony_ci		}
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (missing_pages) {
778c2ecf20Sopenharmony_ci		/*
788c2ecf20Sopenharmony_ci		 * Couldn't get one or more pages, this page has either
798c2ecf20Sopenharmony_ci		 * been VM reclaimed, but others are still in the page cache
808c2ecf20Sopenharmony_ci		 * and uptodate, or we're racing with another thread in
818c2ecf20Sopenharmony_ci		 * squashfs_readpage also trying to grab them.  Fall back to
828c2ecf20Sopenharmony_ci		 * using an intermediate buffer.
838c2ecf20Sopenharmony_ci		 */
848c2ecf20Sopenharmony_ci		res = squashfs_read_cache(target_page, block, bsize, pages,
858c2ecf20Sopenharmony_ci							page, expected);
868c2ecf20Sopenharmony_ci		if (res < 0)
878c2ecf20Sopenharmony_ci			goto mark_errored;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		goto out;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* Decompress directly into the page cache buffers */
938c2ecf20Sopenharmony_ci	res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
948c2ecf20Sopenharmony_ci	if (res < 0)
958c2ecf20Sopenharmony_ci		goto mark_errored;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (res != expected) {
988c2ecf20Sopenharmony_ci		res = -EIO;
998c2ecf20Sopenharmony_ci		goto mark_errored;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Last page may have trailing bytes not filled */
1038c2ecf20Sopenharmony_ci	bytes = res % PAGE_SIZE;
1048c2ecf20Sopenharmony_ci	if (bytes) {
1058c2ecf20Sopenharmony_ci		pageaddr = kmap_atomic(page[pages - 1]);
1068c2ecf20Sopenharmony_ci		memset(pageaddr + bytes, 0, PAGE_SIZE - bytes);
1078c2ecf20Sopenharmony_ci		kunmap_atomic(pageaddr);
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* Mark pages as uptodate, unlock and release */
1118c2ecf20Sopenharmony_ci	for (i = 0; i < pages; i++) {
1128c2ecf20Sopenharmony_ci		flush_dcache_page(page[i]);
1138c2ecf20Sopenharmony_ci		SetPageUptodate(page[i]);
1148c2ecf20Sopenharmony_ci		unlock_page(page[i]);
1158c2ecf20Sopenharmony_ci		if (page[i] != target_page)
1168c2ecf20Sopenharmony_ci			put_page(page[i]);
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	kfree(actor);
1208c2ecf20Sopenharmony_ci	kfree(page);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cimark_errored:
1258c2ecf20Sopenharmony_ci	/* Decompression failed, mark pages as errored.  Target_page is
1268c2ecf20Sopenharmony_ci	 * dealt with by the caller
1278c2ecf20Sopenharmony_ci	 */
1288c2ecf20Sopenharmony_ci	for (i = 0; i < pages; i++) {
1298c2ecf20Sopenharmony_ci		if (page[i] == NULL || page[i] == target_page)
1308c2ecf20Sopenharmony_ci			continue;
1318c2ecf20Sopenharmony_ci		flush_dcache_page(page[i]);
1328c2ecf20Sopenharmony_ci		SetPageError(page[i]);
1338c2ecf20Sopenharmony_ci		unlock_page(page[i]);
1348c2ecf20Sopenharmony_ci		put_page(page[i]);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ciout:
1388c2ecf20Sopenharmony_ci	kfree(actor);
1398c2ecf20Sopenharmony_ci	kfree(page);
1408c2ecf20Sopenharmony_ci	return res;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
1458c2ecf20Sopenharmony_ci	int pages, struct page **page, int bytes)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct inode *i = target_page->mapping->host;
1488c2ecf20Sopenharmony_ci	struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
1498c2ecf20Sopenharmony_ci						 block, bsize);
1508c2ecf20Sopenharmony_ci	int res = buffer->error, n, offset = 0;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (res) {
1538c2ecf20Sopenharmony_ci		ERROR("Unable to read page, block %llx, size %x\n", block,
1548c2ecf20Sopenharmony_ci			bsize);
1558c2ecf20Sopenharmony_ci		goto out;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	for (n = 0; n < pages && bytes > 0; n++,
1598c2ecf20Sopenharmony_ci			bytes -= PAGE_SIZE, offset += PAGE_SIZE) {
1608c2ecf20Sopenharmony_ci		int avail = min_t(int, bytes, PAGE_SIZE);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		if (page[n] == NULL)
1638c2ecf20Sopenharmony_ci			continue;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		squashfs_fill_page(page[n], buffer, offset, avail);
1668c2ecf20Sopenharmony_ci		unlock_page(page[n]);
1678c2ecf20Sopenharmony_ci		if (page[n] != target_page)
1688c2ecf20Sopenharmony_ci			put_page(page[n]);
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ciout:
1728c2ecf20Sopenharmony_ci	squashfs_cache_put(buffer);
1738c2ecf20Sopenharmony_ci	return res;
1748c2ecf20Sopenharmony_ci}
175