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" 3581bb8debSPhillip Lougher 3681bb8debSPhillip Lougher struct squashfs_xz { 3781bb8debSPhillip Lougher struct xz_dec *state; 3881bb8debSPhillip Lougher struct xz_buf buf; 3981bb8debSPhillip Lougher }; 4081bb8debSPhillip Lougher 419508c6b9SPhillip Lougher struct disk_comp_opts { 42ff750311SPhillip Lougher __le32 dictionary_size; 43ff750311SPhillip Lougher __le32 flags; 44ff750311SPhillip Lougher }; 45ff750311SPhillip Lougher 469508c6b9SPhillip Lougher struct comp_opts { 479508c6b9SPhillip Lougher int dict_size; 489508c6b9SPhillip Lougher }; 499508c6b9SPhillip Lougher 509508c6b9SPhillip Lougher static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, 519508c6b9SPhillip Lougher void *buff, int len) 5281bb8debSPhillip Lougher { 539508c6b9SPhillip Lougher struct disk_comp_opts *comp_opts = buff; 549508c6b9SPhillip Lougher struct comp_opts *opts; 559508c6b9SPhillip Lougher int err = 0, n; 569508c6b9SPhillip Lougher 579508c6b9SPhillip Lougher opts = kmalloc(sizeof(*opts), GFP_KERNEL); 589508c6b9SPhillip Lougher if (opts == NULL) { 599508c6b9SPhillip Lougher err = -ENOMEM; 609508c6b9SPhillip Lougher goto out2; 619508c6b9SPhillip Lougher } 6281bb8debSPhillip Lougher 63ff750311SPhillip Lougher if (comp_opts) { 64ff750311SPhillip Lougher /* check compressor options are the expected length */ 65ff750311SPhillip Lougher if (len < sizeof(*comp_opts)) { 66ff750311SPhillip Lougher err = -EIO; 679508c6b9SPhillip Lougher goto out; 68ff750311SPhillip Lougher } 6981bb8debSPhillip Lougher 709508c6b9SPhillip Lougher opts->dict_size = le32_to_cpu(comp_opts->dictionary_size); 71ff750311SPhillip Lougher 72ff750311SPhillip Lougher /* the dictionary size should be 2^n or 2^n+2^(n+1) */ 739508c6b9SPhillip Lougher n = ffs(opts->dict_size) - 1; 749508c6b9SPhillip Lougher if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) + 75ff750311SPhillip Lougher (1 << (n + 1))) { 76ff750311SPhillip Lougher err = -EIO; 779508c6b9SPhillip Lougher goto out; 78ff750311SPhillip Lougher } 799508c6b9SPhillip Lougher } else 809508c6b9SPhillip Lougher /* use defaults */ 819508c6b9SPhillip Lougher opts->dict_size = max_t(int, msblk->block_size, 829508c6b9SPhillip Lougher SQUASHFS_METADATA_SIZE); 839508c6b9SPhillip Lougher 849508c6b9SPhillip Lougher return opts; 859508c6b9SPhillip Lougher 869508c6b9SPhillip Lougher out: 879508c6b9SPhillip Lougher kfree(opts); 889508c6b9SPhillip Lougher out2: 899508c6b9SPhillip Lougher return ERR_PTR(err); 90ff750311SPhillip Lougher } 91ff750311SPhillip Lougher 929508c6b9SPhillip Lougher 939508c6b9SPhillip Lougher static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff) 949508c6b9SPhillip Lougher { 959508c6b9SPhillip Lougher struct comp_opts *comp_opts = buff; 969508c6b9SPhillip Lougher struct squashfs_xz *stream; 979508c6b9SPhillip Lougher int err; 98ff750311SPhillip Lougher 99ff750311SPhillip Lougher stream = kmalloc(sizeof(*stream), GFP_KERNEL); 100ff750311SPhillip Lougher if (stream == NULL) { 101ff750311SPhillip Lougher err = -ENOMEM; 102ff750311SPhillip Lougher goto failed; 103ff750311SPhillip Lougher } 104ff750311SPhillip Lougher 1059508c6b9SPhillip Lougher stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size); 106ff750311SPhillip Lougher if (stream->state == NULL) { 107ff750311SPhillip Lougher kfree(stream); 108ff750311SPhillip Lougher err = -ENOMEM; 109ff750311SPhillip Lougher goto failed; 110ff750311SPhillip Lougher } 11181bb8debSPhillip Lougher 11281bb8debSPhillip Lougher return stream; 11381bb8debSPhillip Lougher 11481bb8debSPhillip Lougher failed: 115ff750311SPhillip Lougher ERROR("Failed to initialise xz decompressor\n"); 116ff750311SPhillip Lougher return ERR_PTR(err); 11781bb8debSPhillip Lougher } 11881bb8debSPhillip Lougher 11981bb8debSPhillip Lougher 12081bb8debSPhillip Lougher static void squashfs_xz_free(void *strm) 12181bb8debSPhillip Lougher { 12281bb8debSPhillip Lougher struct squashfs_xz *stream = strm; 12381bb8debSPhillip Lougher 12481bb8debSPhillip Lougher if (stream) { 12581bb8debSPhillip Lougher xz_dec_end(stream->state); 12681bb8debSPhillip Lougher kfree(stream); 12781bb8debSPhillip Lougher } 12881bb8debSPhillip Lougher } 12981bb8debSPhillip Lougher 13081bb8debSPhillip Lougher 1319508c6b9SPhillip Lougher static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, 1329508c6b9SPhillip Lougher void **buffer, struct buffer_head **bh, int b, int offset, int length, 1339508c6b9SPhillip Lougher int srclength, int pages) 13481bb8debSPhillip Lougher { 13581bb8debSPhillip Lougher enum xz_ret xz_err; 13681bb8debSPhillip Lougher int avail, total = 0, k = 0, page = 0; 1379508c6b9SPhillip Lougher struct squashfs_xz *stream = strm; 13881bb8debSPhillip Lougher 13981bb8debSPhillip Lougher xz_dec_reset(stream->state); 14081bb8debSPhillip Lougher stream->buf.in_pos = 0; 14181bb8debSPhillip Lougher stream->buf.in_size = 0; 14281bb8debSPhillip Lougher stream->buf.out_pos = 0; 14381bb8debSPhillip Lougher stream->buf.out_size = PAGE_CACHE_SIZE; 14481bb8debSPhillip Lougher stream->buf.out = buffer[page++]; 14581bb8debSPhillip Lougher 14681bb8debSPhillip Lougher do { 14781bb8debSPhillip Lougher if (stream->buf.in_pos == stream->buf.in_size && k < b) { 14881bb8debSPhillip Lougher avail = min(length, msblk->devblksize - offset); 14981bb8debSPhillip Lougher length -= avail; 15081bb8debSPhillip Lougher stream->buf.in = bh[k]->b_data + offset; 15181bb8debSPhillip Lougher stream->buf.in_size = avail; 15281bb8debSPhillip Lougher stream->buf.in_pos = 0; 15381bb8debSPhillip Lougher offset = 0; 15481bb8debSPhillip Lougher } 15581bb8debSPhillip Lougher 15681bb8debSPhillip Lougher if (stream->buf.out_pos == stream->buf.out_size 15781bb8debSPhillip Lougher && page < pages) { 15881bb8debSPhillip Lougher stream->buf.out = buffer[page++]; 15981bb8debSPhillip Lougher stream->buf.out_pos = 0; 16081bb8debSPhillip Lougher total += PAGE_CACHE_SIZE; 16181bb8debSPhillip Lougher } 16281bb8debSPhillip Lougher 16381bb8debSPhillip Lougher xz_err = xz_dec_run(stream->state, &stream->buf); 16481bb8debSPhillip Lougher 16581bb8debSPhillip Lougher if (stream->buf.in_pos == stream->buf.in_size && k < b) 16681bb8debSPhillip Lougher put_bh(bh[k++]); 16781bb8debSPhillip Lougher } while (xz_err == XZ_OK); 16881bb8debSPhillip Lougher 1699508c6b9SPhillip Lougher if (xz_err != XZ_STREAM_END || k < b) 1709508c6b9SPhillip Lougher goto out; 17181bb8debSPhillip Lougher 1729508c6b9SPhillip Lougher return total + stream->buf.out_pos; 17381bb8debSPhillip Lougher 1749508c6b9SPhillip Lougher out: 17581bb8debSPhillip Lougher for (; k < b; k++) 17681bb8debSPhillip Lougher put_bh(bh[k]); 17781bb8debSPhillip Lougher 17881bb8debSPhillip Lougher return -EIO; 17981bb8debSPhillip Lougher } 18081bb8debSPhillip Lougher 18181bb8debSPhillip Lougher const struct squashfs_decompressor squashfs_xz_comp_ops = { 18281bb8debSPhillip Lougher .init = squashfs_xz_init, 1839508c6b9SPhillip Lougher .comp_opts = squashfs_xz_comp_opts, 18481bb8debSPhillip Lougher .free = squashfs_xz_free, 18581bb8debSPhillip Lougher .decompress = squashfs_xz_uncompress, 18681bb8debSPhillip Lougher .id = XZ_COMPRESSION, 18781bb8debSPhillip Lougher .name = "xz", 18881bb8debSPhillip Lougher .supported = 1 18981bb8debSPhillip Lougher }; 190