197 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Squashfs - a compressed read only filesystem for Linux4 *5 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 20106 * Phillip Lougher <phillip@squashfs.org.uk>7 *8 * xz_wrapper.c9 */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 } else68 /* 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 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 181finish:182 squashfs_finish_page(output);183 184 return error ? error : total + stream->buf.out_pos;185}186 187const 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 = 1196};197