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 36 struct squashfs_xz { 37 struct xz_dec *state; 38 struct xz_buf buf; 39 }; 40 41 struct comp_opts { 42 __le32 dictionary_size; 43 __le32 flags; 44 }; 45 46 static 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 goto failed; 59 } 60 61 dict_size = le32_to_cpu(comp_opts->dictionary_size); 62 63 /* the dictionary size should be 2^n or 2^n+2^(n+1) */ 64 n = ffs(dict_size) - 1; 65 if (dict_size != (1 << n) && dict_size != (1 << n) + 66 (1 << (n + 1))) { 67 err = -EIO; 68 goto failed; 69 } 70 } 71 72 dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE); 73 74 stream = kmalloc(sizeof(*stream), GFP_KERNEL); 75 if (stream == NULL) { 76 err = -ENOMEM; 77 goto failed; 78 } 79 80 stream->state = xz_dec_init(XZ_PREALLOC, dict_size); 81 if (stream->state == NULL) { 82 kfree(stream); 83 err = -ENOMEM; 84 goto failed; 85 } 86 87 return stream; 88 89 failed: 90 ERROR("Failed to initialise xz decompressor\n"); 91 return ERR_PTR(err); 92 } 93 94 95 static void squashfs_xz_free(void *strm) 96 { 97 struct squashfs_xz *stream = strm; 98 99 if (stream) { 100 xz_dec_end(stream->state); 101 kfree(stream); 102 } 103 } 104 105 106 static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, 107 struct buffer_head **bh, int b, int offset, int length, int srclength, 108 int pages) 109 { 110 enum xz_ret xz_err; 111 int avail, total = 0, k = 0, page = 0; 112 struct squashfs_xz *stream = msblk->stream; 113 114 mutex_lock(&msblk->read_data_mutex); 115 116 xz_dec_reset(stream->state); 117 stream->buf.in_pos = 0; 118 stream->buf.in_size = 0; 119 stream->buf.out_pos = 0; 120 stream->buf.out_size = PAGE_CACHE_SIZE; 121 stream->buf.out = buffer[page++]; 122 123 do { 124 if (stream->buf.in_pos == stream->buf.in_size && k < b) { 125 avail = min(length, msblk->devblksize - offset); 126 length -= avail; 127 wait_on_buffer(bh[k]); 128 if (!buffer_uptodate(bh[k])) 129 goto release_mutex; 130 131 stream->buf.in = bh[k]->b_data + offset; 132 stream->buf.in_size = avail; 133 stream->buf.in_pos = 0; 134 offset = 0; 135 } 136 137 if (stream->buf.out_pos == stream->buf.out_size 138 && page < pages) { 139 stream->buf.out = buffer[page++]; 140 stream->buf.out_pos = 0; 141 total += PAGE_CACHE_SIZE; 142 } 143 144 xz_err = xz_dec_run(stream->state, &stream->buf); 145 146 if (stream->buf.in_pos == stream->buf.in_size && k < b) 147 put_bh(bh[k++]); 148 } while (xz_err == XZ_OK); 149 150 if (xz_err != XZ_STREAM_END) { 151 ERROR("xz_dec_run error, data probably corrupt\n"); 152 goto release_mutex; 153 } 154 155 if (k < b) { 156 ERROR("xz_uncompress error, input remaining\n"); 157 goto release_mutex; 158 } 159 160 total += stream->buf.out_pos; 161 mutex_unlock(&msblk->read_data_mutex); 162 return total; 163 164 release_mutex: 165 mutex_unlock(&msblk->read_data_mutex); 166 167 for (; k < b; k++) 168 put_bh(bh[k]); 169 170 return -EIO; 171 } 172 173 const struct squashfs_decompressor squashfs_xz_comp_ops = { 174 .init = squashfs_xz_init, 175 .free = squashfs_xz_free, 176 .decompress = squashfs_xz_uncompress, 177 .id = XZ_COMPRESSION, 178 .name = "xz", 179 .supported = 1 180 }; 181