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