168252eb5SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21701aecbSPhillip Lougher /* 31701aecbSPhillip Lougher * Squashfs - a compressed read only filesystem for Linux 41701aecbSPhillip Lougher * 51701aecbSPhillip Lougher * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 6d7f2ff67SPhillip Lougher * Phillip Lougher <phillip@squashfs.org.uk> 71701aecbSPhillip Lougher * 81701aecbSPhillip Lougher * file.c 91701aecbSPhillip Lougher */ 101701aecbSPhillip Lougher 111701aecbSPhillip Lougher /* 121701aecbSPhillip Lougher * This file contains code for handling regular files. A regular file 131701aecbSPhillip Lougher * consists of a sequence of contiguous compressed blocks, and/or a 141701aecbSPhillip Lougher * compressed fragment block (tail-end packed block). The compressed size 151701aecbSPhillip Lougher * of each datablock is stored in a block list contained within the 161701aecbSPhillip Lougher * file inode (itself stored in one or more compressed metadata blocks). 171701aecbSPhillip Lougher * 181701aecbSPhillip Lougher * To speed up access to datablocks when reading 'large' files (256 Mbytes or 191701aecbSPhillip Lougher * larger), the code implements an index cache that caches the mapping from 201701aecbSPhillip Lougher * block index to datablock location on disk. 211701aecbSPhillip Lougher * 221701aecbSPhillip Lougher * The index cache allows Squashfs to handle large files (up to 1.75 TiB) while 231701aecbSPhillip Lougher * retaining a simple and space-efficient block list on disk. The cache 241701aecbSPhillip Lougher * is split into slots, caching up to eight 224 GiB files (128 KiB blocks). 251701aecbSPhillip Lougher * Larger files use multiple slots, with 1.75 TiB files using all 8 slots. 261701aecbSPhillip Lougher * The index cache is designed to be memory efficient, and by default uses 271701aecbSPhillip Lougher * 16 KiB. 281701aecbSPhillip Lougher */ 291701aecbSPhillip Lougher 301701aecbSPhillip Lougher #include <linux/fs.h> 311701aecbSPhillip Lougher #include <linux/vfs.h> 321701aecbSPhillip Lougher #include <linux/kernel.h> 331701aecbSPhillip Lougher #include <linux/slab.h> 341701aecbSPhillip Lougher #include <linux/string.h> 351701aecbSPhillip Lougher #include <linux/pagemap.h> 361701aecbSPhillip Lougher #include <linux/mutex.h> 371701aecbSPhillip Lougher 381701aecbSPhillip Lougher #include "squashfs_fs.h" 391701aecbSPhillip Lougher #include "squashfs_fs_sb.h" 401701aecbSPhillip Lougher #include "squashfs_fs_i.h" 411701aecbSPhillip Lougher #include "squashfs.h" 421701aecbSPhillip Lougher 431701aecbSPhillip Lougher /* 441701aecbSPhillip Lougher * Locate cache slot in range [offset, index] for specified inode. If 451701aecbSPhillip Lougher * there's more than one return the slot closest to index. 461701aecbSPhillip Lougher */ 471701aecbSPhillip Lougher static struct meta_index *locate_meta_index(struct inode *inode, int offset, 481701aecbSPhillip Lougher int index) 491701aecbSPhillip Lougher { 501701aecbSPhillip Lougher struct meta_index *meta = NULL; 511701aecbSPhillip Lougher struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 521701aecbSPhillip Lougher int i; 531701aecbSPhillip Lougher 541701aecbSPhillip Lougher mutex_lock(&msblk->meta_index_mutex); 551701aecbSPhillip Lougher 561701aecbSPhillip Lougher TRACE("locate_meta_index: index %d, offset %d\n", index, offset); 571701aecbSPhillip Lougher 581701aecbSPhillip Lougher if (msblk->meta_index == NULL) 591701aecbSPhillip Lougher goto not_allocated; 601701aecbSPhillip Lougher 611701aecbSPhillip Lougher for (i = 0; i < SQUASHFS_META_SLOTS; i++) { 621701aecbSPhillip Lougher if (msblk->meta_index[i].inode_number == inode->i_ino && 631701aecbSPhillip Lougher msblk->meta_index[i].offset >= offset && 641701aecbSPhillip Lougher msblk->meta_index[i].offset <= index && 651701aecbSPhillip Lougher msblk->meta_index[i].locked == 0) { 661701aecbSPhillip Lougher TRACE("locate_meta_index: entry %d, offset %d\n", i, 671701aecbSPhillip Lougher msblk->meta_index[i].offset); 681701aecbSPhillip Lougher meta = &msblk->meta_index[i]; 691701aecbSPhillip Lougher offset = meta->offset; 701701aecbSPhillip Lougher } 711701aecbSPhillip Lougher } 721701aecbSPhillip Lougher 731701aecbSPhillip Lougher if (meta) 741701aecbSPhillip Lougher meta->locked = 1; 751701aecbSPhillip Lougher 761701aecbSPhillip Lougher not_allocated: 771701aecbSPhillip Lougher mutex_unlock(&msblk->meta_index_mutex); 781701aecbSPhillip Lougher 791701aecbSPhillip Lougher return meta; 801701aecbSPhillip Lougher } 811701aecbSPhillip Lougher 821701aecbSPhillip Lougher 831701aecbSPhillip Lougher /* 841701aecbSPhillip Lougher * Find and initialise an empty cache slot for index offset. 851701aecbSPhillip Lougher */ 861701aecbSPhillip Lougher static struct meta_index *empty_meta_index(struct inode *inode, int offset, 871701aecbSPhillip Lougher int skip) 881701aecbSPhillip Lougher { 891701aecbSPhillip Lougher struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 901701aecbSPhillip Lougher struct meta_index *meta = NULL; 911701aecbSPhillip Lougher int i; 921701aecbSPhillip Lougher 931701aecbSPhillip Lougher mutex_lock(&msblk->meta_index_mutex); 941701aecbSPhillip Lougher 951701aecbSPhillip Lougher TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); 961701aecbSPhillip Lougher 971701aecbSPhillip Lougher if (msblk->meta_index == NULL) { 981701aecbSPhillip Lougher /* 991701aecbSPhillip Lougher * First time cache index has been used, allocate and 1001701aecbSPhillip Lougher * initialise. The cache index could be allocated at 1011701aecbSPhillip Lougher * mount time but doing it here means it is allocated only 1021701aecbSPhillip Lougher * if a 'large' file is read. 1031701aecbSPhillip Lougher */ 1041701aecbSPhillip Lougher msblk->meta_index = kcalloc(SQUASHFS_META_SLOTS, 1051701aecbSPhillip Lougher sizeof(*(msblk->meta_index)), GFP_KERNEL); 1061701aecbSPhillip Lougher if (msblk->meta_index == NULL) { 1071701aecbSPhillip Lougher ERROR("Failed to allocate meta_index\n"); 1081701aecbSPhillip Lougher goto failed; 1091701aecbSPhillip Lougher } 1101701aecbSPhillip Lougher for (i = 0; i < SQUASHFS_META_SLOTS; i++) { 1111701aecbSPhillip Lougher msblk->meta_index[i].inode_number = 0; 1121701aecbSPhillip Lougher msblk->meta_index[i].locked = 0; 1131701aecbSPhillip Lougher } 1141701aecbSPhillip Lougher msblk->next_meta_index = 0; 1151701aecbSPhillip Lougher } 1161701aecbSPhillip Lougher 1171701aecbSPhillip Lougher for (i = SQUASHFS_META_SLOTS; i && 1181701aecbSPhillip Lougher msblk->meta_index[msblk->next_meta_index].locked; i--) 1191701aecbSPhillip Lougher msblk->next_meta_index = (msblk->next_meta_index + 1) % 1201701aecbSPhillip Lougher SQUASHFS_META_SLOTS; 1211701aecbSPhillip Lougher 1221701aecbSPhillip Lougher if (i == 0) { 1231701aecbSPhillip Lougher TRACE("empty_meta_index: failed!\n"); 1241701aecbSPhillip Lougher goto failed; 1251701aecbSPhillip Lougher } 1261701aecbSPhillip Lougher 1271701aecbSPhillip Lougher TRACE("empty_meta_index: returned meta entry %d, %p\n", 1281701aecbSPhillip Lougher msblk->next_meta_index, 1291701aecbSPhillip Lougher &msblk->meta_index[msblk->next_meta_index]); 1301701aecbSPhillip Lougher 1311701aecbSPhillip Lougher meta = &msblk->meta_index[msblk->next_meta_index]; 1321701aecbSPhillip Lougher msblk->next_meta_index = (msblk->next_meta_index + 1) % 1331701aecbSPhillip Lougher SQUASHFS_META_SLOTS; 1341701aecbSPhillip Lougher 1351701aecbSPhillip Lougher meta->inode_number = inode->i_ino; 1361701aecbSPhillip Lougher meta->offset = offset; 1371701aecbSPhillip Lougher meta->skip = skip; 1381701aecbSPhillip Lougher meta->entries = 0; 1391701aecbSPhillip Lougher meta->locked = 1; 1401701aecbSPhillip Lougher 1411701aecbSPhillip Lougher failed: 1421701aecbSPhillip Lougher mutex_unlock(&msblk->meta_index_mutex); 1431701aecbSPhillip Lougher return meta; 1441701aecbSPhillip Lougher } 1451701aecbSPhillip Lougher 1461701aecbSPhillip Lougher 1471701aecbSPhillip Lougher static void release_meta_index(struct inode *inode, struct meta_index *meta) 1481701aecbSPhillip Lougher { 1491701aecbSPhillip Lougher struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 1501701aecbSPhillip Lougher mutex_lock(&msblk->meta_index_mutex); 1511701aecbSPhillip Lougher meta->locked = 0; 1521701aecbSPhillip Lougher mutex_unlock(&msblk->meta_index_mutex); 1531701aecbSPhillip Lougher } 1541701aecbSPhillip Lougher 1551701aecbSPhillip Lougher 1561701aecbSPhillip Lougher /* 1571701aecbSPhillip Lougher * Read the next n blocks from the block list, starting from 1581701aecbSPhillip Lougher * metadata block <start_block, offset>. 1591701aecbSPhillip Lougher */ 1601701aecbSPhillip Lougher static long long read_indexes(struct super_block *sb, int n, 1611701aecbSPhillip Lougher u64 *start_block, int *offset) 1621701aecbSPhillip Lougher { 1631701aecbSPhillip Lougher int err, i; 1641701aecbSPhillip Lougher long long block = 0; 16509cbfeafSKirill A. Shutemov __le32 *blist = kmalloc(PAGE_SIZE, GFP_KERNEL); 1661701aecbSPhillip Lougher 1671701aecbSPhillip Lougher if (blist == NULL) { 1681701aecbSPhillip Lougher ERROR("read_indexes: Failed to allocate block_list\n"); 1691701aecbSPhillip Lougher return -ENOMEM; 1701701aecbSPhillip Lougher } 1711701aecbSPhillip Lougher 1721701aecbSPhillip Lougher while (n) { 17309cbfeafSKirill A. Shutemov int blocks = min_t(int, n, PAGE_SIZE >> 2); 1741701aecbSPhillip Lougher 1751701aecbSPhillip Lougher err = squashfs_read_metadata(sb, blist, start_block, 1761701aecbSPhillip Lougher offset, blocks << 2); 1771701aecbSPhillip Lougher if (err < 0) { 1781701aecbSPhillip Lougher ERROR("read_indexes: reading block [%llx:%x]\n", 1791701aecbSPhillip Lougher *start_block, *offset); 1801701aecbSPhillip Lougher goto failure; 1811701aecbSPhillip Lougher } 1821701aecbSPhillip Lougher 1831701aecbSPhillip Lougher for (i = 0; i < blocks; i++) { 18401cfb793SLinus Torvalds int size = squashfs_block_size(blist[i]); 18501cfb793SLinus Torvalds if (size < 0) { 18601cfb793SLinus Torvalds err = size; 18701cfb793SLinus Torvalds goto failure; 18801cfb793SLinus Torvalds } 1891701aecbSPhillip Lougher block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size); 1901701aecbSPhillip Lougher } 1911701aecbSPhillip Lougher n -= blocks; 1921701aecbSPhillip Lougher } 1931701aecbSPhillip Lougher 1941701aecbSPhillip Lougher kfree(blist); 1951701aecbSPhillip Lougher return block; 1961701aecbSPhillip Lougher 1971701aecbSPhillip Lougher failure: 1981701aecbSPhillip Lougher kfree(blist); 1991701aecbSPhillip Lougher return err; 2001701aecbSPhillip Lougher } 2011701aecbSPhillip Lougher 2021701aecbSPhillip Lougher 2031701aecbSPhillip Lougher /* 2041701aecbSPhillip Lougher * Each cache index slot has SQUASHFS_META_ENTRIES, each of which 2051701aecbSPhillip Lougher * can cache one index -> datablock/blocklist-block mapping. We wish 2061701aecbSPhillip Lougher * to distribute these over the length of the file, entry[0] maps index x, 2071701aecbSPhillip Lougher * entry[1] maps index x + skip, entry[2] maps index x + 2 * skip, and so on. 2081701aecbSPhillip Lougher * The larger the file, the greater the skip factor. The skip factor is 2091701aecbSPhillip Lougher * limited to the size of the metadata cache (SQUASHFS_CACHED_BLKS) to ensure 2101701aecbSPhillip Lougher * the number of metadata blocks that need to be read fits into the cache. 2111701aecbSPhillip Lougher * If the skip factor is limited in this way then the file will use multiple 2121701aecbSPhillip Lougher * slots. 2131701aecbSPhillip Lougher */ 214d6e621deSPhillip Lougher static inline int calculate_skip(u64 blocks) 2151701aecbSPhillip Lougher { 216d6e621deSPhillip Lougher u64 skip = blocks / ((SQUASHFS_META_ENTRIES + 1) 2171701aecbSPhillip Lougher * SQUASHFS_META_INDEXES); 218d6e621deSPhillip Lougher return min((u64) SQUASHFS_CACHED_BLKS - 1, skip + 1); 2191701aecbSPhillip Lougher } 2201701aecbSPhillip Lougher 2211701aecbSPhillip Lougher 2221701aecbSPhillip Lougher /* 2231701aecbSPhillip Lougher * Search and grow the index cache for the specified inode, returning the 2241701aecbSPhillip Lougher * on-disk locations of the datablock and block list metadata block 2251701aecbSPhillip Lougher * <index_block, index_offset> for index (scaled to nearest cache index). 2261701aecbSPhillip Lougher */ 2271701aecbSPhillip Lougher static int fill_meta_index(struct inode *inode, int index, 2281701aecbSPhillip Lougher u64 *index_block, int *index_offset, u64 *data_block) 2291701aecbSPhillip Lougher { 2301701aecbSPhillip Lougher struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 2311701aecbSPhillip Lougher int skip = calculate_skip(i_size_read(inode) >> msblk->block_log); 2321701aecbSPhillip Lougher int offset = 0; 2331701aecbSPhillip Lougher struct meta_index *meta; 2341701aecbSPhillip Lougher struct meta_entry *meta_entry; 2351701aecbSPhillip Lougher u64 cur_index_block = squashfs_i(inode)->block_list_start; 2361701aecbSPhillip Lougher int cur_offset = squashfs_i(inode)->offset; 2371701aecbSPhillip Lougher u64 cur_data_block = squashfs_i(inode)->start; 2381701aecbSPhillip Lougher int err, i; 2391701aecbSPhillip Lougher 2401701aecbSPhillip Lougher /* 2411701aecbSPhillip Lougher * Scale index to cache index (cache slot entry) 2421701aecbSPhillip Lougher */ 2431701aecbSPhillip Lougher index /= SQUASHFS_META_INDEXES * skip; 2441701aecbSPhillip Lougher 2451701aecbSPhillip Lougher while (offset < index) { 2461701aecbSPhillip Lougher meta = locate_meta_index(inode, offset + 1, index); 2471701aecbSPhillip Lougher 2481701aecbSPhillip Lougher if (meta == NULL) { 2491701aecbSPhillip Lougher meta = empty_meta_index(inode, offset + 1, skip); 2501701aecbSPhillip Lougher if (meta == NULL) 2511701aecbSPhillip Lougher goto all_done; 2521701aecbSPhillip Lougher } else { 2531701aecbSPhillip Lougher offset = index < meta->offset + meta->entries ? index : 2541701aecbSPhillip Lougher meta->offset + meta->entries - 1; 2551701aecbSPhillip Lougher meta_entry = &meta->meta_entry[offset - meta->offset]; 2561701aecbSPhillip Lougher cur_index_block = meta_entry->index_block + 2571701aecbSPhillip Lougher msblk->inode_table; 2581701aecbSPhillip Lougher cur_offset = meta_entry->offset; 2591701aecbSPhillip Lougher cur_data_block = meta_entry->data_block; 2601701aecbSPhillip Lougher TRACE("get_meta_index: offset %d, meta->offset %d, " 2611701aecbSPhillip Lougher "meta->entries %d\n", offset, meta->offset, 2621701aecbSPhillip Lougher meta->entries); 2631701aecbSPhillip Lougher TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" 2641701aecbSPhillip Lougher " data_block 0x%llx\n", cur_index_block, 2651701aecbSPhillip Lougher cur_offset, cur_data_block); 2661701aecbSPhillip Lougher } 2671701aecbSPhillip Lougher 2681701aecbSPhillip Lougher /* 2691701aecbSPhillip Lougher * If necessary grow cache slot by reading block list. Cache 2701701aecbSPhillip Lougher * slot is extended up to index or to the end of the slot, in 2711701aecbSPhillip Lougher * which case further slots will be used. 2721701aecbSPhillip Lougher */ 2731701aecbSPhillip Lougher for (i = meta->offset + meta->entries; i <= index && 2741701aecbSPhillip Lougher i < meta->offset + SQUASHFS_META_ENTRIES; i++) { 2751701aecbSPhillip Lougher int blocks = skip * SQUASHFS_META_INDEXES; 2761701aecbSPhillip Lougher long long res = read_indexes(inode->i_sb, blocks, 2771701aecbSPhillip Lougher &cur_index_block, &cur_offset); 2781701aecbSPhillip Lougher 2791701aecbSPhillip Lougher if (res < 0) { 2801701aecbSPhillip Lougher if (meta->entries == 0) 2811701aecbSPhillip Lougher /* 2821701aecbSPhillip Lougher * Don't leave an empty slot on read 2831701aecbSPhillip Lougher * error allocated to this inode... 2841701aecbSPhillip Lougher */ 2851701aecbSPhillip Lougher meta->inode_number = 0; 2861701aecbSPhillip Lougher err = res; 2871701aecbSPhillip Lougher goto failed; 2881701aecbSPhillip Lougher } 2891701aecbSPhillip Lougher 2901701aecbSPhillip Lougher cur_data_block += res; 2911701aecbSPhillip Lougher meta_entry = &meta->meta_entry[i - meta->offset]; 2921701aecbSPhillip Lougher meta_entry->index_block = cur_index_block - 2931701aecbSPhillip Lougher msblk->inode_table; 2941701aecbSPhillip Lougher meta_entry->offset = cur_offset; 2951701aecbSPhillip Lougher meta_entry->data_block = cur_data_block; 2961701aecbSPhillip Lougher meta->entries++; 2971701aecbSPhillip Lougher offset++; 2981701aecbSPhillip Lougher } 2991701aecbSPhillip Lougher 3001701aecbSPhillip Lougher TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", 3011701aecbSPhillip Lougher meta->offset, meta->entries); 3021701aecbSPhillip Lougher 3031701aecbSPhillip Lougher release_meta_index(inode, meta); 3041701aecbSPhillip Lougher } 3051701aecbSPhillip Lougher 3061701aecbSPhillip Lougher all_done: 3071701aecbSPhillip Lougher *index_block = cur_index_block; 3081701aecbSPhillip Lougher *index_offset = cur_offset; 3091701aecbSPhillip Lougher *data_block = cur_data_block; 3101701aecbSPhillip Lougher 3111701aecbSPhillip Lougher /* 3121701aecbSPhillip Lougher * Scale cache index (cache slot entry) to index 3131701aecbSPhillip Lougher */ 3141701aecbSPhillip Lougher return offset * SQUASHFS_META_INDEXES * skip; 3151701aecbSPhillip Lougher 3161701aecbSPhillip Lougher failed: 3171701aecbSPhillip Lougher release_meta_index(inode, meta); 3181701aecbSPhillip Lougher return err; 3191701aecbSPhillip Lougher } 3201701aecbSPhillip Lougher 3211701aecbSPhillip Lougher 3221701aecbSPhillip Lougher /* 3231701aecbSPhillip Lougher * Get the on-disk location and compressed size of the datablock 3241701aecbSPhillip Lougher * specified by index. Fill_meta_index() does most of the work. 3251701aecbSPhillip Lougher */ 3261701aecbSPhillip Lougher static int read_blocklist(struct inode *inode, int index, u64 *block) 3271701aecbSPhillip Lougher { 3281701aecbSPhillip Lougher u64 start; 3291701aecbSPhillip Lougher long long blks; 3301701aecbSPhillip Lougher int offset; 3311701aecbSPhillip Lougher __le32 size; 3321701aecbSPhillip Lougher int res = fill_meta_index(inode, index, &start, &offset, block); 3331701aecbSPhillip Lougher 3341701aecbSPhillip Lougher TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset" 3351701aecbSPhillip Lougher " 0x%x, block 0x%llx\n", res, index, start, offset, 3361701aecbSPhillip Lougher *block); 3371701aecbSPhillip Lougher 3381701aecbSPhillip Lougher if (res < 0) 3391701aecbSPhillip Lougher return res; 3401701aecbSPhillip Lougher 3411701aecbSPhillip Lougher /* 3421701aecbSPhillip Lougher * res contains the index of the mapping returned by fill_meta_index(), 3431701aecbSPhillip Lougher * this will likely be less than the desired index (because the 3441701aecbSPhillip Lougher * meta_index cache works at a higher granularity). Read any 3451701aecbSPhillip Lougher * extra block indexes needed. 3461701aecbSPhillip Lougher */ 3471701aecbSPhillip Lougher if (res < index) { 3481701aecbSPhillip Lougher blks = read_indexes(inode->i_sb, index - res, &start, &offset); 3491701aecbSPhillip Lougher if (blks < 0) 3501701aecbSPhillip Lougher return (int) blks; 3511701aecbSPhillip Lougher *block += blks; 3521701aecbSPhillip Lougher } 3531701aecbSPhillip Lougher 3541701aecbSPhillip Lougher /* 3551701aecbSPhillip Lougher * Read length of block specified by index. 3561701aecbSPhillip Lougher */ 3571701aecbSPhillip Lougher res = squashfs_read_metadata(inode->i_sb, &size, &start, &offset, 3581701aecbSPhillip Lougher sizeof(size)); 3591701aecbSPhillip Lougher if (res < 0) 3601701aecbSPhillip Lougher return res; 36101cfb793SLinus Torvalds return squashfs_block_size(size); 3621701aecbSPhillip Lougher } 3631701aecbSPhillip Lougher 364cdbb65c4SLinus Torvalds void squashfs_fill_page(struct page *page, struct squashfs_cache_entry *buffer, int offset, int avail) 365cdbb65c4SLinus Torvalds { 366cdbb65c4SLinus Torvalds int copied; 367cdbb65c4SLinus Torvalds void *pageaddr; 368cdbb65c4SLinus Torvalds 369cdbb65c4SLinus Torvalds pageaddr = kmap_atomic(page); 370cdbb65c4SLinus Torvalds copied = squashfs_copy_data(pageaddr, buffer, offset, avail); 371cdbb65c4SLinus Torvalds memset(pageaddr + copied, 0, PAGE_SIZE - copied); 372cdbb65c4SLinus Torvalds kunmap_atomic(pageaddr); 373cdbb65c4SLinus Torvalds 374cdbb65c4SLinus Torvalds flush_dcache_page(page); 375cdbb65c4SLinus Torvalds if (copied == avail) 376cdbb65c4SLinus Torvalds SetPageUptodate(page); 377cdbb65c4SLinus Torvalds else 378cdbb65c4SLinus Torvalds SetPageError(page); 379cdbb65c4SLinus Torvalds } 380cdbb65c4SLinus Torvalds 3815f55dbc0SPhillip Lougher /* Copy data into page cache */ 3825f55dbc0SPhillip Lougher void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer, 3835f55dbc0SPhillip Lougher int bytes, int offset) 3841701aecbSPhillip Lougher { 3851701aecbSPhillip Lougher struct inode *inode = page->mapping->host; 3861701aecbSPhillip Lougher struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 38709cbfeafSKirill A. Shutemov int i, mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1; 3885f55dbc0SPhillip Lougher int start_index = page->index & ~mask, end_index = start_index | mask; 3891701aecbSPhillip Lougher 3901701aecbSPhillip Lougher /* 3911701aecbSPhillip Lougher * Loop copying datablock into pages. As the datablock likely covers 392ea1754a0SKirill A. Shutemov * many PAGE_SIZE pages (default block size is 128 KiB) explicitly 3931701aecbSPhillip Lougher * grab the pages from the page cache, except for the page that we've 3941701aecbSPhillip Lougher * been called to fill. 3951701aecbSPhillip Lougher */ 3961701aecbSPhillip Lougher for (i = start_index; i <= end_index && bytes > 0; i++, 39709cbfeafSKirill A. Shutemov bytes -= PAGE_SIZE, offset += PAGE_SIZE) { 3981701aecbSPhillip Lougher struct page *push_page; 39909cbfeafSKirill A. Shutemov int avail = buffer ? min_t(int, bytes, PAGE_SIZE) : 0; 4001701aecbSPhillip Lougher 4011701aecbSPhillip Lougher TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); 4021701aecbSPhillip Lougher 4031701aecbSPhillip Lougher push_page = (i == page->index) ? page : 4041701aecbSPhillip Lougher grab_cache_page_nowait(page->mapping, i); 4051701aecbSPhillip Lougher 4061701aecbSPhillip Lougher if (!push_page) 4071701aecbSPhillip Lougher continue; 4081701aecbSPhillip Lougher 4091701aecbSPhillip Lougher if (PageUptodate(push_page)) 4101701aecbSPhillip Lougher goto skip_page; 4111701aecbSPhillip Lougher 412cdbb65c4SLinus Torvalds squashfs_fill_page(push_page, buffer, offset, avail); 4131701aecbSPhillip Lougher skip_page: 4141701aecbSPhillip Lougher unlock_page(push_page); 4151701aecbSPhillip Lougher if (i != page->index) 41609cbfeafSKirill A. Shutemov put_page(push_page); 4171701aecbSPhillip Lougher } 4185f55dbc0SPhillip Lougher } 4191701aecbSPhillip Lougher 4205f55dbc0SPhillip Lougher /* Read datablock stored packed inside a fragment (tail-end packed block) */ 421a3f94cb9SPhillip Lougher static int squashfs_readpage_fragment(struct page *page, int expected) 4225f55dbc0SPhillip Lougher { 4235f55dbc0SPhillip Lougher struct inode *inode = page->mapping->host; 4245f55dbc0SPhillip Lougher struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, 4255f55dbc0SPhillip Lougher squashfs_i(inode)->fragment_block, 4265f55dbc0SPhillip Lougher squashfs_i(inode)->fragment_size); 4275f55dbc0SPhillip Lougher int res = buffer->error; 4285f55dbc0SPhillip Lougher 4295f55dbc0SPhillip Lougher if (res) 4305f55dbc0SPhillip Lougher ERROR("Unable to read page, block %llx, size %x\n", 4315f55dbc0SPhillip Lougher squashfs_i(inode)->fragment_block, 4325f55dbc0SPhillip Lougher squashfs_i(inode)->fragment_size); 4335f55dbc0SPhillip Lougher else 434a3f94cb9SPhillip Lougher squashfs_copy_cache(page, buffer, expected, 4355f55dbc0SPhillip Lougher squashfs_i(inode)->fragment_offset); 4365f55dbc0SPhillip Lougher 4371701aecbSPhillip Lougher squashfs_cache_put(buffer); 4385f55dbc0SPhillip Lougher return res; 4395f55dbc0SPhillip Lougher } 4401701aecbSPhillip Lougher 441a3f94cb9SPhillip Lougher static int squashfs_readpage_sparse(struct page *page, int expected) 4425f55dbc0SPhillip Lougher { 443a3f94cb9SPhillip Lougher squashfs_copy_cache(page, NULL, expected, 0); 4445f55dbc0SPhillip Lougher return 0; 4455f55dbc0SPhillip Lougher } 4465f55dbc0SPhillip Lougher 447*124cfc15SMatthew Wilcox (Oracle) static int squashfs_read_folio(struct file *file, struct folio *folio) 4485f55dbc0SPhillip Lougher { 449*124cfc15SMatthew Wilcox (Oracle) struct page *page = &folio->page; 4505f55dbc0SPhillip Lougher struct inode *inode = page->mapping->host; 4515f55dbc0SPhillip Lougher struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; 45209cbfeafSKirill A. Shutemov int index = page->index >> (msblk->block_log - PAGE_SHIFT); 4535f55dbc0SPhillip Lougher int file_end = i_size_read(inode) >> msblk->block_log; 454a3f94cb9SPhillip Lougher int expected = index == file_end ? 455a3f94cb9SPhillip Lougher (i_size_read(inode) & (msblk->block_size - 1)) : 456a3f94cb9SPhillip Lougher msblk->block_size; 4575f55dbc0SPhillip Lougher int res; 4585f55dbc0SPhillip Lougher void *pageaddr; 4595f55dbc0SPhillip Lougher 4605f55dbc0SPhillip Lougher TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", 4615f55dbc0SPhillip Lougher page->index, squashfs_i(inode)->start); 4625f55dbc0SPhillip Lougher 46309cbfeafSKirill A. Shutemov if (page->index >= ((i_size_read(inode) + PAGE_SIZE - 1) >> 46409cbfeafSKirill A. Shutemov PAGE_SHIFT)) 4655f55dbc0SPhillip Lougher goto out; 4665f55dbc0SPhillip Lougher 4675f55dbc0SPhillip Lougher if (index < file_end || squashfs_i(inode)->fragment_block == 4685f55dbc0SPhillip Lougher SQUASHFS_INVALID_BLK) { 4695f55dbc0SPhillip Lougher u64 block = 0; 4705f55dbc0SPhillip Lougher int bsize = read_blocklist(inode, index, &block); 4715f55dbc0SPhillip Lougher if (bsize < 0) 4725f55dbc0SPhillip Lougher goto error_out; 4735f55dbc0SPhillip Lougher 4745f55dbc0SPhillip Lougher if (bsize == 0) 475a3f94cb9SPhillip Lougher res = squashfs_readpage_sparse(page, expected); 4765f55dbc0SPhillip Lougher else 477a3f94cb9SPhillip Lougher res = squashfs_readpage_block(page, block, bsize, expected); 4785f55dbc0SPhillip Lougher } else 479a3f94cb9SPhillip Lougher res = squashfs_readpage_fragment(page, expected); 4805f55dbc0SPhillip Lougher 4815f55dbc0SPhillip Lougher if (!res) 4821701aecbSPhillip Lougher return 0; 4831701aecbSPhillip Lougher 4841701aecbSPhillip Lougher error_out: 4851701aecbSPhillip Lougher SetPageError(page); 4861701aecbSPhillip Lougher out: 48753b55e55SCong Wang pageaddr = kmap_atomic(page); 48809cbfeafSKirill A. Shutemov memset(pageaddr, 0, PAGE_SIZE); 48953b55e55SCong Wang kunmap_atomic(pageaddr); 4901701aecbSPhillip Lougher flush_dcache_page(page); 4911701aecbSPhillip Lougher if (!PageError(page)) 4921701aecbSPhillip Lougher SetPageUptodate(page); 4931701aecbSPhillip Lougher unlock_page(page); 4941701aecbSPhillip Lougher 4951701aecbSPhillip Lougher return 0; 4961701aecbSPhillip Lougher } 4971701aecbSPhillip Lougher 4981701aecbSPhillip Lougher 4991701aecbSPhillip Lougher const struct address_space_operations squashfs_aops = { 500*124cfc15SMatthew Wilcox (Oracle) .read_folio = squashfs_read_folio 5011701aecbSPhillip Lougher }; 502