1/* 2 * Squashfs - a compressed read only filesystem for Linux 3 * 4 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 5 * Phillip Lougher <phillip@lougher.demon.co.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2, 10 * or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 * xz_wrapper.c 22 */ 23 24 25#include <linux/mutex.h> 26#include <linux/buffer_head.h> 27#include <linux/slab.h> 28#include <linux/xz.h> 29#include <linux/bitops.h> 30 31#include "squashfs_fs.h" 32#include "squashfs_fs_sb.h" 33#include "squashfs.h" 34#include "decompressor.h" 35 36struct squashfs_xz { 37 struct xz_dec *state; 38 struct xz_buf buf; 39}; 40 41struct comp_opts { 42 __le32 dictionary_size; 43 __le32 flags; 44}; 45 46static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff, 47 int len) 48{ 49 struct comp_opts *comp_opts = buff; 50 struct squashfs_xz *stream; 51 int dict_size = msblk->block_size; 52 int err, n; 53 54 if (comp_opts) { 55 /* check compressor options are the expected length */ 56 if (len < sizeof(*comp_opts)) { 57 err = -EIO; 58 ERROR("squashfs_xz_init:1:: len=%d < %d \n", len, sizeof(*comp_opts)); 59 goto failed; 60 } 61 62 dict_size = le32_to_cpu(comp_opts->dictionary_size); 63 64 /* the dictionary size should be 2^n or 2^n+2^(n+1) */ 65 n = ffs(dict_size) - 1; 66 if (dict_size != (1 << n) && dict_size != (1 << n) + 67 (1 << (n + 1))) { 68 ERROR("squashfs_xz_init:2:: n =%d /dict_size %d/comp_opts->dictionary_size %d \n", 69 n, dict_size,comp_opts->dictionary_size); 70 err = -EIO; 71 goto failed; 72 } 73 } 74 75 dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE); 76 77 stream = kmalloc(sizeof(*stream), GFP_KERNEL); 78 if (stream == NULL) { 79 ERROR("squashfs_xz_init:3"); 80 err = -ENOMEM; 81 goto failed; 82 } 83 84 stream->state = xz_dec_init(XZ_PREALLOC, dict_size); 85 if (stream->state == NULL) { 86 kfree(stream); 87 ERROR("squashfs_xz_init:4"); 88 err = -ENOMEM; 89 goto failed; 90 } 91 92 return stream; 93 94failed: 95 ERROR("Failed to initialise xz decompressor\n"); 96 return ERR_PTR(err); 97} 98 99 100static void squashfs_xz_free(void *strm) 101{ 102 struct squashfs_xz *stream = strm; 103 104 if (stream) { 105 xz_dec_end(stream->state); 106 kfree(stream); 107 } 108} 109 110 111static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, 112 struct buffer_head **bh, int b, int offset, int length, int srclength, 113 int pages) 114{ 115 enum xz_ret xz_err; 116 int avail, total = 0, k = 0, page = 0; 117 struct squashfs_xz *stream = msblk->stream; 118 119 mutex_lock(&msblk->read_data_mutex); 120 121 xz_dec_reset(stream->state); 122 stream->buf.in_pos = 0; 123 stream->buf.in_size = 0; 124 stream->buf.out_pos = 0; 125 stream->buf.out_size = PAGE_CACHE_SIZE; 126 stream->buf.out = buffer[page++]; 127 128 do { 129 if (stream->buf.in_pos == stream->buf.in_size && k < b) { 130 avail = min(length, msblk->devblksize - offset); 131 length -= avail; 132 wait_on_buffer(bh[k]); 133 if (!buffer_uptodate(bh[k])) 134 goto release_mutex; 135 136 stream->buf.in = bh[k]->b_data + offset; 137 stream->buf.in_size = avail; 138 stream->buf.in_pos = 0; 139 offset = 0; 140 } 141 142 if (stream->buf.out_pos == stream->buf.out_size 143 && page < pages) { 144 stream->buf.out = buffer[page++]; 145 stream->buf.out_pos = 0; 146 total += PAGE_CACHE_SIZE; 147 } 148 149 xz_err = xz_dec_run(stream->state, &stream->buf); 150 151 if (stream->buf.in_pos == stream->buf.in_size && k < b) 152 put_bh(bh[k++]); 153 } while (xz_err == XZ_OK); 154 155 if (xz_err != XZ_STREAM_END) { 156 ERROR("xz_dec_run error, data probably corrupt\n"); 157 goto release_mutex; 158 } 159 160 if (k < b) { 161 ERROR("xz_uncompress error, input remaining\n"); 162 goto release_mutex; 163 } 164 165 total += stream->buf.out_pos; 166 mutex_unlock(&msblk->read_data_mutex); 167 return total; 168 169release_mutex: 170 mutex_unlock(&msblk->read_data_mutex); 171 172 for (; k < b; k++) 173 put_bh(bh[k]); 174 175 return -EIO; 176} 177 178const struct squashfs_decompressor squashfs_xz_comp_ops = { 179 .init = squashfs_xz_init, 180 .free = squashfs_xz_free, 181 .decompress = squashfs_xz_uncompress, 182 .id = XZ_COMPRESSION, 183 .name = "xz", 184 .supported = 1 185}; 186