1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Squashfs - a compressed read only filesystem for Linux 4 * 5 * Copyright (c) 2010 LG Electronics 6 * Chan Jeong <chan.jeong@lge.com> 7 * 8 * lzo_wrapper.c 9 */ 10 11 #include <linux/mutex.h> 12 #include <linux/bio.h> 13 #include <linux/slab.h> 14 #include <linux/vmalloc.h> 15 #include <linux/lzo.h> 16 17 #include "squashfs_fs.h" 18 #include "squashfs_fs_sb.h" 19 #include "squashfs.h" 20 #include "decompressor.h" 21 #include "page_actor.h" 22 23 struct squashfs_lzo { 24 void *input; 25 void *output; 26 }; 27 28 static void *lzo_init(struct squashfs_sb_info *msblk, void *buff) 29 { 30 int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); 31 32 struct squashfs_lzo *stream = kzalloc(sizeof(*stream), GFP_KERNEL); 33 if (stream == NULL) 34 goto failed; 35 stream->input = vmalloc(block_size); 36 if (stream->input == NULL) 37 goto failed; 38 stream->output = vmalloc(block_size); 39 if (stream->output == NULL) 40 goto failed2; 41 42 return stream; 43 44 failed2: 45 vfree(stream->input); 46 failed: 47 ERROR("Failed to allocate lzo workspace\n"); 48 kfree(stream); 49 return ERR_PTR(-ENOMEM); 50 } 51 52 53 static void lzo_free(void *strm) 54 { 55 struct squashfs_lzo *stream = strm; 56 57 if (stream) { 58 vfree(stream->input); 59 vfree(stream->output); 60 } 61 kfree(stream); 62 } 63 64 65 static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, 66 struct bio *bio, int offset, int length, 67 struct squashfs_page_actor *output) 68 { 69 struct bvec_iter_all iter_all = {}; 70 struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 71 struct squashfs_lzo *stream = strm; 72 void *buff = stream->input, *data; 73 int bytes = length, res; 74 size_t out_len = output->length; 75 76 while (bio_next_segment(bio, &iter_all)) { 77 int avail = min(bytes, ((int)bvec->bv_len) - offset); 78 79 data = bvec_virt(bvec); 80 memcpy(buff, data + offset, avail); 81 buff += avail; 82 bytes -= avail; 83 offset = 0; 84 } 85 86 res = lzo1x_decompress_safe(stream->input, (size_t)length, 87 stream->output, &out_len); 88 if (res != LZO_E_OK) 89 goto failed; 90 91 res = bytes = (int)out_len; 92 data = squashfs_first_page(output); 93 buff = stream->output; 94 while (data) { 95 if (bytes <= PAGE_SIZE) { 96 if (!IS_ERR(data)) 97 memcpy(data, buff, bytes); 98 break; 99 } else { 100 if (!IS_ERR(data)) 101 memcpy(data, buff, PAGE_SIZE); 102 buff += PAGE_SIZE; 103 bytes -= PAGE_SIZE; 104 data = squashfs_next_page(output); 105 } 106 } 107 squashfs_finish_page(output); 108 109 return res; 110 111 failed: 112 return -EIO; 113 } 114 115 const struct squashfs_decompressor squashfs_lzo_comp_ops = { 116 .init = lzo_init, 117 .free = lzo_free, 118 .decompress = lzo_uncompress, 119 .id = LZO_COMPRESSION, 120 .name = "lzo", 121 .alloc_buffer = 0, 122 .supported = 1 123 }; 124