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