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 * fragment.c 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * This file implements code to handle compressed fragments (tail-end packed 1362306a36Sopenharmony_ci * datablocks). 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Regular files contain a fragment index which is mapped to a fragment 1662306a36Sopenharmony_ci * location on disk and compressed size using a fragment lookup table. 1762306a36Sopenharmony_ci * Like everything in Squashfs this fragment lookup table is itself stored 1862306a36Sopenharmony_ci * compressed into metadata blocks. A second index table is used to locate 1962306a36Sopenharmony_ci * these. This second index table for speed of access (and because it 2062306a36Sopenharmony_ci * is small) is read at mount time and cached in memory. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/fs.h> 2462306a36Sopenharmony_ci#include <linux/vfs.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "squashfs_fs.h" 2862306a36Sopenharmony_ci#include "squashfs_fs_sb.h" 2962306a36Sopenharmony_ci#include "squashfs.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * Look-up fragment using the fragment index table. Return the on disk 3362306a36Sopenharmony_ci * location of the fragment and its compressed size 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ciint squashfs_frag_lookup(struct super_block *sb, unsigned int fragment, 3662306a36Sopenharmony_ci u64 *fragment_block) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct squashfs_sb_info *msblk = sb->s_fs_info; 3962306a36Sopenharmony_ci int block, offset, size; 4062306a36Sopenharmony_ci struct squashfs_fragment_entry fragment_entry; 4162306a36Sopenharmony_ci u64 start_block; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (fragment >= msblk->fragments) 4462306a36Sopenharmony_ci return -EIO; 4562306a36Sopenharmony_ci block = SQUASHFS_FRAGMENT_INDEX(fragment); 4662306a36Sopenharmony_ci offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci start_block = le64_to_cpu(msblk->fragment_index[block]); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci size = squashfs_read_metadata(sb, &fragment_entry, &start_block, 5162306a36Sopenharmony_ci &offset, sizeof(fragment_entry)); 5262306a36Sopenharmony_ci if (size < 0) 5362306a36Sopenharmony_ci return size; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci *fragment_block = le64_to_cpu(fragment_entry.start_block); 5662306a36Sopenharmony_ci return squashfs_block_size(fragment_entry.size); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Read the uncompressed fragment lookup table indexes off disk into memory 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci__le64 *squashfs_read_fragment_index_table(struct super_block *sb, 6462306a36Sopenharmony_ci u64 fragment_table_start, u64 next_table, unsigned int fragments) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments); 6762306a36Sopenharmony_ci __le64 *table; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * Sanity check, length bytes should not extend into the next table - 7162306a36Sopenharmony_ci * this check also traps instances where fragment_table_start is 7262306a36Sopenharmony_ci * incorrectly larger than the next table start 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci if (fragment_table_start + length > next_table) 7562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci table = squashfs_read_table(sb, fragment_table_start, length); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * table[0] points to the first fragment table metadata block, this 8162306a36Sopenharmony_ci * should be less than fragment_table_start 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci if (!IS_ERR(table) && le64_to_cpu(table[0]) >= fragment_table_start) { 8462306a36Sopenharmony_ci kfree(table); 8562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return table; 8962306a36Sopenharmony_ci} 90