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@squashfs.org.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#include "page_actor.h" 36 37struct squashfs_xz { 38 struct xz_dec *state; 39 struct xz_buf buf; 40}; 41 42struct disk_comp_opts { 43 __le32 dictionary_size; 44 __le32 flags; 45}; 46 47struct comp_opts { 48 int dict_size; 49}; 50 51static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, 52 void *buff, int len) 53{ 54 struct disk_comp_opts *comp_opts = buff; 55 struct comp_opts *opts; 56 int err = 0, n; 57 58 opts = kmalloc(sizeof(*opts), GFP_KERNEL); 59 if (opts == NULL) { 60 err = -ENOMEM; 61 goto out2; 62 } 63 64 if (comp_opts) { 65 /* check compressor options are the expected length */ 66 if (len < sizeof(*comp_opts)) { 67 err = -EIO; 68 goto out; 69 } 70 71 opts->dict_size = le32_to_cpu(comp_opts->dictionary_size); 72 73 /* the dictionary size should be 2^n or 2^n+2^(n+1) */ 74 n = ffs(opts->dict_size) - 1; 75 if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) + 76 (1 << (n + 1))) { 77 err = -EIO; 78 goto out; 79 } 80 } else 81 /* use defaults */ 82 opts->dict_size = max_t(int, msblk->block_size, 83 SQUASHFS_METADATA_SIZE); 84 85 return opts; 86 87out: 88 kfree(opts); 89out2: 90 return ERR_PTR(err); 91} 92 93 94static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff) 95{ 96 struct comp_opts *comp_opts = buff; 97 struct squashfs_xz *stream; 98 int err; 99 100 stream = kmalloc(sizeof(*stream), GFP_KERNEL); 101 if (stream == NULL) { 102 err = -ENOMEM; 103 goto failed; 104 } 105 106 stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size); 107 if (stream->state == NULL) { 108 kfree(stream); 109 err = -ENOMEM; 110 goto failed; 111 } 112 113 return stream; 114 115failed: 116 ERROR("Failed to initialise xz decompressor\n"); 117 return ERR_PTR(err); 118} 119 120 121static void squashfs_xz_free(void *strm) 122{ 123 struct squashfs_xz *stream = strm; 124 125 if (stream) { 126 xz_dec_end(stream->state); 127 kfree(stream); 128 } 129} 130 131 132static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, 133 struct buffer_head **bh, int b, int offset, int length, 134 struct squashfs_page_actor *output) 135{ 136 enum xz_ret xz_err; 137 int avail, total = 0, k = 0; 138 struct squashfs_xz *stream = strm; 139 140 xz_dec_reset(stream->state); 141 stream->buf.in_pos = 0; 142 stream->buf.in_size = 0; 143 stream->buf.out_pos = 0; 144 stream->buf.out_size = PAGE_CACHE_SIZE; 145 stream->buf.out = squashfs_first_page(output); 146 147 do { 148 if (stream->buf.in_pos == stream->buf.in_size && k < b) { 149 avail = min(length, msblk->devblksize - offset); 150 length -= avail; 151 stream->buf.in = bh[k]->b_data + offset; 152 stream->buf.in_size = avail; 153 stream->buf.in_pos = 0; 154 offset = 0; 155 } 156 157 if (stream->buf.out_pos == stream->buf.out_size) { 158 stream->buf.out = squashfs_next_page(output); 159 if (stream->buf.out != NULL) { 160 stream->buf.out_pos = 0; 161 total += PAGE_CACHE_SIZE; 162 } 163 } 164 165 xz_err = xz_dec_run(stream->state, &stream->buf); 166 167 if (stream->buf.in_pos == stream->buf.in_size && k < b) 168 put_bh(bh[k++]); 169 } while (xz_err == XZ_OK); 170 171 squashfs_finish_page(output); 172 173 if (xz_err != XZ_STREAM_END || k < b) 174 goto out; 175 176 return total + stream->buf.out_pos; 177 178out: 179 for (; k < b; k++) 180 put_bh(bh[k]); 181 182 return -EIO; 183} 184 185const struct squashfs_decompressor squashfs_xz_comp_ops = { 186 .init = squashfs_xz_init, 187 .comp_opts = squashfs_xz_comp_opts, 188 .free = squashfs_xz_free, 189 .decompress = squashfs_xz_uncompress, 190 .id = XZ_COMPRESSION, 191 .name = "xz", 192 .supported = 1 193}; 194