1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2013, 2014 4 * Phillip Lougher <phillip@squashfs.org.uk> 5 */ 6 7 #include <linux/bio.h> 8 #include <linux/mutex.h> 9 #include <linux/slab.h> 10 #include <linux/vmalloc.h> 11 #include <linux/lz4.h> 12 13 #include "squashfs_fs.h" 14 #include "squashfs_fs_sb.h" 15 #include "squashfs.h" 16 #include "decompressor.h" 17 #include "page_actor.h" 18 19 #define LZ4_LEGACY 1 20 21 struct lz4_comp_opts { 22 __le32 version; 23 __le32 flags; 24 }; 25 26 struct squashfs_lz4 { 27 void *input; 28 void *output; 29 }; 30 31 32 static void *lz4_comp_opts(struct squashfs_sb_info *msblk, 33 void *buff, int len) 34 { 35 struct lz4_comp_opts *comp_opts = buff; 36 37 /* LZ4 compressed filesystems always have compression options */ 38 if (comp_opts == NULL || len < sizeof(*comp_opts)) 39 return ERR_PTR(-EIO); 40 41 if (le32_to_cpu(comp_opts->version) != LZ4_LEGACY) { 42 /* LZ4 format currently used by the kernel is the 'legacy' 43 * format */ 44 ERROR("Unknown LZ4 version\n"); 45 return ERR_PTR(-EINVAL); 46 } 47 48 return NULL; 49 } 50 51 52 static void *lz4_init(struct squashfs_sb_info *msblk, void *buff) 53 { 54 int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); 55 struct squashfs_lz4 *stream; 56 57 stream = kzalloc(sizeof(*stream), GFP_KERNEL); 58 if (stream == NULL) 59 goto failed; 60 stream->input = vmalloc(block_size); 61 if (stream->input == NULL) 62 goto failed2; 63 stream->output = vmalloc(block_size); 64 if (stream->output == NULL) 65 goto failed3; 66 67 return stream; 68 69 failed3: 70 vfree(stream->input); 71 failed2: 72 kfree(stream); 73 failed: 74 ERROR("Failed to initialise LZ4 decompressor\n"); 75 return ERR_PTR(-ENOMEM); 76 } 77 78 79 static void lz4_free(void *strm) 80 { 81 struct squashfs_lz4 *stream = strm; 82 83 if (stream) { 84 vfree(stream->input); 85 vfree(stream->output); 86 } 87 kfree(stream); 88 } 89 90 91 static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm, 92 struct bio *bio, int offset, int length, 93 struct squashfs_page_actor *output) 94 { 95 struct bvec_iter_all iter_all = {}; 96 struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 97 struct squashfs_lz4 *stream = strm; 98 void *buff = stream->input, *data; 99 int bytes = length, res; 100 101 while (bio_next_segment(bio, &iter_all)) { 102 int avail = min(bytes, ((int)bvec->bv_len) - offset); 103 104 data = page_address(bvec->bv_page) + bvec->bv_offset; 105 memcpy(buff, data + offset, avail); 106 buff += avail; 107 bytes -= avail; 108 offset = 0; 109 } 110 111 res = LZ4_decompress_safe(stream->input, stream->output, 112 length, output->length); 113 114 if (res < 0) 115 return -EIO; 116 117 bytes = res; 118 data = squashfs_first_page(output); 119 buff = stream->output; 120 while (data) { 121 if (bytes <= PAGE_SIZE) { 122 memcpy(data, buff, bytes); 123 break; 124 } 125 memcpy(data, buff, PAGE_SIZE); 126 buff += PAGE_SIZE; 127 bytes -= PAGE_SIZE; 128 data = squashfs_next_page(output); 129 } 130 squashfs_finish_page(output); 131 132 return res; 133 } 134 135 const struct squashfs_decompressor squashfs_lz4_comp_ops = { 136 .init = lz4_init, 137 .comp_opts = lz4_comp_opts, 138 .free = lz4_free, 139 .decompress = lz4_uncompress, 140 .id = LZ4_COMPRESSION, 141 .name = "lz4", 142 .supported = 1 143 }; 144