xz_wrapper.c (3847B)
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 24struct squashfs_xz { 25 struct xz_dec *state; 26 struct xz_buf buf; 27}; 28 29struct disk_comp_opts { 30 __le32 dictionary_size; 31 __le32 flags; 32}; 33 34struct comp_opts { 35 int dict_size; 36}; 37 38static 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 74out: 75 kfree(opts); 76out2: 77 return ERR_PTR(err); 78} 79 80 81static 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 102failed: 103 ERROR("Failed to initialise xz decompressor\n"); 104 return ERR_PTR(err); 105} 106 107 108static 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 119static 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 135 for (;;) { 136 enum xz_ret xz_err; 137 138 if (stream->buf.in_pos == stream->buf.in_size) { 139 const void *data; 140 int avail; 141 142 if (!bio_next_segment(bio, &iter_all)) { 143 /* XZ_STREAM_END must be reached. */ 144 error = -EIO; 145 break; 146 } 147 148 avail = min(length, ((int)bvec->bv_len) - offset); 149 data = bvec_virt(bvec); 150 length -= avail; 151 stream->buf.in = data + offset; 152 stream->buf.in_size = avail; 153 stream->buf.in_pos = 0; 154 offset = 0; 155 } 156 157 if (stream->buf.out_pos == stream->buf.out_size) { 158 stream->buf.out = squashfs_next_page(output); 159 if (stream->buf.out != NULL) { 160 stream->buf.out_pos = 0; 161 total += PAGE_SIZE; 162 } 163 } 164 165 xz_err = xz_dec_run(stream->state, &stream->buf); 166 if (xz_err == XZ_STREAM_END) 167 break; 168 if (xz_err != XZ_OK) { 169 error = -EIO; 170 break; 171 } 172 } 173 174 squashfs_finish_page(output); 175 176 return error ? error : total + stream->buf.out_pos; 177} 178 179const struct squashfs_decompressor squashfs_xz_comp_ops = { 180 .init = squashfs_xz_init, 181 .comp_opts = squashfs_xz_comp_opts, 182 .free = squashfs_xz_free, 183 .decompress = squashfs_xz_uncompress, 184 .id = XZ_COMPRESSION, 185 .name = "xz", 186 .supported = 1 187};