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