1 // SPDX-License-Identifier: GPL-2.0-or-later 1 2 /* 3 * Squashfs - a compressed read only filesyste 4 * 5 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 6 * Phillip Lougher <phillip@squashfs.org.uk> 7 * 8 * block.c 9 */ 10 11 /* 12 * This file implements the low-level routines 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/pagemap.h> 21 #include <linux/string.h> 22 #include <linux/bio.h> 23 24 #include "squashfs_fs.h" 25 #include "squashfs_fs_sb.h" 26 #include "squashfs.h" 27 #include "decompressor.h" 28 #include "page_actor.h" 29 30 /* 31 * Returns the amount of bytes copied to the p 32 */ 33 static int copy_bio_to_actor(struct bio *bio, 34 struct squashfs_p 35 int offset, int r 36 { 37 void *actor_addr; 38 struct bvec_iter_all iter_all = {}; 39 struct bio_vec *bvec = bvec_init_iter_ 40 int copied_bytes = 0; 41 int actor_offset = 0; 42 43 squashfs_actor_nobuff(actor); 44 actor_addr = squashfs_first_page(actor 45 46 if (WARN_ON_ONCE(!bio_next_segment(bio 47 return 0; 48 49 while (copied_bytes < req_length) { 50 int bytes_to_copy = min_t(int, 51 PAGE 52 53 bytes_to_copy = min_t(int, byt 54 req_leng 55 if (!IS_ERR(actor_addr)) 56 memcpy(actor_addr + ac 57 offset 58 59 actor_offset += bytes_to_copy; 60 copied_bytes += bytes_to_copy; 61 offset += bytes_to_copy; 62 63 if (actor_offset >= PAGE_SIZE) 64 actor_addr = squashfs_ 65 if (!actor_addr) 66 break; 67 actor_offset = 0; 68 } 69 if (offset >= bvec->bv_len) { 70 if (!bio_next_segment( 71 break; 72 offset = 0; 73 } 74 } 75 squashfs_finish_page(actor); 76 return copied_bytes; 77 } 78 79 static int squashfs_bio_read_cached(struct bio 80 struct address_space *cache_ma 81 u64 read_start, u64 read_end, 82 { 83 struct page *head_to_cache = NULL, *ta 84 struct block_device *bdev = fullbio->b 85 int start_idx = 0, end_idx = 0; 86 struct bvec_iter_all iter_all; 87 struct bio *bio = NULL; 88 struct bio_vec *bv; 89 int idx = 0; 90 int err = 0; 91 92 bio_for_each_segment_all(bv, fullbio, 93 struct page *page = bv->bv_pag 94 95 if (page->mapping == cache_map 96 idx++; 97 continue; 98 } 99 100 /* 101 * We only use this when the d 102 * the page size, so read_star 103 * 104 * Compare these to the origin 105 * only cache pages which were 106 * are the ones which are like 107 * adjacent blocks. 108 */ 109 if (idx == 0 && index != read_ 110 head_to_cache = page; 111 else if (idx == page_count - 1 112 tail_to_cache = page; 113 114 if (!bio || idx != end_idx) { 115 struct bio *new = bio_ 116 117 118 if (bio) { 119 bio_trim(bio, 120 (end_ 121 bio_chain(bio, 122 submit_bio(bio 123 } 124 125 bio = new; 126 start_idx = idx; 127 } 128 129 idx++; 130 end_idx = idx; 131 } 132 133 if (bio) { 134 bio_trim(bio, start_idx * PAGE 135 (end_idx - start_idx) 136 err = submit_bio_wait(bio); 137 bio_put(bio); 138 } 139 140 if (err) 141 return err; 142 143 if (head_to_cache) { 144 int ret = add_to_page_cache_lr 145 146 147 148 if (!ret) { 149 SetPageUptodate(head_t 150 unlock_page(head_to_ca 151 } 152 153 } 154 155 if (tail_to_cache) { 156 int ret = add_to_page_cache_lr 157 158 159 160 if (!ret) { 161 SetPageUptodate(tail_t 162 unlock_page(tail_to_ca 163 } 164 } 165 166 return 0; 167 } 168 169 static struct page *squashfs_get_cache_page(st 170 pg 171 { 172 struct page *page; 173 174 if (!mapping) 175 return NULL; 176 177 page = find_get_page(mapping, index); 178 if (!page) 179 return NULL; 180 181 if (!PageUptodate(page)) { 182 put_page(page); 183 return NULL; 184 } 185 186 return page; 187 } 188 189 static int squashfs_bio_read(struct super_bloc 190 struct bio **biop 191 { 192 struct squashfs_sb_info *msblk = sb->s 193 struct address_space *cache_mapping = 194 const u64 read_start = round_down(inde 195 const sector_t block = read_start >> m 196 const u64 read_end = round_up(index + 197 const sector_t block_end = read_end >> 198 int offset = read_start - round_down(i 199 int total_len = (block_end - block) << 200 const int page_count = DIV_ROUND_UP(to 201 int error, i; 202 struct bio *bio; 203 204 bio = bio_kmalloc(page_count, GFP_NOIO 205 if (!bio) 206 return -ENOMEM; 207 bio_init(bio, sb->s_bdev, bio->bi_inli 208 bio->bi_iter.bi_sector = block * (msbl 209 210 for (i = 0; i < page_count; ++i) { 211 unsigned int len = 212 min_t(unsigned int, PA 213 pgoff_t index = (read_start >> 214 struct page *page; 215 216 page = squashfs_get_cache_page 217 if (!page) 218 page = alloc_page(GFP_ 219 220 if (!page) { 221 error = -ENOMEM; 222 goto out_free_bio; 223 } 224 225 /* 226 * Use the __ version to avoid 227 * to be separate when we chec 228 */ 229 __bio_add_page(bio, page, len, 230 offset = 0; 231 total_len -= len; 232 } 233 234 if (cache_mapping) 235 error = squashfs_bio_read_cach 236 237 238 else 239 error = submit_bio_wait(bio); 240 if (error) 241 goto out_free_bio; 242 243 *biop = bio; 244 *block_offset = index & ((1 << msblk-> 245 return 0; 246 247 out_free_bio: 248 bio_free_pages(bio); 249 bio_uninit(bio); 250 kfree(bio); 251 return error; 252 } 253 254 /* 255 * Read and decompress a metadata block or dat 256 * if a datablock is being read (the size is s 257 * filesystem), otherwise the length is obtain 258 * the metadata block. A bit in the length fi 259 * is stored uncompressed in the filesystem (u 260 * generated a larger block - this does occasi 261 * algorithms). 262 */ 263 int squashfs_read_data(struct super_block *sb, 264 u64 *next_index, struct 265 { 266 struct squashfs_sb_info *msblk = sb->s 267 struct bio *bio = NULL; 268 int compressed; 269 int res; 270 int offset; 271 272 if (length) { 273 /* 274 * Datablock. 275 */ 276 compressed = SQUASHFS_COMPRESS 277 length = SQUASHFS_COMPRESSED_S 278 TRACE("Block @ 0x%llx, %scompr 279 index, compressed ? "" 280 } else { 281 /* 282 * Metadata block. 283 */ 284 const u8 *data; 285 struct bvec_iter_all iter_all 286 struct bio_vec *bvec = bvec_in 287 288 if (index + 2 > msblk->bytes_u 289 res = -EIO; 290 goto out; 291 } 292 res = squashfs_bio_read(sb, in 293 if (res) 294 goto out; 295 296 if (WARN_ON_ONCE(!bio_next_seg 297 res = -EIO; 298 goto out_free_bio; 299 } 300 /* Extract the length of the m 301 data = bvec_virt(bvec); 302 length = data[offset]; 303 if (offset < bvec->bv_len - 1) 304 length |= data[offset 305 } else { 306 if (WARN_ON_ONCE(!bio_ 307 res = -EIO; 308 goto out_free_ 309 } 310 data = bvec_virt(bvec) 311 length |= data[0] << 8 312 } 313 bio_free_pages(bio); 314 bio_uninit(bio); 315 kfree(bio); 316 317 compressed = SQUASHFS_COMPRESS 318 length = SQUASHFS_COMPRESSED_S 319 index += 2; 320 321 TRACE("Block @ 0x%llx, %scompr 322 compressed ? "" : "un", 323 } 324 if (length <= 0 || length > output->le 325 (index + length) > msb 326 res = -EIO; 327 goto out; 328 } 329 330 if (next_index) 331 *next_index = index + length; 332 333 res = squashfs_bio_read(sb, index, len 334 if (res) 335 goto out; 336 337 if (compressed) { 338 if (!msblk->stream) { 339 res = -EIO; 340 goto out_free_bio; 341 } 342 res = msblk->thread_ops->decom 343 } else { 344 res = copy_bio_to_actor(bio, o 345 } 346 347 out_free_bio: 348 bio_free_pages(bio); 349 bio_uninit(bio); 350 kfree(bio); 351 out: 352 if (res < 0) { 353 ERROR("Failed to read block 0x 354 if (msblk->panic_on_errors) 355 panic("squashfs read f 356 } 357 358 return res; 359 } 360
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.