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/buffer_head.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 buffer_head **bh, int b, int offset, int length, 121 struct squashfs_page_actor *output) 122 { 123 enum xz_ret xz_err; 124 int avail, total = 0, k = 0; 125 struct squashfs_xz *stream = strm; 126 127 xz_dec_reset(stream->state); 128 stream->buf.in_pos = 0; 129 stream->buf.in_size = 0; 130 stream->buf.out_pos = 0; 131 stream->buf.out_size = PAGE_SIZE; 132 stream->buf.out = squashfs_first_page(output); 133 134 do { 135 if (stream->buf.in_pos == stream->buf.in_size && k < b) { 136 avail = min(length, msblk->devblksize - offset); 137 length -= avail; 138 stream->buf.in = bh[k]->b_data + offset; 139 stream->buf.in_size = avail; 140 stream->buf.in_pos = 0; 141 offset = 0; 142 } 143 144 if (stream->buf.out_pos == stream->buf.out_size) { 145 stream->buf.out = squashfs_next_page(output); 146 if (stream->buf.out != NULL) { 147 stream->buf.out_pos = 0; 148 total += PAGE_SIZE; 149 } 150 } 151 152 xz_err = xz_dec_run(stream->state, &stream->buf); 153 154 if (stream->buf.in_pos == stream->buf.in_size && k < b) 155 put_bh(bh[k++]); 156 } while (xz_err == XZ_OK); 157 158 squashfs_finish_page(output); 159 160 if (xz_err != XZ_STREAM_END || k < b) 161 goto out; 162 163 return total + stream->buf.out_pos; 164 165 out: 166 for (; k < b; k++) 167 put_bh(bh[k]); 168 169 return -EIO; 170 } 171 172 const struct squashfs_decompressor squashfs_xz_comp_ops = { 173 .init = squashfs_xz_init, 174 .comp_opts = squashfs_xz_comp_opts, 175 .free = squashfs_xz_free, 176 .decompress = squashfs_xz_uncompress, 177 .id = XZ_COMPRESSION, 178 .name = "xz", 179 .supported = 1 180 }; 181