1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Squashfs - a compressed read only filesystem for Linux 4 * 5 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 6 * Phillip Lougher <phillip@squashfs.org.uk> 7 * 8 * block.c 9 */ 10 11 /* 12 * This file implements the low-level routines to read and decompress 13 * datablocks and metadata blocks. 14 */ 15 16 #include <linux/blkdev.h> 17 #include <linux/fs.h> 18 #include <linux/vfs.h> 19 #include <linux/slab.h> 20 #include <linux/string.h> 21 #include <linux/bio.h> 22 23 #include "squashfs_fs.h" 24 #include "squashfs_fs_sb.h" 25 #include "squashfs.h" 26 #include "decompressor.h" 27 #include "page_actor.h" 28 29 /* 30 * Returns the amount of bytes copied to the page actor. 31 */ 32 static int copy_bio_to_actor(struct bio *bio, 33 struct squashfs_page_actor *actor, 34 int offset, int req_length) 35 { 36 void *actor_addr; 37 struct bvec_iter_all iter_all = {}; 38 struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 39 int copied_bytes = 0; 40 int actor_offset = 0; 41 42 squashfs_actor_nobuff(actor); 43 actor_addr = squashfs_first_page(actor); 44 45 if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) 46 return 0; 47 48 while (copied_bytes < req_length) { 49 int bytes_to_copy = min_t(int, bvec->bv_len - offset, 50 PAGE_SIZE - actor_offset); 51 52 bytes_to_copy = min_t(int, bytes_to_copy, 53 req_length - copied_bytes); 54 if (!IS_ERR(actor_addr)) 55 memcpy(actor_addr + actor_offset, bvec_virt(bvec) + 56 offset, bytes_to_copy); 57 58 actor_offset += bytes_to_copy; 59 copied_bytes += bytes_to_copy; 60 offset += bytes_to_copy; 61 62 if (actor_offset >= PAGE_SIZE) { 63 actor_addr = squashfs_next_page(actor); 64 if (!actor_addr) 65 break; 66 actor_offset = 0; 67 } 68 if (offset >= bvec->bv_len) { 69 if (!bio_next_segment(bio, &iter_all)) 70 break; 71 offset = 0; 72 } 73 } 74 squashfs_finish_page(actor); 75 return copied_bytes; 76 } 77 78 static int squashfs_bio_read(struct super_block *sb, u64 index, int length, 79 struct bio **biop, int *block_offset) 80 { 81 struct squashfs_sb_info *msblk = sb->s_fs_info; 82 const u64 read_start = round_down(index, msblk->devblksize); 83 const sector_t block = read_start >> msblk->devblksize_log2; 84 const u64 read_end = round_up(index + length, msblk->devblksize); 85 const sector_t block_end = read_end >> msblk->devblksize_log2; 86 int offset = read_start - round_down(index, PAGE_SIZE); 87 int total_len = (block_end - block) << msblk->devblksize_log2; 88 const int page_count = DIV_ROUND_UP(total_len + offset, PAGE_SIZE); 89 int error, i; 90 struct bio *bio; 91 92 bio = bio_kmalloc(page_count, GFP_NOIO); 93 if (!bio) 94 return -ENOMEM; 95 bio_init(bio, sb->s_bdev, bio->bi_inline_vecs, page_count, REQ_OP_READ); 96 bio->bi_iter.bi_sector = block * (msblk->devblksize >> SECTOR_SHIFT); 97 98 for (i = 0; i < page_count; ++i) { 99 unsigned int len = 100 min_t(unsigned int, PAGE_SIZE - offset, total_len); 101 struct page *page = alloc_page(GFP_NOIO); 102 103 if (!page) { 104 error = -ENOMEM; 105 goto out_free_bio; 106 } 107 if (!bio_add_page(bio, page, len, offset)) { 108 error = -EIO; 109 goto out_free_bio; 110 } 111 offset = 0; 112 total_len -= len; 113 } 114 115 error = submit_bio_wait(bio); 116 if (error) 117 goto out_free_bio; 118 119 *biop = bio; 120 *block_offset = index & ((1 << msblk->devblksize_log2) - 1); 121 return 0; 122 123 out_free_bio: 124 bio_free_pages(bio); 125 bio_uninit(bio); 126 kfree(bio); 127 return error; 128 } 129 130 /* 131 * Read and decompress a metadata block or datablock. Length is non-zero 132 * if a datablock is being read (the size is stored elsewhere in the 133 * filesystem), otherwise the length is obtained from the first two bytes of 134 * the metadata block. A bit in the length field indicates if the block 135 * is stored uncompressed in the filesystem (usually because compression 136 * generated a larger block - this does occasionally happen with compression 137 * algorithms). 138 */ 139 int squashfs_read_data(struct super_block *sb, u64 index, int length, 140 u64 *next_index, struct squashfs_page_actor *output) 141 { 142 struct squashfs_sb_info *msblk = sb->s_fs_info; 143 struct bio *bio = NULL; 144 int compressed; 145 int res; 146 int offset; 147 148 if (length) { 149 /* 150 * Datablock. 151 */ 152 compressed = SQUASHFS_COMPRESSED_BLOCK(length); 153 length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); 154 TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", 155 index, compressed ? "" : "un", length, output->length); 156 } else { 157 /* 158 * Metadata block. 159 */ 160 const u8 *data; 161 struct bvec_iter_all iter_all = {}; 162 struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 163 164 if (index + 2 > msblk->bytes_used) { 165 res = -EIO; 166 goto out; 167 } 168 res = squashfs_bio_read(sb, index, 2, &bio, &offset); 169 if (res) 170 goto out; 171 172 if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) { 173 res = -EIO; 174 goto out_free_bio; 175 } 176 /* Extract the length of the metadata block */ 177 data = bvec_virt(bvec); 178 length = data[offset]; 179 if (offset < bvec->bv_len - 1) { 180 length |= data[offset + 1] << 8; 181 } else { 182 if (WARN_ON_ONCE(!bio_next_segment(bio, &iter_all))) { 183 res = -EIO; 184 goto out_free_bio; 185 } 186 data = bvec_virt(bvec); 187 length |= data[0] << 8; 188 } 189 bio_free_pages(bio); 190 bio_uninit(bio); 191 kfree(bio); 192 193 compressed = SQUASHFS_COMPRESSED(length); 194 length = SQUASHFS_COMPRESSED_SIZE(length); 195 index += 2; 196 197 TRACE("Block @ 0x%llx, %scompressed size %d\n", index - 2, 198 compressed ? "" : "un", length); 199 } 200 if (length < 0 || length > output->length || 201 (index + length) > msblk->bytes_used) { 202 res = -EIO; 203 goto out; 204 } 205 206 if (next_index) 207 *next_index = index + length; 208 209 res = squashfs_bio_read(sb, index, length, &bio, &offset); 210 if (res) 211 goto out; 212 213 if (compressed) { 214 if (!msblk->stream) { 215 res = -EIO; 216 goto out_free_bio; 217 } 218 res = msblk->thread_ops->decompress(msblk, bio, offset, length, output); 219 } else { 220 res = copy_bio_to_actor(bio, output, offset, length); 221 } 222 223 out_free_bio: 224 bio_free_pages(bio); 225 bio_uninit(bio); 226 kfree(bio); 227 out: 228 if (res < 0) { 229 ERROR("Failed to read block 0x%llx: %d\n", index, res); 230 if (msblk->panic_on_errors) 231 panic("squashfs read failed"); 232 } 233 234 return res; 235 } 236