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/buffer_head.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 buffer_head **bh, int b, int offset, int length, 93 struct squashfs_page_actor *output) 94 { 95 struct squashfs_lz4 *stream = strm; 96 void *buff = stream->input, *data; 97 int avail, i, bytes = length, res; 98 99 for (i = 0; i < b; i++) { 100 avail = min(bytes, msblk->devblksize - offset); 101 memcpy(buff, bh[i]->b_data + offset, avail); 102 buff += avail; 103 bytes -= avail; 104 offset = 0; 105 put_bh(bh[i]); 106 } 107 108 res = LZ4_decompress_safe(stream->input, stream->output, 109 length, output->length); 110 111 if (res < 0) 112 return -EIO; 113 114 bytes = res; 115 data = squashfs_first_page(output); 116 buff = stream->output; 117 while (data) { 118 if (bytes <= PAGE_SIZE) { 119 memcpy(data, buff, bytes); 120 break; 121 } 122 memcpy(data, buff, PAGE_SIZE); 123 buff += PAGE_SIZE; 124 bytes -= PAGE_SIZE; 125 data = squashfs_next_page(output); 126 } 127 squashfs_finish_page(output); 128 129 return res; 130 } 131 132 const struct squashfs_decompressor squashfs_lz4_comp_ops = { 133 .init = lz4_init, 134 .comp_opts = lz4_comp_opts, 135 .free = lz4_free, 136 .decompress = lz4_uncompress, 137 .id = LZ4_COMPRESSION, 138 .name = "lz4", 139 .supported = 1 140 }; 141