168252eb5SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2e2780ab1SPhillip Lougher /* 3e2780ab1SPhillip Lougher * Squashfs - a compressed read only filesystem for Linux 4e2780ab1SPhillip Lougher * 5e2780ab1SPhillip Lougher * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 6d7f2ff67SPhillip Lougher * Phillip Lougher <phillip@squashfs.org.uk> 7e2780ab1SPhillip Lougher * 8e2780ab1SPhillip Lougher * block.c 9e2780ab1SPhillip Lougher */ 10e2780ab1SPhillip Lougher 11e2780ab1SPhillip Lougher /* 12e2780ab1SPhillip Lougher * This file implements the low-level routines to read and decompress 13e2780ab1SPhillip Lougher * datablocks and metadata blocks. 14e2780ab1SPhillip Lougher */ 15e2780ab1SPhillip Lougher 16e2780ab1SPhillip Lougher #include <linux/fs.h> 17e2780ab1SPhillip Lougher #include <linux/vfs.h> 18e2780ab1SPhillip Lougher #include <linux/slab.h> 19e2780ab1SPhillip Lougher #include <linux/string.h> 20e2780ab1SPhillip Lougher #include <linux/buffer_head.h> 212f8b5444SChristoph Hellwig #include <linux/bio.h> 22e2780ab1SPhillip Lougher 23e2780ab1SPhillip Lougher #include "squashfs_fs.h" 24e2780ab1SPhillip Lougher #include "squashfs_fs_sb.h" 25e2780ab1SPhillip Lougher #include "squashfs.h" 264c0f0bb2SPhillip Lougher #include "decompressor.h" 27846b730eSPhillip Lougher #include "page_actor.h" 28e2780ab1SPhillip Lougher 29e2780ab1SPhillip Lougher /* 30e2780ab1SPhillip Lougher * Read the metadata block length, this is stored in the first two 31e2780ab1SPhillip Lougher * bytes of the metadata block. 32e2780ab1SPhillip Lougher */ 33e2780ab1SPhillip Lougher static struct buffer_head *get_block_length(struct super_block *sb, 34e2780ab1SPhillip Lougher u64 *cur_index, int *offset, int *length) 35e2780ab1SPhillip Lougher { 36e2780ab1SPhillip Lougher struct squashfs_sb_info *msblk = sb->s_fs_info; 37e2780ab1SPhillip Lougher struct buffer_head *bh; 38e2780ab1SPhillip Lougher 39e2780ab1SPhillip Lougher bh = sb_bread(sb, *cur_index); 40e2780ab1SPhillip Lougher if (bh == NULL) 41e2780ab1SPhillip Lougher return NULL; 42e2780ab1SPhillip Lougher 43e2780ab1SPhillip Lougher if (msblk->devblksize - *offset == 1) { 44e2780ab1SPhillip Lougher *length = (unsigned char) bh->b_data[*offset]; 45e2780ab1SPhillip Lougher put_bh(bh); 46e2780ab1SPhillip Lougher bh = sb_bread(sb, ++(*cur_index)); 47e2780ab1SPhillip Lougher if (bh == NULL) 48e2780ab1SPhillip Lougher return NULL; 49e2780ab1SPhillip Lougher *length |= (unsigned char) bh->b_data[0] << 8; 50e2780ab1SPhillip Lougher *offset = 1; 51e2780ab1SPhillip Lougher } else { 52e2780ab1SPhillip Lougher *length = (unsigned char) bh->b_data[*offset] | 53e2780ab1SPhillip Lougher (unsigned char) bh->b_data[*offset + 1] << 8; 54e2780ab1SPhillip Lougher *offset += 2; 553689456bSPhillip Lougher 563689456bSPhillip Lougher if (*offset == msblk->devblksize) { 573689456bSPhillip Lougher put_bh(bh); 583689456bSPhillip Lougher bh = sb_bread(sb, ++(*cur_index)); 593689456bSPhillip Lougher if (bh == NULL) 603689456bSPhillip Lougher return NULL; 613689456bSPhillip Lougher *offset = 0; 623689456bSPhillip Lougher } 63e2780ab1SPhillip Lougher } 64e2780ab1SPhillip Lougher 65e2780ab1SPhillip Lougher return bh; 66e2780ab1SPhillip Lougher } 67e2780ab1SPhillip Lougher 68e2780ab1SPhillip Lougher 69e2780ab1SPhillip Lougher /* 70e2780ab1SPhillip Lougher * Read and decompress a metadata block or datablock. Length is non-zero 71e2780ab1SPhillip Lougher * if a datablock is being read (the size is stored elsewhere in the 72e2780ab1SPhillip Lougher * filesystem), otherwise the length is obtained from the first two bytes of 73e2780ab1SPhillip Lougher * the metadata block. A bit in the length field indicates if the block 74e2780ab1SPhillip Lougher * is stored uncompressed in the filesystem (usually because compression 75ec9267b6SPhillip Lougher * generated a larger block - this does occasionally happen with compression 76ec9267b6SPhillip Lougher * algorithms). 77e2780ab1SPhillip Lougher */ 78846b730eSPhillip Lougher int squashfs_read_data(struct super_block *sb, u64 index, int length, 79846b730eSPhillip Lougher u64 *next_index, struct squashfs_page_actor *output) 80e2780ab1SPhillip Lougher { 81e2780ab1SPhillip Lougher struct squashfs_sb_info *msblk = sb->s_fs_info; 82e2780ab1SPhillip Lougher struct buffer_head **bh; 83e2780ab1SPhillip Lougher int offset = index & ((1 << msblk->devblksize_log2) - 1); 84e2780ab1SPhillip Lougher u64 cur_index = index >> msblk->devblksize_log2; 85846b730eSPhillip Lougher int bytes, compressed, b = 0, k = 0, avail, i; 86e2780ab1SPhillip Lougher 87846b730eSPhillip Lougher bh = kcalloc(((output->length + msblk->devblksize - 1) 88e0d1f700SPhillip Lougher >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); 89e2780ab1SPhillip Lougher if (bh == NULL) 90e2780ab1SPhillip Lougher return -ENOMEM; 91e2780ab1SPhillip Lougher 92e2780ab1SPhillip Lougher if (length) { 93e2780ab1SPhillip Lougher /* 94e2780ab1SPhillip Lougher * Datablock. 95e2780ab1SPhillip Lougher */ 96e2780ab1SPhillip Lougher bytes = -offset; 97e2780ab1SPhillip Lougher compressed = SQUASHFS_COMPRESSED_BLOCK(length); 98e2780ab1SPhillip Lougher length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); 99e2780ab1SPhillip Lougher if (next_index) 100e2780ab1SPhillip Lougher *next_index = index + length; 101e2780ab1SPhillip Lougher 102e2780ab1SPhillip Lougher TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", 103846b730eSPhillip Lougher index, compressed ? "" : "un", length, output->length); 104e2780ab1SPhillip Lougher 105846b730eSPhillip Lougher if (length < 0 || length > output->length || 106e2780ab1SPhillip Lougher (index + length) > msblk->bytes_used) 107e2780ab1SPhillip Lougher goto read_failure; 108e2780ab1SPhillip Lougher 109e2780ab1SPhillip Lougher for (b = 0; bytes < length; b++, cur_index++) { 110e2780ab1SPhillip Lougher bh[b] = sb_getblk(sb, cur_index); 111e2780ab1SPhillip Lougher if (bh[b] == NULL) 112e2780ab1SPhillip Lougher goto block_release; 113e2780ab1SPhillip Lougher bytes += msblk->devblksize; 114e2780ab1SPhillip Lougher } 115dfec8a14SMike Christie ll_rw_block(REQ_OP_READ, 0, b, bh); 116e2780ab1SPhillip Lougher } else { 117e2780ab1SPhillip Lougher /* 118e2780ab1SPhillip Lougher * Metadata block. 119e2780ab1SPhillip Lougher */ 120e2780ab1SPhillip Lougher if ((index + 2) > msblk->bytes_used) 121e2780ab1SPhillip Lougher goto read_failure; 122e2780ab1SPhillip Lougher 123e2780ab1SPhillip Lougher bh[0] = get_block_length(sb, &cur_index, &offset, &length); 124e2780ab1SPhillip Lougher if (bh[0] == NULL) 125e2780ab1SPhillip Lougher goto read_failure; 126e2780ab1SPhillip Lougher b = 1; 127e2780ab1SPhillip Lougher 128e2780ab1SPhillip Lougher bytes = msblk->devblksize - offset; 129e2780ab1SPhillip Lougher compressed = SQUASHFS_COMPRESSED(length); 130e2780ab1SPhillip Lougher length = SQUASHFS_COMPRESSED_SIZE(length); 131e2780ab1SPhillip Lougher if (next_index) 132e2780ab1SPhillip Lougher *next_index = index + length + 2; 133e2780ab1SPhillip Lougher 134e2780ab1SPhillip Lougher TRACE("Block @ 0x%llx, %scompressed size %d\n", index, 135e2780ab1SPhillip Lougher compressed ? "" : "un", length); 136e2780ab1SPhillip Lougher 137846b730eSPhillip Lougher if (length < 0 || length > output->length || 138e2780ab1SPhillip Lougher (index + length) > msblk->bytes_used) 139e2780ab1SPhillip Lougher goto block_release; 140e2780ab1SPhillip Lougher 141e2780ab1SPhillip Lougher for (; bytes < length; b++) { 142e2780ab1SPhillip Lougher bh[b] = sb_getblk(sb, ++cur_index); 143e2780ab1SPhillip Lougher if (bh[b] == NULL) 144e2780ab1SPhillip Lougher goto block_release; 145e2780ab1SPhillip Lougher bytes += msblk->devblksize; 146e2780ab1SPhillip Lougher } 147dfec8a14SMike Christie ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1); 148e2780ab1SPhillip Lougher } 149e2780ab1SPhillip Lougher 1509508c6b9SPhillip Lougher for (i = 0; i < b; i++) { 1519508c6b9SPhillip Lougher wait_on_buffer(bh[i]); 1529508c6b9SPhillip Lougher if (!buffer_uptodate(bh[i])) 1539508c6b9SPhillip Lougher goto block_release; 1549508c6b9SPhillip Lougher } 1559508c6b9SPhillip Lougher 156e2780ab1SPhillip Lougher if (compressed) { 157d5125847SLinus Torvalds if (!msblk->stream) 158d5125847SLinus Torvalds goto read_failure; 159846b730eSPhillip Lougher length = squashfs_decompress(msblk, bh, b, offset, length, 160846b730eSPhillip Lougher output); 161e6a6d379SPhillip Lougher if (length < 0) 162e6a6d379SPhillip Lougher goto read_failure; 163e2780ab1SPhillip Lougher } else { 164e2780ab1SPhillip Lougher /* 165e2780ab1SPhillip Lougher * Block is uncompressed. 166e2780ab1SPhillip Lougher */ 167e0125262SManish Sharma int in, pg_offset = 0; 168846b730eSPhillip Lougher void *data = squashfs_first_page(output); 169e2780ab1SPhillip Lougher 170e2780ab1SPhillip Lougher for (bytes = length; k < b; k++) { 171e2780ab1SPhillip Lougher in = min(bytes, msblk->devblksize - offset); 172e2780ab1SPhillip Lougher bytes -= in; 173e2780ab1SPhillip Lougher while (in) { 17409cbfeafSKirill A. Shutemov if (pg_offset == PAGE_SIZE) { 175846b730eSPhillip Lougher data = squashfs_next_page(output); 176e2780ab1SPhillip Lougher pg_offset = 0; 177e2780ab1SPhillip Lougher } 17809cbfeafSKirill A. Shutemov avail = min_t(int, in, PAGE_SIZE - 179e2780ab1SPhillip Lougher pg_offset); 180846b730eSPhillip Lougher memcpy(data + pg_offset, bh[k]->b_data + offset, 181846b730eSPhillip Lougher avail); 182e2780ab1SPhillip Lougher in -= avail; 183e2780ab1SPhillip Lougher pg_offset += avail; 184e2780ab1SPhillip Lougher offset += avail; 185e2780ab1SPhillip Lougher } 186e2780ab1SPhillip Lougher offset = 0; 187e2780ab1SPhillip Lougher put_bh(bh[k]); 188e2780ab1SPhillip Lougher } 189846b730eSPhillip Lougher squashfs_finish_page(output); 190e2780ab1SPhillip Lougher } 191e2780ab1SPhillip Lougher 192e2780ab1SPhillip Lougher kfree(bh); 193e2780ab1SPhillip Lougher return length; 194e2780ab1SPhillip Lougher 195e2780ab1SPhillip Lougher block_release: 196e2780ab1SPhillip Lougher for (; k < b; k++) 197e2780ab1SPhillip Lougher put_bh(bh[k]); 198e2780ab1SPhillip Lougher 199e2780ab1SPhillip Lougher read_failure: 200118e1ef6SPhillip Lougher ERROR("squashfs_read_data failed to read block 0x%llx\n", 201118e1ef6SPhillip Lougher (unsigned long long) index); 202e2780ab1SPhillip Lougher kfree(bh); 203e2780ab1SPhillip Lougher return -EIO; 204e2780ab1SPhillip Lougher } 205