181bb8debSPhillip Lougher /* 281bb8debSPhillip Lougher * Squashfs - a compressed read only filesystem for Linux 381bb8debSPhillip Lougher * 481bb8debSPhillip Lougher * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 5d7f2ff67SPhillip Lougher * Phillip Lougher <phillip@squashfs.org.uk> 681bb8debSPhillip Lougher * 781bb8debSPhillip Lougher * This program is free software; you can redistribute it and/or 881bb8debSPhillip Lougher * modify it under the terms of the GNU General Public License 981bb8debSPhillip Lougher * as published by the Free Software Foundation; either version 2, 1081bb8debSPhillip Lougher * or (at your option) any later version. 1181bb8debSPhillip Lougher * 1281bb8debSPhillip Lougher * This program is distributed in the hope that it will be useful, 1381bb8debSPhillip Lougher * but WITHOUT ANY WARRANTY; without even the implied warranty of 1481bb8debSPhillip Lougher * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1581bb8debSPhillip Lougher * GNU General Public License for more details. 1681bb8debSPhillip Lougher * 1781bb8debSPhillip Lougher * You should have received a copy of the GNU General Public License 1881bb8debSPhillip Lougher * along with this program; if not, write to the Free Software 1981bb8debSPhillip Lougher * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 2081bb8debSPhillip Lougher * 2181bb8debSPhillip Lougher * xz_wrapper.c 2281bb8debSPhillip Lougher */ 2381bb8debSPhillip Lougher 2481bb8debSPhillip Lougher 2581bb8debSPhillip Lougher #include <linux/mutex.h> 2681bb8debSPhillip Lougher #include <linux/buffer_head.h> 2781bb8debSPhillip Lougher #include <linux/slab.h> 2881bb8debSPhillip Lougher #include <linux/xz.h> 29ff750311SPhillip Lougher #include <linux/bitops.h> 3081bb8debSPhillip Lougher 3181bb8debSPhillip Lougher #include "squashfs_fs.h" 3281bb8debSPhillip Lougher #include "squashfs_fs_sb.h" 3381bb8debSPhillip Lougher #include "squashfs.h" 3481bb8debSPhillip Lougher #include "decompressor.h" 35846b730eSPhillip Lougher #include "page_actor.h" 3681bb8debSPhillip Lougher 3781bb8debSPhillip Lougher struct squashfs_xz { 3881bb8debSPhillip Lougher struct xz_dec *state; 3981bb8debSPhillip Lougher struct xz_buf buf; 4081bb8debSPhillip Lougher }; 4181bb8debSPhillip Lougher 429508c6b9SPhillip Lougher struct disk_comp_opts { 43ff750311SPhillip Lougher __le32 dictionary_size; 44ff750311SPhillip Lougher __le32 flags; 45ff750311SPhillip Lougher }; 46ff750311SPhillip Lougher 479508c6b9SPhillip Lougher struct comp_opts { 489508c6b9SPhillip Lougher int dict_size; 499508c6b9SPhillip Lougher }; 509508c6b9SPhillip Lougher 519508c6b9SPhillip Lougher static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, 529508c6b9SPhillip Lougher void *buff, int len) 5381bb8debSPhillip Lougher { 549508c6b9SPhillip Lougher struct disk_comp_opts *comp_opts = buff; 559508c6b9SPhillip Lougher struct comp_opts *opts; 569508c6b9SPhillip Lougher int err = 0, n; 579508c6b9SPhillip Lougher 589508c6b9SPhillip Lougher opts = kmalloc(sizeof(*opts), GFP_KERNEL); 599508c6b9SPhillip Lougher if (opts == NULL) { 609508c6b9SPhillip Lougher err = -ENOMEM; 619508c6b9SPhillip Lougher goto out2; 629508c6b9SPhillip Lougher } 6381bb8debSPhillip Lougher 64ff750311SPhillip Lougher if (comp_opts) { 65ff750311SPhillip Lougher /* check compressor options are the expected length */ 66ff750311SPhillip Lougher if (len < sizeof(*comp_opts)) { 67ff750311SPhillip Lougher err = -EIO; 689508c6b9SPhillip Lougher goto out; 69ff750311SPhillip Lougher } 7081bb8debSPhillip Lougher 719508c6b9SPhillip Lougher opts->dict_size = le32_to_cpu(comp_opts->dictionary_size); 72ff750311SPhillip Lougher 73ff750311SPhillip Lougher /* the dictionary size should be 2^n or 2^n+2^(n+1) */ 749508c6b9SPhillip Lougher n = ffs(opts->dict_size) - 1; 759508c6b9SPhillip Lougher if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) + 76ff750311SPhillip Lougher (1 << (n + 1))) { 77ff750311SPhillip Lougher err = -EIO; 789508c6b9SPhillip Lougher goto out; 79ff750311SPhillip Lougher } 809508c6b9SPhillip Lougher } else 819508c6b9SPhillip Lougher /* use defaults */ 829508c6b9SPhillip Lougher opts->dict_size = max_t(int, msblk->block_size, 839508c6b9SPhillip Lougher SQUASHFS_METADATA_SIZE); 849508c6b9SPhillip Lougher 859508c6b9SPhillip Lougher return opts; 869508c6b9SPhillip Lougher 879508c6b9SPhillip Lougher out: 889508c6b9SPhillip Lougher kfree(opts); 899508c6b9SPhillip Lougher out2: 909508c6b9SPhillip Lougher return ERR_PTR(err); 91ff750311SPhillip Lougher } 92ff750311SPhillip Lougher 939508c6b9SPhillip Lougher 949508c6b9SPhillip Lougher static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff) 959508c6b9SPhillip Lougher { 969508c6b9SPhillip Lougher struct comp_opts *comp_opts = buff; 979508c6b9SPhillip Lougher struct squashfs_xz *stream; 989508c6b9SPhillip Lougher int err; 99ff750311SPhillip Lougher 100ff750311SPhillip Lougher stream = kmalloc(sizeof(*stream), GFP_KERNEL); 101ff750311SPhillip Lougher if (stream == NULL) { 102ff750311SPhillip Lougher err = -ENOMEM; 103ff750311SPhillip Lougher goto failed; 104ff750311SPhillip Lougher } 105ff750311SPhillip Lougher 1069508c6b9SPhillip Lougher stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size); 107ff750311SPhillip Lougher if (stream->state == NULL) { 108ff750311SPhillip Lougher kfree(stream); 109ff750311SPhillip Lougher err = -ENOMEM; 110ff750311SPhillip Lougher goto failed; 111ff750311SPhillip Lougher } 11281bb8debSPhillip Lougher 11381bb8debSPhillip Lougher return stream; 11481bb8debSPhillip Lougher 11581bb8debSPhillip Lougher failed: 116ff750311SPhillip Lougher ERROR("Failed to initialise xz decompressor\n"); 117ff750311SPhillip Lougher return ERR_PTR(err); 11881bb8debSPhillip Lougher } 11981bb8debSPhillip Lougher 12081bb8debSPhillip Lougher 12181bb8debSPhillip Lougher static void squashfs_xz_free(void *strm) 12281bb8debSPhillip Lougher { 12381bb8debSPhillip Lougher struct squashfs_xz *stream = strm; 12481bb8debSPhillip Lougher 12581bb8debSPhillip Lougher if (stream) { 12681bb8debSPhillip Lougher xz_dec_end(stream->state); 12781bb8debSPhillip Lougher kfree(stream); 12881bb8debSPhillip Lougher } 12981bb8debSPhillip Lougher } 13081bb8debSPhillip Lougher 13181bb8debSPhillip Lougher 1329508c6b9SPhillip Lougher static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, 133846b730eSPhillip Lougher struct buffer_head **bh, int b, int offset, int length, 134846b730eSPhillip Lougher struct squashfs_page_actor *output) 13581bb8debSPhillip Lougher { 13681bb8debSPhillip Lougher enum xz_ret xz_err; 137846b730eSPhillip Lougher int avail, total = 0, k = 0; 1389508c6b9SPhillip Lougher struct squashfs_xz *stream = strm; 13981bb8debSPhillip Lougher 14081bb8debSPhillip Lougher xz_dec_reset(stream->state); 14181bb8debSPhillip Lougher stream->buf.in_pos = 0; 14281bb8debSPhillip Lougher stream->buf.in_size = 0; 14381bb8debSPhillip Lougher stream->buf.out_pos = 0; 14481bb8debSPhillip Lougher stream->buf.out_size = PAGE_CACHE_SIZE; 145846b730eSPhillip Lougher stream->buf.out = squashfs_first_page(output); 14681bb8debSPhillip Lougher 14781bb8debSPhillip Lougher do { 14881bb8debSPhillip Lougher if (stream->buf.in_pos == stream->buf.in_size && k < b) { 14981bb8debSPhillip Lougher avail = min(length, msblk->devblksize - offset); 15081bb8debSPhillip Lougher length -= avail; 15181bb8debSPhillip Lougher stream->buf.in = bh[k]->b_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; 16181bb8debSPhillip Lougher total += PAGE_CACHE_SIZE; 16281bb8debSPhillip Lougher } 163846b730eSPhillip Lougher } 16481bb8debSPhillip Lougher 16581bb8debSPhillip Lougher xz_err = xz_dec_run(stream->state, &stream->buf); 16681bb8debSPhillip Lougher 16781bb8debSPhillip Lougher if (stream->buf.in_pos == stream->buf.in_size && k < b) 16881bb8debSPhillip Lougher put_bh(bh[k++]); 16981bb8debSPhillip Lougher } while (xz_err == XZ_OK); 17081bb8debSPhillip Lougher 171846b730eSPhillip Lougher squashfs_finish_page(output); 172846b730eSPhillip Lougher 1739508c6b9SPhillip Lougher if (xz_err != XZ_STREAM_END || k < b) 1749508c6b9SPhillip Lougher goto out; 17581bb8debSPhillip Lougher 1769508c6b9SPhillip Lougher return total + stream->buf.out_pos; 17781bb8debSPhillip Lougher 1789508c6b9SPhillip Lougher out: 17981bb8debSPhillip Lougher for (; k < b; k++) 18081bb8debSPhillip Lougher put_bh(bh[k]); 18181bb8debSPhillip Lougher 18281bb8debSPhillip Lougher return -EIO; 18381bb8debSPhillip Lougher } 18481bb8debSPhillip Lougher 18581bb8debSPhillip Lougher const struct squashfs_decompressor squashfs_xz_comp_ops = { 18681bb8debSPhillip Lougher .init = squashfs_xz_init, 1879508c6b9SPhillip Lougher .comp_opts = squashfs_xz_comp_opts, 18881bb8debSPhillip Lougher .free = squashfs_xz_free, 18981bb8debSPhillip Lougher .decompress = squashfs_xz_uncompress, 19081bb8debSPhillip Lougher .id = XZ_COMPRESSION, 19181bb8debSPhillip Lougher .name = "xz", 19281bb8debSPhillip Lougher .supported = 1 19381bb8debSPhillip Lougher }; 194