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 * block.c 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * This file implements the low-level routines to read and decompress 1362306a36Sopenharmony_ci * datablocks and metadata blocks. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/blkdev.h> 1762306a36Sopenharmony_ci#include <linux/fs.h> 1862306a36Sopenharmony_ci#include <linux/vfs.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/pagemap.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci#include <linux/bio.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "squashfs_fs.h" 2562306a36Sopenharmony_ci#include "squashfs_fs_sb.h" 2662306a36Sopenharmony_ci#include "squashfs.h" 2762306a36Sopenharmony_ci#include "decompressor.h" 2862306a36Sopenharmony_ci#include "page_actor.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * Returns the amount of bytes copied to the page actor. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic int copy_bio_to_actor(struct bio *bio, 3462306a36Sopenharmony_ci struct squashfs_page_actor *actor, 3562306a36Sopenharmony_ci int offset, int req_length) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci void *actor_addr; 3862306a36Sopenharmony_ci struct bvec_iter_all iter_all = {}; 3962306a36Sopenharmony_ci struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 4062306a36Sopenharmony_ci int copied_bytes = 0; 4162306a36Sopenharmony_ci int actor_offset = 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci squashfs_actor_nobuff(actor); 4462306a36Sopenharmony_ci actor_addr = squashfs_first_page(actor); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci while (copied_bytes < req_length) { 5062306a36Sopenharmony_ci int bytes_to_copy = min_t(int, bvec->bv_len - offset, 5162306a36Sopenharmony_ci PAGE_SIZE - actor_offset); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci bytes_to_copy = min_t(int, bytes_to_copy, 5462306a36Sopenharmony_ci req_length - copied_bytes); 5562306a36Sopenharmony_ci if (!IS_ERR(actor_addr)) 5662306a36Sopenharmony_ci memcpy(actor_addr + actor_offset, bvec_virt(bvec) + 5762306a36Sopenharmony_ci offset, bytes_to_copy); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci actor_offset += bytes_to_copy; 6062306a36Sopenharmony_ci copied_bytes += bytes_to_copy; 6162306a36Sopenharmony_ci offset += bytes_to_copy; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (actor_offset >= PAGE_SIZE) { 6462306a36Sopenharmony_ci actor_addr = squashfs_next_page(actor); 6562306a36Sopenharmony_ci if (!actor_addr) 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci actor_offset = 0; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci if (offset >= bvec->bv_len) { 7062306a36Sopenharmony_ci if (!bio_next_segment(bio, &iter_all)) 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci offset = 0; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci squashfs_finish_page(actor); 7662306a36Sopenharmony_ci return copied_bytes; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int squashfs_bio_read_cached(struct bio *fullbio, 8062306a36Sopenharmony_ci struct address_space *cache_mapping, u64 index, int length, 8162306a36Sopenharmony_ci u64 read_start, u64 read_end, int page_count) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct page *head_to_cache = NULL, *tail_to_cache = NULL; 8462306a36Sopenharmony_ci struct block_device *bdev = fullbio->bi_bdev; 8562306a36Sopenharmony_ci int start_idx = 0, end_idx = 0; 8662306a36Sopenharmony_ci struct bvec_iter_all iter_all; 8762306a36Sopenharmony_ci struct bio *bio = NULL; 8862306a36Sopenharmony_ci struct bio_vec *bv; 8962306a36Sopenharmony_ci int idx = 0; 9062306a36Sopenharmony_ci int err = 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci bio_for_each_segment_all(bv, fullbio, iter_all) { 9362306a36Sopenharmony_ci struct page *page = bv->bv_page; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (page->mapping == cache_mapping) { 9662306a36Sopenharmony_ci idx++; 9762306a36Sopenharmony_ci continue; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* 10162306a36Sopenharmony_ci * We only use this when the device block size is the same as 10262306a36Sopenharmony_ci * the page size, so read_start and read_end cover full pages. 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * Compare these to the original required index and length to 10562306a36Sopenharmony_ci * only cache pages which were requested partially, since these 10662306a36Sopenharmony_ci * are the ones which are likely to be needed when reading 10762306a36Sopenharmony_ci * adjacent blocks. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci if (idx == 0 && index != read_start) 11062306a36Sopenharmony_ci head_to_cache = page; 11162306a36Sopenharmony_ci else if (idx == page_count - 1 && index + length != read_end) 11262306a36Sopenharmony_ci tail_to_cache = page; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (!bio || idx != end_idx) { 11562306a36Sopenharmony_ci struct bio *new = bio_alloc_clone(bdev, fullbio, 11662306a36Sopenharmony_ci GFP_NOIO, &fs_bio_set); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (bio) { 11962306a36Sopenharmony_ci bio_trim(bio, start_idx * PAGE_SECTORS, 12062306a36Sopenharmony_ci (end_idx - start_idx) * PAGE_SECTORS); 12162306a36Sopenharmony_ci bio_chain(bio, new); 12262306a36Sopenharmony_ci submit_bio(bio); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci bio = new; 12662306a36Sopenharmony_ci start_idx = idx; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci idx++; 13062306a36Sopenharmony_ci end_idx = idx; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (bio) { 13462306a36Sopenharmony_ci bio_trim(bio, start_idx * PAGE_SECTORS, 13562306a36Sopenharmony_ci (end_idx - start_idx) * PAGE_SECTORS); 13662306a36Sopenharmony_ci err = submit_bio_wait(bio); 13762306a36Sopenharmony_ci bio_put(bio); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (err) 14162306a36Sopenharmony_ci return err; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (head_to_cache) { 14462306a36Sopenharmony_ci int ret = add_to_page_cache_lru(head_to_cache, cache_mapping, 14562306a36Sopenharmony_ci read_start >> PAGE_SHIFT, 14662306a36Sopenharmony_ci GFP_NOIO); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!ret) { 14962306a36Sopenharmony_ci SetPageUptodate(head_to_cache); 15062306a36Sopenharmony_ci unlock_page(head_to_cache); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (tail_to_cache) { 15662306a36Sopenharmony_ci int ret = add_to_page_cache_lru(tail_to_cache, cache_mapping, 15762306a36Sopenharmony_ci (read_end >> PAGE_SHIFT) - 1, 15862306a36Sopenharmony_ci GFP_NOIO); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!ret) { 16162306a36Sopenharmony_ci SetPageUptodate(tail_to_cache); 16262306a36Sopenharmony_ci unlock_page(tail_to_cache); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct page *squashfs_get_cache_page(struct address_space *mapping, 17062306a36Sopenharmony_ci pgoff_t index) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct page *page; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!mapping) 17562306a36Sopenharmony_ci return NULL; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci page = find_get_page(mapping, index); 17862306a36Sopenharmony_ci if (!page) 17962306a36Sopenharmony_ci return NULL; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!PageUptodate(page)) { 18262306a36Sopenharmony_ci put_page(page); 18362306a36Sopenharmony_ci return NULL; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return page; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int squashfs_bio_read(struct super_block *sb, u64 index, int length, 19062306a36Sopenharmony_ci struct bio **biop, int *block_offset) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct squashfs_sb_info *msblk = sb->s_fs_info; 19362306a36Sopenharmony_ci struct address_space *cache_mapping = msblk->cache_mapping; 19462306a36Sopenharmony_ci const u64 read_start = round_down(index, msblk->devblksize); 19562306a36Sopenharmony_ci const sector_t block = read_start >> msblk->devblksize_log2; 19662306a36Sopenharmony_ci const u64 read_end = round_up(index + length, msblk->devblksize); 19762306a36Sopenharmony_ci const sector_t block_end = read_end >> msblk->devblksize_log2; 19862306a36Sopenharmony_ci int offset = read_start - round_down(index, PAGE_SIZE); 19962306a36Sopenharmony_ci int total_len = (block_end - block) << msblk->devblksize_log2; 20062306a36Sopenharmony_ci const int page_count = DIV_ROUND_UP(total_len + offset, PAGE_SIZE); 20162306a36Sopenharmony_ci int error, i; 20262306a36Sopenharmony_ci struct bio *bio; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci bio = bio_kmalloc(page_count, GFP_NOIO); 20562306a36Sopenharmony_ci if (!bio) 20662306a36Sopenharmony_ci return -ENOMEM; 20762306a36Sopenharmony_ci bio_init(bio, sb->s_bdev, bio->bi_inline_vecs, page_count, REQ_OP_READ); 20862306a36Sopenharmony_ci bio->bi_iter.bi_sector = block * (msblk->devblksize >> SECTOR_SHIFT); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci for (i = 0; i < page_count; ++i) { 21162306a36Sopenharmony_ci unsigned int len = 21262306a36Sopenharmony_ci min_t(unsigned int, PAGE_SIZE - offset, total_len); 21362306a36Sopenharmony_ci pgoff_t index = (read_start >> PAGE_SHIFT) + i; 21462306a36Sopenharmony_ci struct page *page; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci page = squashfs_get_cache_page(cache_mapping, index); 21762306a36Sopenharmony_ci if (!page) 21862306a36Sopenharmony_ci page = alloc_page(GFP_NOIO); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!page) { 22162306a36Sopenharmony_ci error = -ENOMEM; 22262306a36Sopenharmony_ci goto out_free_bio; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * Use the __ version to avoid merging since we need each page 22762306a36Sopenharmony_ci * to be separate when we check for and avoid cached pages. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci __bio_add_page(bio, page, len, offset); 23062306a36Sopenharmony_ci offset = 0; 23162306a36Sopenharmony_ci total_len -= len; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (cache_mapping) 23562306a36Sopenharmony_ci error = squashfs_bio_read_cached(bio, cache_mapping, index, 23662306a36Sopenharmony_ci length, read_start, read_end, 23762306a36Sopenharmony_ci page_count); 23862306a36Sopenharmony_ci else 23962306a36Sopenharmony_ci error = submit_bio_wait(bio); 24062306a36Sopenharmony_ci if (error) 24162306a36Sopenharmony_ci goto out_free_bio; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci *biop = bio; 24462306a36Sopenharmony_ci *block_offset = index & ((1 << msblk->devblksize_log2) - 1); 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ciout_free_bio: 24862306a36Sopenharmony_ci bio_free_pages(bio); 24962306a36Sopenharmony_ci bio_uninit(bio); 25062306a36Sopenharmony_ci kfree(bio); 25162306a36Sopenharmony_ci return error; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* 25562306a36Sopenharmony_ci * Read and decompress a metadata block or datablock. Length is non-zero 25662306a36Sopenharmony_ci * if a datablock is being read (the size is stored elsewhere in the 25762306a36Sopenharmony_ci * filesystem), otherwise the length is obtained from the first two bytes of 25862306a36Sopenharmony_ci * the metadata block. A bit in the length field indicates if the block 25962306a36Sopenharmony_ci * is stored uncompressed in the filesystem (usually because compression 26062306a36Sopenharmony_ci * generated a larger block - this does occasionally happen with compression 26162306a36Sopenharmony_ci * algorithms). 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ciint squashfs_read_data(struct super_block *sb, u64 index, int length, 26462306a36Sopenharmony_ci u64 *next_index, struct squashfs_page_actor *output) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct squashfs_sb_info *msblk = sb->s_fs_info; 26762306a36Sopenharmony_ci struct bio *bio = NULL; 26862306a36Sopenharmony_ci int compressed; 26962306a36Sopenharmony_ci int res; 27062306a36Sopenharmony_ci int offset; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (length) { 27362306a36Sopenharmony_ci /* 27462306a36Sopenharmony_ci * Datablock. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci compressed = SQUASHFS_COMPRESSED_BLOCK(length); 27762306a36Sopenharmony_ci length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); 27862306a36Sopenharmony_ci TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", 27962306a36Sopenharmony_ci index, compressed ? "" : "un", length, output->length); 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * Metadata block. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci const u8 *data; 28562306a36Sopenharmony_ci struct bvec_iter_all iter_all = {}; 28662306a36Sopenharmony_ci struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (index + 2 > msblk->bytes_used) { 28962306a36Sopenharmony_ci res = -EIO; 29062306a36Sopenharmony_ci goto out; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci res = squashfs_bio_read(sb, index, 2, &bio, &offset); 29362306a36Sopenharmony_ci if (res) 29462306a36Sopenharmony_ci goto out; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) { 29762306a36Sopenharmony_ci res = -EIO; 29862306a36Sopenharmony_ci goto out_free_bio; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci /* Extract the length of the metadata block */ 30162306a36Sopenharmony_ci data = bvec_virt(bvec); 30262306a36Sopenharmony_ci length = data[offset]; 30362306a36Sopenharmony_ci if (offset < bvec->bv_len - 1) { 30462306a36Sopenharmony_ci length |= data[offset + 1] << 8; 30562306a36Sopenharmony_ci } else { 30662306a36Sopenharmony_ci if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) { 30762306a36Sopenharmony_ci res = -EIO; 30862306a36Sopenharmony_ci goto out_free_bio; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci data = bvec_virt(bvec); 31162306a36Sopenharmony_ci length |= data[0] << 8; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci bio_free_pages(bio); 31462306a36Sopenharmony_ci bio_uninit(bio); 31562306a36Sopenharmony_ci kfree(bio); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci compressed = SQUASHFS_COMPRESSED(length); 31862306a36Sopenharmony_ci length = SQUASHFS_COMPRESSED_SIZE(length); 31962306a36Sopenharmony_ci index += 2; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci TRACE("Block @ 0x%llx, %scompressed size %d\n", index - 2, 32262306a36Sopenharmony_ci compressed ? "" : "un", length); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci if (length < 0 || length > output->length || 32562306a36Sopenharmony_ci (index + length) > msblk->bytes_used) { 32662306a36Sopenharmony_ci res = -EIO; 32762306a36Sopenharmony_ci goto out; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (next_index) 33162306a36Sopenharmony_ci *next_index = index + length; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci res = squashfs_bio_read(sb, index, length, &bio, &offset); 33462306a36Sopenharmony_ci if (res) 33562306a36Sopenharmony_ci goto out; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (compressed) { 33862306a36Sopenharmony_ci if (!msblk->stream) { 33962306a36Sopenharmony_ci res = -EIO; 34062306a36Sopenharmony_ci goto out_free_bio; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci res = msblk->thread_ops->decompress(msblk, bio, offset, length, output); 34362306a36Sopenharmony_ci } else { 34462306a36Sopenharmony_ci res = copy_bio_to_actor(bio, output, offset, length); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ciout_free_bio: 34862306a36Sopenharmony_ci bio_free_pages(bio); 34962306a36Sopenharmony_ci bio_uninit(bio); 35062306a36Sopenharmony_ci kfree(bio); 35162306a36Sopenharmony_ciout: 35262306a36Sopenharmony_ci if (res < 0) { 35362306a36Sopenharmony_ci ERROR("Failed to read block 0x%llx: %d\n", index, res); 35462306a36Sopenharmony_ci if (msblk->panic_on_errors) 35562306a36Sopenharmony_ci panic("squashfs read failed"); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return res; 35962306a36Sopenharmony_ci} 360