168252eb5SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 281bb8debSPhillip Lougher /* 381bb8debSPhillip Lougher * Squashfs - a compressed read only filesystem for Linux 481bb8debSPhillip Lougher * 581bb8debSPhillip Lougher * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 6d7f2ff67SPhillip Lougher * Phillip Lougher <phillip@squashfs.org.uk> 781bb8debSPhillip Lougher * 881bb8debSPhillip Lougher * xz_wrapper.c 981bb8debSPhillip Lougher */ 1081bb8debSPhillip Lougher 1181bb8debSPhillip Lougher 1281bb8debSPhillip Lougher #include <linux/mutex.h> 1393e72b3cSPhilippe Liard #include <linux/bio.h> 1481bb8debSPhillip Lougher #include <linux/slab.h> 1581bb8debSPhillip Lougher #include <linux/xz.h> 16ff750311SPhillip Lougher #include <linux/bitops.h> 1781bb8debSPhillip Lougher 1881bb8debSPhillip Lougher #include "squashfs_fs.h" 1981bb8debSPhillip Lougher #include "squashfs_fs_sb.h" 2081bb8debSPhillip Lougher #include "squashfs.h" 2181bb8debSPhillip Lougher #include "decompressor.h" 22846b730eSPhillip Lougher #include "page_actor.h" 2381bb8debSPhillip Lougher 2481bb8debSPhillip Lougher struct squashfs_xz { 2581bb8debSPhillip Lougher struct xz_dec *state; 2681bb8debSPhillip Lougher struct xz_buf buf; 2781bb8debSPhillip Lougher }; 2881bb8debSPhillip Lougher 299508c6b9SPhillip Lougher struct disk_comp_opts { 30ff750311SPhillip Lougher __le32 dictionary_size; 31ff750311SPhillip Lougher __le32 flags; 32ff750311SPhillip Lougher }; 33ff750311SPhillip Lougher 349508c6b9SPhillip Lougher struct comp_opts { 359508c6b9SPhillip Lougher int dict_size; 369508c6b9SPhillip Lougher }; 379508c6b9SPhillip Lougher 389508c6b9SPhillip Lougher static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, 399508c6b9SPhillip Lougher void *buff, int len) 4081bb8debSPhillip Lougher { 419508c6b9SPhillip Lougher struct disk_comp_opts *comp_opts = buff; 429508c6b9SPhillip Lougher struct comp_opts *opts; 439508c6b9SPhillip Lougher int err = 0, n; 449508c6b9SPhillip Lougher 459508c6b9SPhillip Lougher opts = kmalloc(sizeof(*opts), GFP_KERNEL); 469508c6b9SPhillip Lougher if (opts == NULL) { 479508c6b9SPhillip Lougher err = -ENOMEM; 489508c6b9SPhillip Lougher goto out2; 499508c6b9SPhillip Lougher } 5081bb8debSPhillip Lougher 51ff750311SPhillip Lougher if (comp_opts) { 52ff750311SPhillip Lougher /* check compressor options are the expected length */ 53ff750311SPhillip Lougher if (len < sizeof(*comp_opts)) { 54ff750311SPhillip Lougher err = -EIO; 559508c6b9SPhillip Lougher goto out; 56ff750311SPhillip Lougher } 5781bb8debSPhillip Lougher 589508c6b9SPhillip Lougher opts->dict_size = le32_to_cpu(comp_opts->dictionary_size); 59ff750311SPhillip Lougher 60ff750311SPhillip Lougher /* the dictionary size should be 2^n or 2^n+2^(n+1) */ 619508c6b9SPhillip Lougher n = ffs(opts->dict_size) - 1; 629508c6b9SPhillip Lougher if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) + 63ff750311SPhillip Lougher (1 << (n + 1))) { 64ff750311SPhillip Lougher err = -EIO; 659508c6b9SPhillip Lougher goto out; 66ff750311SPhillip Lougher } 679508c6b9SPhillip Lougher } else 689508c6b9SPhillip Lougher /* use defaults */ 699508c6b9SPhillip Lougher opts->dict_size = max_t(int, msblk->block_size, 709508c6b9SPhillip Lougher SQUASHFS_METADATA_SIZE); 719508c6b9SPhillip Lougher 729508c6b9SPhillip Lougher return opts; 739508c6b9SPhillip Lougher 749508c6b9SPhillip Lougher out: 759508c6b9SPhillip Lougher kfree(opts); 769508c6b9SPhillip Lougher out2: 779508c6b9SPhillip Lougher return ERR_PTR(err); 78ff750311SPhillip Lougher } 79ff750311SPhillip Lougher 809508c6b9SPhillip Lougher 819508c6b9SPhillip Lougher static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff) 829508c6b9SPhillip Lougher { 839508c6b9SPhillip Lougher struct comp_opts *comp_opts = buff; 849508c6b9SPhillip Lougher struct squashfs_xz *stream; 859508c6b9SPhillip Lougher int err; 86ff750311SPhillip Lougher 87ff750311SPhillip Lougher stream = kmalloc(sizeof(*stream), GFP_KERNEL); 88ff750311SPhillip Lougher if (stream == NULL) { 89ff750311SPhillip Lougher err = -ENOMEM; 90ff750311SPhillip Lougher goto failed; 91ff750311SPhillip Lougher } 92ff750311SPhillip Lougher 939508c6b9SPhillip Lougher stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size); 94ff750311SPhillip Lougher if (stream->state == NULL) { 95ff750311SPhillip Lougher kfree(stream); 96ff750311SPhillip Lougher err = -ENOMEM; 97ff750311SPhillip Lougher goto failed; 98ff750311SPhillip Lougher } 9981bb8debSPhillip Lougher 10081bb8debSPhillip Lougher return stream; 10181bb8debSPhillip Lougher 10281bb8debSPhillip Lougher failed: 103ff750311SPhillip Lougher ERROR("Failed to initialise xz decompressor\n"); 104ff750311SPhillip Lougher return ERR_PTR(err); 10581bb8debSPhillip Lougher } 10681bb8debSPhillip Lougher 10781bb8debSPhillip Lougher 10881bb8debSPhillip Lougher static void squashfs_xz_free(void *strm) 10981bb8debSPhillip Lougher { 11081bb8debSPhillip Lougher struct squashfs_xz *stream = strm; 11181bb8debSPhillip Lougher 11281bb8debSPhillip Lougher if (stream) { 11381bb8debSPhillip Lougher xz_dec_end(stream->state); 11481bb8debSPhillip Lougher kfree(stream); 11581bb8debSPhillip Lougher } 11681bb8debSPhillip Lougher } 11781bb8debSPhillip Lougher 11881bb8debSPhillip Lougher 1199508c6b9SPhillip Lougher static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, 12093e72b3cSPhilippe Liard struct bio *bio, int offset, int length, 121846b730eSPhillip Lougher struct squashfs_page_actor *output) 12281bb8debSPhillip Lougher { 12393e72b3cSPhilippe Liard struct bvec_iter_all iter_all = {}; 12493e72b3cSPhilippe Liard struct bio_vec *bvec = bvec_init_iter_all(&iter_all); 12593e72b3cSPhilippe Liard int total = 0, error = 0; 1269508c6b9SPhillip Lougher struct squashfs_xz *stream = strm; 12781bb8debSPhillip Lougher 12881bb8debSPhillip Lougher xz_dec_reset(stream->state); 12981bb8debSPhillip Lougher stream->buf.in_pos = 0; 13081bb8debSPhillip Lougher stream->buf.in_size = 0; 13181bb8debSPhillip Lougher stream->buf.out_pos = 0; 13209cbfeafSKirill A. Shutemov stream->buf.out_size = PAGE_SIZE; 133846b730eSPhillip Lougher stream->buf.out = squashfs_first_page(output); 13481bb8debSPhillip Lougher 13593e72b3cSPhilippe Liard for (;;) { 13693e72b3cSPhilippe Liard enum xz_ret xz_err; 13793e72b3cSPhilippe Liard 13893e72b3cSPhilippe Liard if (stream->buf.in_pos == stream->buf.in_size) { 13993e72b3cSPhilippe Liard const void *data; 14093e72b3cSPhilippe Liard int avail; 14193e72b3cSPhilippe Liard 14293e72b3cSPhilippe Liard if (!bio_next_segment(bio, &iter_all)) { 14393e72b3cSPhilippe Liard /* XZ_STREAM_END must be reached. */ 14493e72b3cSPhilippe Liard error = -EIO; 14593e72b3cSPhilippe Liard break; 14693e72b3cSPhilippe Liard } 14793e72b3cSPhilippe Liard 14893e72b3cSPhilippe Liard avail = min(length, ((int)bvec->bv_len) - offset); 149*fbc27241SChristoph Hellwig data = bvec_virt(bvec); 15081bb8debSPhillip Lougher length -= avail; 15193e72b3cSPhilippe Liard stream->buf.in = data + offset; 15281bb8debSPhillip Lougher stream->buf.in_size = avail; 15381bb8debSPhillip Lougher stream->buf.in_pos = 0; 15481bb8debSPhillip Lougher offset = 0; 15581bb8debSPhillip Lougher } 15681bb8debSPhillip Lougher 157846b730eSPhillip Lougher if (stream->buf.out_pos == stream->buf.out_size) { 158846b730eSPhillip Lougher stream->buf.out = squashfs_next_page(output); 159846b730eSPhillip Lougher if (stream->buf.out != NULL) { 16081bb8debSPhillip Lougher stream->buf.out_pos = 0; 16109cbfeafSKirill A. Shutemov total += PAGE_SIZE; 16281bb8debSPhillip Lougher } 163846b730eSPhillip Lougher } 16481bb8debSPhillip Lougher 16581bb8debSPhillip Lougher xz_err = xz_dec_run(stream->state, &stream->buf); 16693e72b3cSPhilippe Liard if (xz_err == XZ_STREAM_END) 16793e72b3cSPhilippe Liard break; 16893e72b3cSPhilippe Liard if (xz_err != XZ_OK) { 16993e72b3cSPhilippe Liard error = -EIO; 17093e72b3cSPhilippe Liard break; 17193e72b3cSPhilippe Liard } 17293e72b3cSPhilippe Liard } 17381bb8debSPhillip Lougher 174846b730eSPhillip Lougher squashfs_finish_page(output); 175846b730eSPhillip Lougher 17693e72b3cSPhilippe Liard return error ? error : total + stream->buf.out_pos; 17781bb8debSPhillip Lougher } 17881bb8debSPhillip Lougher 17981bb8debSPhillip Lougher const struct squashfs_decompressor squashfs_xz_comp_ops = { 18081bb8debSPhillip Lougher .init = squashfs_xz_init, 1819508c6b9SPhillip Lougher .comp_opts = squashfs_xz_comp_opts, 18281bb8debSPhillip Lougher .free = squashfs_xz_free, 18381bb8debSPhillip Lougher .decompress = squashfs_xz_uncompress, 18481bb8debSPhillip Lougher .id = XZ_COMPRESSION, 18581bb8debSPhillip Lougher .name = "xz", 18681bb8debSPhillip Lougher .supported = 1 18781bb8debSPhillip Lougher }; 188