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, 2009, 2010 6 * Phillip Lougher <phillip@squashfs.org.uk> 7 * 8 * xz_wrapper.c 9 */ 10 11 12 #include <linux/mutex.h> 13 #include <linux/bio.h> 14 #include <linux/slab.h> 15 #include <linux/xz.h> 16 #include <linux/bitops.h> 17 18 #include "squashfs_fs.h" 19 #include "squashfs_fs_sb.h" 20 #include "squashfs.h" 21 #include "decompressor.h" 22 #include "page_actor.h" 23 24 struct squashfs_xz { 25 struct xz_dec *state; 26 struct xz_buf buf; 27 }; 28 29 struct disk_comp_opts { 30 __le32 dictionary_size; 31 __le32 flags; 32 }; 33 34 struct comp_opts { 35 int dict_size; 36 }; 37 38 static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, 39 void *buff, int len) 40 { 41 struct disk_comp_opts *comp_opts = buff; 42 struct comp_opts *opts; 43 int err = 0, n; 44 45 opts = kmalloc(sizeof(*opts), GFP_KERNEL); 46 if (opts == NULL) { 47 err = -ENOMEM; 48 goto out2; 49 } 50 51 if (comp_opts) { 52 /* check compressor options are the expected length */ 53 if (len < sizeof(*comp_opts)) { 54 err = -EIO; 55 goto out; 56 } 57 58 opts->dict_size = le32_to_cpu(comp_opts->dictionary_size); 59 60 /* the dictionary size should be 2^n or 2^n+2^(n+1) */ 61 n = ffs(opts->dict_size) - 1; 62 if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) + 63 (1 << (n + 1))) { 64 err = -EIO; 65 goto out; 66 } 67 } else 68 /* use defaults */ 69 opts->dict_size = max_t(int, msblk->block_size, 70 SQUASHFS_METADATA_SIZE); 71 72 return opts; 73 74 out: 75 kfree(opts); 76 out2: 77 return ERR_PTR(err); 78 } 79 80 81 static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff) 82 { 83 struct comp_opts *comp_opts = buff; 84 struct squashfs_xz *stream; 85 int err; 86 87 stream = kmalloc(sizeof(*stream), GFP_KERNEL); 88 if (stream == NULL) { 89 err = -ENOMEM; 90 goto failed; 91 } 92 93 stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size); 94 if (stream->state == NULL) { 95 kfree(stream); 96 err = -ENOMEM; 97 goto failed; 98 } 99 100 return stream; 101 102 failed: 103 ERROR("Failed to initialise xz decompressor\n"); 104 return ERR_PTR(err); 105 } 106 107 108 static void squashfs_xz_free(void *strm) 109 { 110 struct squashfs_xz *stream = strm; 111 112 if (stream) { 113 xz_dec_end(stream->state); 114 kfree(stream); 115 } 116 } 117 118 119 static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, 120 struct bio *bio, int offset, int length, 121 struct squashfs_page_actor *output) 122 { 123 struct bvec_iter_all iter_all = {}; 124 struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 125 int total = 0, error = 0; 126 struct squashfs_xz *stream = strm; 127 128 xz_dec_reset(stream->state); 129 stream->buf.in_pos = 0; 130 stream->buf.in_size = 0; 131 stream->buf.out_pos = 0; 132 stream->buf.out_size = PAGE_SIZE; 133 stream->buf.out = squashfs_first_page(output); 134 if (IS_ERR(stream->buf.out)) { 135 error = PTR_ERR(stream->buf.out); 136 goto finish; 137 } 138 139 for (;;) { 140 enum xz_ret xz_err; 141 142 if (stream->buf.in_pos == stream->buf.in_size) { 143 const void *data; 144 int avail; 145 146 if (!bio_next_segment(bio, &iter_all)) { 147 /* XZ_STREAM_END must be reached. */ 148 error = -EIO; 149 break; 150 } 151 152 avail = min(length, ((int)bvec->bv_len) - offset); 153 data = bvec_virt(bvec); 154 length -= avail; 155 stream->buf.in = data + offset; 156 stream->buf.in_size = avail; 157 stream->buf.in_pos = 0; 158 offset = 0; 159 } 160 161 if (stream->buf.out_pos == stream->buf.out_size) { 162 stream->buf.out = squashfs_next_page(output); 163 if (IS_ERR(stream->buf.out)) { 164 error = PTR_ERR(stream->buf.out); 165 break; 166 } else if (stream->buf.out != NULL) { 167 stream->buf.out_pos = 0; 168 total += PAGE_SIZE; 169 } 170 } 171 172 xz_err = xz_dec_run(stream->state, &stream->buf); 173 if (xz_err == XZ_STREAM_END) 174 break; 175 if (xz_err != XZ_OK) { 176 error = -EIO; 177 break; 178 } 179 } 180 181 finish: 182 squashfs_finish_page(output); 183 184 return error ? error : total + stream->buf.out_pos; 185 } 186 187 const struct squashfs_decompressor squashfs_xz_comp_ops = { 188 .init = squashfs_xz_init, 189 .comp_opts = squashfs_xz_comp_opts, 190 .free = squashfs_xz_free, 191 .decompress = squashfs_xz_uncompress, 192 .id = XZ_COMPRESSION, 193 .name = "xz", 194 .alloc_buffer = 1, 195 .supported = 1 196 }; 197