diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index 25a00d1..7ec5d7e 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig @@ -26,6 +26,12 @@ config SQUASHFS If unsure, say N. +config SQUASHFS_LZMA + bool "Include support for LZMA compressed file systems" + depends on SQUASHFS + select DECOMPRESS_LZMA + select DECOMPRESS_LZMA_NEEDED + config SQUASHFS_EMBEDDED bool "Additional option for memory-constrained systems" diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 70e3244..45aaefd 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_SQUASHFS) += squashfs.o squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o -squashfs-y += namei.o super.o symlink.o +squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o +squashfs-$(CONFIG_SQUASHFS_LZMA) += lzma_wrapper.o diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c index 2a79603..6f9914d 100644 --- a/fs/squashfs/block.c +++ b/fs/squashfs/block.c @@ -29,16 +29,14 @@ #include #include #include -#include #include #include -#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" #include "squashfs_fs_i.h" #include "squashfs.h" - +#include "decompressor.h" /* * Read the metadata block length, this is stored in the first two * bytes of the metadata block. @@ -153,72 +151,10 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, } if (compressed) { - int zlib_err = 0, zlib_init = 0; - - /* - * Uncompress block. - */ - - mutex_lock(&msblk->read_data_mutex); - - msblk->stream.avail_out = 0; - msblk->stream.avail_in = 0; - - bytes = length; - do { - if (msblk->stream.avail_in == 0 && k < b) { - avail = min(bytes, msblk->devblksize - offset); - bytes -= avail; - wait_on_buffer(bh[k]); - if (!buffer_uptodate(bh[k])) - goto release_mutex; - - if (avail == 0) { - offset = 0; - put_bh(bh[k++]); - continue; - } - - msblk->stream.next_in = bh[k]->b_data + offset; - msblk->stream.avail_in = avail; - offset = 0; - } - - if (msblk->stream.avail_out == 0 && page < pages) { - msblk->stream.next_out = buffer[page++]; - msblk->stream.avail_out = PAGE_CACHE_SIZE; - } - - if (!zlib_init) { - zlib_err = zlib_inflateInit(&msblk->stream); - if (zlib_err != Z_OK) { - ERROR("zlib_inflateInit returned" - " unexpected result 0x%x," - " srclength %d\n", zlib_err, - srclength); - goto release_mutex; - } - zlib_init = 1; - } - - zlib_err = zlib_inflate(&msblk->stream, Z_SYNC_FLUSH); - - if (msblk->stream.avail_in == 0 && k < b) - put_bh(bh[k++]); - } while (zlib_err == Z_OK); - - if (zlib_err != Z_STREAM_END) { - ERROR("zlib_inflate error, data probably corrupt\n"); - goto release_mutex; - } - - zlib_err = zlib_inflateEnd(&msblk->stream); - if (zlib_err != Z_OK) { - ERROR("zlib_inflate error, data probably corrupt\n"); - goto release_mutex; - } - length = msblk->stream.total_out; - mutex_unlock(&msblk->read_data_mutex); + length = squashfs_decompress(msblk, buffer, bh, b, offset, + length, srclength, pages); + if (length < 0) + goto read_failure; } else { /* * Block is uncompressed. @@ -255,9 +191,6 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, kfree(bh); return length; -release_mutex: - mutex_unlock(&msblk->read_data_mutex); - block_release: for (; k < b; k++) put_bh(bh[k]); diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index 40c98fa..57314be 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include "squashfs_fs.h" diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c new file mode 100644 index 0000000..0b6ad9b --- /dev/null +++ b/fs/squashfs/decompressor.c @@ -0,0 +1,72 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * decompressor.c + */ + +#include +#include +#include + +#include "squashfs_fs.h" +#include "squashfs_fs_sb.h" +#include "squashfs_fs_i.h" +#include "decompressor.h" +#include "squashfs.h" + +/* + * This file (and decompressor.h) implements a decompressor framework for + * Squashfs, allowing multiple decompressors to be easily supported + */ + +static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = { + NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0 +}; + +static const struct squashfs_decompressor squashfs_lzo_unsupported_comp_ops = { + NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0 +}; + +static const struct squashfs_decompressor squashfs_unknown_comp_ops = { + NULL, NULL, NULL, 0, "unknown", 0 +}; + +static const struct squashfs_decompressor *decompressor[] = { + &squashfs_zlib_comp_ops, +#ifdef CONFIG_SQUASHFS_LZMA + &squashfs_lzma_comp_ops, +#else + &squashfs_lzma_unsupported_comp_ops, +#endif + &squashfs_lzo_unsupported_comp_ops, + &squashfs_unknown_comp_ops +}; + + +const struct squashfs_decompressor *squashfs_lookup_decompressor(int id) +{ + int i; + + for (i = 0; decompressor[i]->id; i++) + if (id == decompressor[i]->id) + break; + + return decompressor[i]; +} diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h new file mode 100644 index 0000000..7425f80 --- /dev/null +++ b/fs/squashfs/decompressor.h @@ -0,0 +1,55 @@ +#ifndef DECOMPRESSOR_H +#define DECOMPRESSOR_H +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * decompressor.h + */ + +struct squashfs_decompressor { + void *(*init)(struct squashfs_sb_info *); + void (*free)(void *); + int (*decompress)(struct squashfs_sb_info *, void **, + struct buffer_head **, int, int, int, int, int); + int id; + char *name; + int supported; +}; + +static inline void *squashfs_decompressor_init(struct squashfs_sb_info *msblk) +{ + return msblk->decompressor->init(msblk); +} + +static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk, + void *s) +{ + if (msblk->decompressor) + msblk->decompressor->free(s); +} + +static inline int squashfs_decompress(struct squashfs_sb_info *msblk, + void **buffer, struct buffer_head **bh, int b, int offset, int length, + int srclength, int pages) +{ + return msblk->decompressor->decompress(msblk, buffer, bh, b, offset, + length, srclength, pages); +} +#endif diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c index 566b0ea..12b933a 100644 --- a/fs/squashfs/dir.c +++ b/fs/squashfs/dir.c @@ -30,7 +30,6 @@ #include #include #include -#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" diff --git a/fs/squashfs/export.c b/fs/squashfs/export.c index 2b1b8fe..7f93d5a 100644 --- a/fs/squashfs/export.c +++ b/fs/squashfs/export.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include "squashfs_fs.h" diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index 717767d..a25c506 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c @@ -47,7 +47,6 @@ #include #include #include -#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c index b5a2c15..7c90bbd 100644 --- a/fs/squashfs/fragment.c +++ b/fs/squashfs/fragment.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" diff --git a/fs/squashfs/id.c b/fs/squashfs/id.c index 3795b83..b7f64bc 100644 --- a/fs/squashfs/id.c +++ b/fs/squashfs/id.c @@ -34,7 +34,6 @@ #include #include #include -#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c index 9101dbd..49daaf6 100644 --- a/fs/squashfs/inode.c +++ b/fs/squashfs/inode.c @@ -40,7 +40,6 @@ #include #include -#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" diff --git a/fs/squashfs/lzma_wrapper.c b/fs/squashfs/lzma_wrapper.c new file mode 100644 index 0000000..9fa617d --- /dev/null +++ b/fs/squashfs/lzma_wrapper.c @@ -0,0 +1,151 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * lzma_wrapper.c + */ + +#include +#include +#include +#include +#include + +#include "squashfs_fs.h" +#include "squashfs_fs_sb.h" +#include "squashfs_fs_i.h" +#include "squashfs.h" +#include "decompressor.h" + +struct squashfs_lzma { + void *input; + void *output; +}; + +/* decompress_unlzma.c is currently non re-entrant... */ +DEFINE_MUTEX(lzma_mutex); + +/* decompress_unlzma.c doesn't provide any context in its callbacks... */ +static int lzma_error; + +static void error(char *m) +{ + ERROR("unlzma error: %s\n", m); + lzma_error = 1; +} + + +static void *lzma_init(struct squashfs_sb_info *msblk) +{ + struct squashfs_lzma *stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (stream == NULL) + goto failed; + stream->input = vmalloc(msblk->block_size); + if (stream->input == NULL) + goto failed; + stream->output = vmalloc(msblk->block_size); + if (stream->output == NULL) + goto failed2; + + return stream; + +failed2: + vfree(stream->input); +failed: + ERROR("failed to allocate lzma workspace\n"); + kfree(stream); + return NULL; +} + + +static void lzma_free(void *strm) +{ + struct squashfs_lzma *stream = strm; + + if (stream) { + vfree(stream->input); + vfree(stream->output); + } + kfree(stream); +} + + +static int lzma_uncompress(struct squashfs_sb_info *msblk, void **buffer, + struct buffer_head **bh, int b, int offset, int length, int srclength, + int pages) +{ + struct squashfs_lzma *stream = msblk->stream; + void *buff = stream->input; + int avail, i, bytes = length, res; + + mutex_lock(&lzma_mutex); + + for (i = 0; i < b; i++) { + wait_on_buffer(bh[i]); + if (!buffer_uptodate(bh[i])) + goto block_release; + + avail = min(bytes, msblk->devblksize - offset); + memcpy(buff, bh[i]->b_data + offset, avail); + buff += avail; + bytes -= avail; + offset = 0; + put_bh(bh[i]); + } + + lzma_error = 0; + res = unlzma(stream->input, length, NULL, NULL, stream->output, NULL, + error); + if (res || lzma_error) + goto failed; + + /* uncompressed size is stored in the LZMA header (5 byte offset) */ + res = bytes = get_unaligned_le32(stream->input + 5); + for (i = 0, buff = stream->output; bytes && i < pages; i++) { + avail = min_t(int, bytes, PAGE_CACHE_SIZE); + memcpy(buffer[i], buff, avail); + buff += avail; + bytes -= avail; + } + if (bytes) + goto failed; + + mutex_unlock(&lzma_mutex); + return res; + +block_release: + for (; i < b; i++) + put_bh(bh[i]); + +failed: + mutex_unlock(&lzma_mutex); + + ERROR("lzma decompression failed, data probably corrupt\n"); + return -EIO; +} + +const struct squashfs_decompressor squashfs_lzma_comp_ops = { + .init = lzma_init, + .free = lzma_free, + .decompress = lzma_uncompress, + .id = LZMA_COMPRESSION, + .name = "lzma", + .supported = 1 +}; + diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c index 9e39865..5266bd8 100644 --- a/fs/squashfs/namei.c +++ b/fs/squashfs/namei.c @@ -57,7 +57,6 @@ #include #include #include -#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 0e9feb6..d094886 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -51,6 +51,9 @@ extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *, u64, int); extern int squashfs_read_table(struct super_block *, void *, u64, int); +/* decompressor.c */ +extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int); + /* export.c */ extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, unsigned int); @@ -71,7 +74,7 @@ extern struct inode *squashfs_iget(struct super_block *, long long, extern int squashfs_read_inode(struct inode *, long long); /* - * Inodes and files operations + * Inodes, files and decompressor operations */ /* dir.c */ @@ -88,3 +91,9 @@ extern const struct inode_operations squashfs_dir_inode_ops; /* symlink.c */ extern const struct address_space_operations squashfs_symlink_aops; + +/* zlib_wrapper.c */ +extern const struct squashfs_decompressor squashfs_zlib_comp_ops; + +/* lzma wrapper.c */ +extern const struct squashfs_decompressor squashfs_lzma_comp_ops; diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h index 283daaf..36e1604 100644 --- a/fs/squashfs/squashfs_fs.h +++ b/fs/squashfs/squashfs_fs.h @@ -211,7 +211,9 @@ struct meta_index { /* * definitions for structures on disk */ -#define ZLIB_COMPRESSION 1 +#define ZLIB_COMPRESSION 1 +#define LZMA_COMPRESSION 2 +#define LZO_COMPRESSION 3 struct squashfs_super_block { __le32 s_magic; diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h index c8c6561..7533350 100644 --- a/fs/squashfs/squashfs_fs_sb.h +++ b/fs/squashfs/squashfs_fs_sb.h @@ -52,25 +52,26 @@ struct squashfs_cache_entry { }; struct squashfs_sb_info { - int devblksize; - int devblksize_log2; - struct squashfs_cache *block_cache; - struct squashfs_cache *fragment_cache; - struct squashfs_cache *read_page; - int next_meta_index; - __le64 *id_table; - __le64 *fragment_index; - unsigned int *fragment_index_2; - struct mutex read_data_mutex; - struct mutex meta_index_mutex; - struct meta_index *meta_index; - z_stream stream; - __le64 *inode_lookup_table; - u64 inode_table; - u64 directory_table; - unsigned int block_size; - unsigned short block_log; - long long bytes_used; - unsigned int inodes; + const struct squashfs_decompressor *decompressor; + int devblksize; + int devblksize_log2; + struct squashfs_cache *block_cache; + struct squashfs_cache *fragment_cache; + struct squashfs_cache *read_page; + int next_meta_index; + __le64 *id_table; + __le64 *fragment_index; + unsigned int *fragment_index_2; + struct mutex read_data_mutex; + struct mutex meta_index_mutex; + struct meta_index *meta_index; + void *stream; + __le64 *inode_lookup_table; + u64 inode_table; + u64 directory_table; + unsigned int block_size; + unsigned short block_log; + long long bytes_used; + unsigned int inodes; }; #endif diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 6c197ef..3550aec 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -35,34 +35,41 @@ #include #include #include -#include #include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" #include "squashfs_fs_i.h" #include "squashfs.h" +#include "decompressor.h" static struct file_system_type squashfs_fs_type; static const struct super_operations squashfs_super_ops; -static int supported_squashfs_filesystem(short major, short minor, short comp) +static const struct squashfs_decompressor *supported_squashfs_filesystem(short + major, short minor, short id) { + const struct squashfs_decompressor *decompressor; + if (major < SQUASHFS_MAJOR) { ERROR("Major/Minor mismatch, older Squashfs %d.%d " "filesystems are unsupported\n", major, minor); - return -EINVAL; + return NULL; } else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) { ERROR("Major/Minor mismatch, trying to mount newer " "%d.%d filesystem\n", major, minor); ERROR("Please update your kernel\n"); - return -EINVAL; + return NULL; } - if (comp != ZLIB_COMPRESSION) - return -EINVAL; + decompressor = squashfs_lookup_decompressor(id); + if (!decompressor->supported) { + ERROR("Filesystem uses \"%s\" compression. This is not " + "supported\n", decompressor->name); + return NULL; + } - return 0; + return decompressor; } @@ -87,13 +94,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) } msblk = sb->s_fs_info; - msblk->stream.workspace = kmalloc(zlib_inflate_workspacesize(), - GFP_KERNEL); - if (msblk->stream.workspace == NULL) { - ERROR("Failed to allocate zlib workspace\n"); - goto failure; - } - sblk = kzalloc(sizeof(*sblk), GFP_KERNEL); if (sblk == NULL) { ERROR("Failed to allocate squashfs_super_block\n"); @@ -120,25 +120,25 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } + err = -EINVAL; + /* Check it is a SQUASHFS superblock */ sb->s_magic = le32_to_cpu(sblk->s_magic); if (sb->s_magic != SQUASHFS_MAGIC) { if (!silent) ERROR("Can't find a SQUASHFS superblock on %s\n", bdevname(sb->s_bdev, b)); - err = -EINVAL; goto failed_mount; } - /* Check the MAJOR & MINOR versions and compression type */ - err = supported_squashfs_filesystem(le16_to_cpu(sblk->s_major), + /* Check the MAJOR & MINOR versions and lookup compression type */ + msblk->decompressor = supported_squashfs_filesystem( + le16_to_cpu(sblk->s_major), le16_to_cpu(sblk->s_minor), le16_to_cpu(sblk->compression)); - if (err < 0) + if (msblk->decompressor == NULL) goto failed_mount; - err = -EINVAL; - /* * Check if there's xattrs in the filesystem. These are not * supported in this version, so warn that they will be ignored. @@ -205,6 +205,10 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) err = -ENOMEM; + msblk->stream = squashfs_decompressor_init(msblk); + if (msblk->stream == NULL) + goto failed_mount; + msblk->block_cache = squashfs_cache_init("metadata", SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE); if (msblk->block_cache == NULL) @@ -292,17 +296,16 @@ failed_mount: squashfs_cache_delete(msblk->block_cache); squashfs_cache_delete(msblk->fragment_cache); squashfs_cache_delete(msblk->read_page); + squashfs_decompressor_free(msblk, msblk->stream); kfree(msblk->inode_lookup_table); kfree(msblk->fragment_index); kfree(msblk->id_table); - kfree(msblk->stream.workspace); kfree(sb->s_fs_info); sb->s_fs_info = NULL; kfree(sblk); return err; failure: - kfree(msblk->stream.workspace); kfree(sb->s_fs_info); sb->s_fs_info = NULL; return -ENOMEM; @@ -346,10 +349,10 @@ static void squashfs_put_super(struct super_block *sb) squashfs_cache_delete(sbi->block_cache); squashfs_cache_delete(sbi->fragment_cache); squashfs_cache_delete(sbi->read_page); + squashfs_decompressor_free(sbi, sbi->stream); kfree(sbi->id_table); kfree(sbi->fragment_index); kfree(sbi->meta_index); - kfree(sbi->stream.workspace); kfree(sb->s_fs_info); sb->s_fs_info = NULL; } diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c index 83d8788..e80be20 100644 --- a/fs/squashfs/symlink.c +++ b/fs/squashfs/symlink.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c new file mode 100644 index 0000000..4dd70e0 --- /dev/null +++ b/fs/squashfs/zlib_wrapper.c @@ -0,0 +1,150 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * zlib_wrapper.c + */ + + +#include +#include +#include + +#include "squashfs_fs.h" +#include "squashfs_fs_sb.h" +#include "squashfs_fs_i.h" +#include "squashfs.h" +#include "decompressor.h" + +static void *zlib_init(struct squashfs_sb_info *dummy) +{ + z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); + if (stream == NULL) + goto failed; + stream->workspace = kmalloc(zlib_inflate_workspacesize(), + GFP_KERNEL); + if (stream->workspace == NULL) + goto failed; + + return stream; + +failed: + ERROR("Failed to allocate zlib workspace\n"); + kfree(stream); + return NULL; +} + + +static void zlib_free(void *strm) +{ + z_stream *stream = strm; + + if (stream) + kfree(stream->workspace); + kfree(stream); +} + + +static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, + struct buffer_head **bh, int b, int offset, int length, int srclength, + int pages) +{ + int zlib_err = 0, zlib_init = 0; + int avail, bytes, k = 0, page = 0; + z_stream *stream = msblk->stream; + + mutex_lock(&msblk->read_data_mutex); + + stream->avail_out = 0; + stream->avail_in = 0; + + bytes = length; + do { + if (stream->avail_in == 0 && k < b) { + avail = min(bytes, msblk->devblksize - offset); + bytes -= avail; + wait_on_buffer(bh[k]); + if (!buffer_uptodate(bh[k])) + goto release_mutex; + + if (avail == 0) { + offset = 0; + put_bh(bh[k++]); + continue; + } + + stream->next_in = bh[k]->b_data + offset; + stream->avail_in = avail; + offset = 0; + } + + if (stream->avail_out == 0 && page < pages) { + stream->next_out = buffer[page++]; + stream->avail_out = PAGE_CACHE_SIZE; + } + + if (!zlib_init) { + zlib_err = zlib_inflateInit(stream); + if (zlib_err != Z_OK) { + ERROR("zlib_inflateInit returned unexpected " + "result 0x%x, srclength %d\n", + zlib_err, srclength); + goto release_mutex; + } + zlib_init = 1; + } + + zlib_err = zlib_inflate(stream, Z_SYNC_FLUSH); + + if (stream->avail_in == 0 && k < b) + put_bh(bh[k++]); + } while (zlib_err == Z_OK); + + if (zlib_err != Z_STREAM_END) { + ERROR("zlib_inflate error, data probably corrupt\n"); + goto release_mutex; + } + + zlib_err = zlib_inflateEnd(stream); + if (zlib_err != Z_OK) { + ERROR("zlib_inflate error, data probably corrupt\n"); + goto release_mutex; + } + + mutex_unlock(&msblk->read_data_mutex); + return stream->total_out; + +release_mutex: + mutex_unlock(&msblk->read_data_mutex); + + for (; k < b; k++) + put_bh(bh[k]); + + return -EIO; +} + +const struct squashfs_decompressor squashfs_zlib_comp_ops = { + .init = zlib_init, + .free = zlib_free, + .decompress = zlib_uncompress, + .id = ZLIB_COMPRESSION, + .name = "zlib", + .supported = 1 +}; + diff --git a/include/linux/decompress/bunzip2_mm.h b/include/linux/decompress/bunzip2_mm.h new file mode 100644 index 0000000..863efd0 --- /dev/null +++ b/include/linux/decompress/bunzip2_mm.h @@ -0,0 +1,13 @@ +#ifndef BUNZIP2_MM_H +#define BUNZIP2_MM_H + +#ifdef STATIC +/* Code active when included from pre-boot environment: */ +#define INIT +#else +/* Compile for initramfs/initrd code only */ +#define INIT __init +static void(*error)(char *m); +#endif + +#endif diff --git a/include/linux/decompress/inflate_mm.h b/include/linux/decompress/inflate_mm.h new file mode 100644 index 0000000..87a742b --- /dev/null +++ b/include/linux/decompress/inflate_mm.h @@ -0,0 +1,13 @@ +#ifndef INFLATE_MM_H +#define INFLATE_MM_H + +#ifdef STATIC +/* Code active when included from pre-boot environment: */ +#define INIT +#else +/* Compile for initramfs/initrd code only */ +#define INIT __init +static void(*error)(char *m); +#endif + +#endif diff --git a/include/linux/decompress/mm.h b/include/linux/decompress/mm.h index 12ff8c3..44f6e14 100644 --- a/include/linux/decompress/mm.h +++ b/include/linux/decompress/mm.h @@ -25,7 +25,7 @@ static void *malloc(int size) void *p; if (size < 0) - error("Malloc error"); + return NULL; if (!malloc_ptr) malloc_ptr = free_mem_ptr; @@ -35,7 +35,7 @@ static void *malloc(int size) malloc_ptr += size; if (free_mem_end_ptr && malloc_ptr >= free_mem_end_ptr) - error("Out of memory"); + return NULL; malloc_count++; return p; @@ -53,8 +53,6 @@ static void free(void *where) #define set_error_fn(x) -#define INIT - #else /* STATIC */ /* Code active when compiled standalone for use when loading ramdisk: */ @@ -74,10 +72,8 @@ static void free(void *where) #define large_malloc(a) vmalloc(a) #define large_free(a) vfree(a) -static void(*error)(char *m); #define set_error_fn(x) error = x; -#define INIT __init #define STATIC #include diff --git a/include/linux/decompress/unlzma_mm.h b/include/linux/decompress/unlzma_mm.h new file mode 100644 index 0000000..859287e --- /dev/null +++ b/include/linux/decompress/unlzma_mm.h @@ -0,0 +1,20 @@ +#ifndef UNLZMA_MM_H +#define UNLZMA_MM_H + +#ifdef STATIC + +/* Code active when included from pre-boot environment: */ +#define INIT + +#elif defined(CONFIG_DECOMPRESS_LZMA_NEEDED) + +/* Make it available to non initramfs/initrd code */ +#define INIT +#include +#else + +/* Compile for initramfs/initrd code only */ +#define INIT __init +#endif + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index bb1326d..25e7f28 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -117,6 +117,9 @@ config DECOMPRESS_BZIP2 config DECOMPRESS_LZMA tristate +config DECOMPRESS_LZMA_NEEDED + boolean + # # Generic allocator support is selected if needed # diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c index 600f473..7db58b9 100644 --- a/lib/decompress_bunzip2.c +++ b/lib/decompress_bunzip2.c @@ -52,6 +52,7 @@ #include #endif /* STATIC */ +#include #include #ifndef INT_MAX @@ -637,6 +638,8 @@ static int INIT start_bunzip(struct bunzip_data **bdp, void *inbuf, int len, /* Allocate bunzip_data. Most fields initialize to zero. */ bd = *bdp = malloc(i); + if (!bd) + return RETVAL_OUT_OF_MEMORY; memset(bd, 0, sizeof(struct bunzip_data)); /* Setup input buffer */ bd->inbuf = inbuf; @@ -664,6 +667,8 @@ static int INIT start_bunzip(struct bunzip_data **bdp, void *inbuf, int len, bd->dbufSize = 100000*(i-BZh0); bd->dbuf = large_malloc(bd->dbufSize * sizeof(int)); + if (!bd->dbuf) + return RETVAL_OUT_OF_MEMORY; return RETVAL_OK; } @@ -686,7 +691,7 @@ STATIC int INIT bunzip2(unsigned char *buf, int len, if (!outbuf) { error("Could not allocate output bufer"); - return -1; + return RETVAL_OUT_OF_MEMORY; } if (buf) inbuf = buf; @@ -694,6 +699,7 @@ STATIC int INIT bunzip2(unsigned char *buf, int len, inbuf = malloc(BZIP2_IOBUF_SIZE); if (!inbuf) { error("Could not allocate input bufer"); + i = RETVAL_OUT_OF_MEMORY; goto exit_0; } i = start_bunzip(&bd, inbuf, len, fill); @@ -720,11 +726,14 @@ STATIC int INIT bunzip2(unsigned char *buf, int len, } else if (i == RETVAL_UNEXPECTED_OUTPUT_EOF) { error("Compressed file ends unexpectedly"); } + if (!bd) + goto exit_1; if (bd->dbuf) large_free(bd->dbuf); if (pos) *pos = bd->inbufPos; free(bd); +exit_1: if (!buf) free(inbuf); exit_0: diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c index fc686c7..cb6bcab 100644 --- a/lib/decompress_inflate.c +++ b/lib/decompress_inflate.c @@ -23,6 +23,7 @@ #endif /* STATIC */ +#include #include #define GZIP_IOBUF_SIZE (16*1024) diff --git a/lib/decompress_unlzma.c b/lib/decompress_unlzma.c index ca82fde..30d7f8e 100644 --- a/lib/decompress_unlzma.c +++ b/lib/decompress_unlzma.c @@ -36,6 +36,7 @@ #include #endif /* STATIC */ +#include #include #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -88,7 +89,7 @@ static int nofill(void *buffer, unsigned int len) } /* Called twice: once at startup and once in rc_normalize() */ -static void INIT rc_read(struct rc *rc) +static void INIT rc_read(struct rc *rc, void(*error)(char *x)) { rc->buffer_size = rc->fill((char *)rc->buffer, LZMA_IOBUF_SIZE); if (rc->buffer_size <= 0) @@ -115,13 +116,13 @@ static inline void INIT rc_init(struct rc *rc, rc->range = 0xFFFFFFFF; } -static inline void INIT rc_init_code(struct rc *rc) +static inline void INIT rc_init_code(struct rc *rc, void(*error)(char *x)) { int i; for (i = 0; i < 5; i++) { if (rc->ptr >= rc->buffer_end) - rc_read(rc); + rc_read(rc, error); rc->code = (rc->code << 8) | *rc->ptr++; } } @@ -134,32 +135,33 @@ static inline void INIT rc_free(struct rc *rc) } /* Called twice, but one callsite is in inline'd rc_is_bit_0_helper() */ -static void INIT rc_do_normalize(struct rc *rc) +static void INIT rc_do_normalize(struct rc *rc, void(*error)(char *x)) { if (rc->ptr >= rc->buffer_end) - rc_read(rc); + rc_read(rc, error); rc->range <<= 8; rc->code = (rc->code << 8) | *rc->ptr++; } -static inline void INIT rc_normalize(struct rc *rc) +static inline void INIT rc_normalize(struct rc *rc, void(*error)(char *x)) { if (rc->range < (1 << RC_TOP_BITS)) - rc_do_normalize(rc); + rc_do_normalize(rc, error); } /* Called 9 times */ /* Why rc_is_bit_0_helper exists? *Because we want to always expose (rc->code < rc->bound) to optimizer */ -static inline uint32_t INIT rc_is_bit_0_helper(struct rc *rc, uint16_t *p) +static inline uint32_t INIT rc_is_bit_0_helper(struct rc *rc, uint16_t *p, + void (*error)(char *x)) { - rc_normalize(rc); + rc_normalize(rc, error); rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS); return rc->bound; } -static inline int INIT rc_is_bit_0(struct rc *rc, uint16_t *p) +static inline int INIT rc_is_bit_0(struct rc *rc, uint16_t *p, void(*error)(char *x)) { - uint32_t t = rc_is_bit_0_helper(rc, p); + uint32_t t = rc_is_bit_0_helper(rc, p, error); return rc->code < t; } @@ -177,9 +179,9 @@ static inline void rc_update_bit_1(struct rc *rc, uint16_t *p) } /* Called 4 times in unlzma loop */ -static int INIT rc_get_bit(struct rc *rc, uint16_t *p, int *symbol) +static int INIT rc_get_bit(struct rc *rc, uint16_t *p, int *symbol, void(*error)(char *x)) { - if (rc_is_bit_0(rc, p)) { + if (rc_is_bit_0(rc, p, error)) { rc_update_bit_0(rc, p); *symbol *= 2; return 0; @@ -191,9 +193,9 @@ static int INIT rc_get_bit(struct rc *rc, uint16_t *p, int *symbol) } /* Called once */ -static inline int INIT rc_direct_bit(struct rc *rc) +static inline int INIT rc_direct_bit(struct rc *rc , void(*error)(char *x)) { - rc_normalize(rc); + rc_normalize(rc, error); rc->range >>= 1; if (rc->code >= rc->range) { rc->code -= rc->range; @@ -204,13 +206,14 @@ static inline int INIT rc_direct_bit(struct rc *rc) /* Called twice */ static inline void INIT -rc_bit_tree_decode(struct rc *rc, uint16_t *p, int num_levels, int *symbol) +rc_bit_tree_decode(struct rc *rc, uint16_t *p, int num_levels, int *symbol, + void(*error)(char *x)) { int i = num_levels; *symbol = 1; while (i--) - rc_get_bit(rc, p + *symbol, symbol); + rc_get_bit(rc, p + *symbol, symbol, error); *symbol -= 1 << num_levels; } @@ -347,7 +350,8 @@ static inline void INIT copy_bytes(struct writer *wr, static inline void INIT process_bit0(struct writer *wr, struct rc *rc, struct cstate *cst, uint16_t *p, int pos_state, uint16_t *prob, - int lc, uint32_t literal_pos_mask) { + int lc, uint32_t literal_pos_mask, + void(*error)(char *x)) { int mi = 1; rc_update_bit_0(rc, prob); prob = (p + LZMA_LITERAL + @@ -365,7 +369,7 @@ static inline void INIT process_bit0(struct writer *wr, struct rc *rc, match_byte <<= 1; bit = match_byte & 0x100; prob_lit = prob + 0x100 + bit + mi; - if (rc_get_bit(rc, prob_lit, &mi)) { + if (rc_get_bit(rc, prob_lit, &mi, error)) { if (!bit) break; } else { @@ -376,7 +380,7 @@ static inline void INIT process_bit0(struct writer *wr, struct rc *rc, } while (mi < 0x100) { uint16_t *prob_lit = prob + mi; - rc_get_bit(rc, prob_lit, &mi); + rc_get_bit(rc, prob_lit, &mi, error); } write_byte(wr, mi); if (cst->state < 4) @@ -389,7 +393,8 @@ static inline void INIT process_bit0(struct writer *wr, struct rc *rc, static inline void INIT process_bit1(struct writer *wr, struct rc *rc, struct cstate *cst, uint16_t *p, - int pos_state, uint16_t *prob) { + int pos_state, uint16_t *prob, + void(*error)(char *x)) { int offset; uint16_t *prob_len; int num_bits; @@ -397,7 +402,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, rc_update_bit_1(rc, prob); prob = p + LZMA_IS_REP + cst->state; - if (rc_is_bit_0(rc, prob)) { + if (rc_is_bit_0(rc, prob, error)) { rc_update_bit_0(rc, prob); cst->rep3 = cst->rep2; cst->rep2 = cst->rep1; @@ -407,13 +412,13 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, } else { rc_update_bit_1(rc, prob); prob = p + LZMA_IS_REP_G0 + cst->state; - if (rc_is_bit_0(rc, prob)) { + if (rc_is_bit_0(rc, prob, error)) { rc_update_bit_0(rc, prob); prob = (p + LZMA_IS_REP_0_LONG + (cst->state << LZMA_NUM_POS_BITS_MAX) + pos_state); - if (rc_is_bit_0(rc, prob)) { + if (rc_is_bit_0(rc, prob, error)) { rc_update_bit_0(rc, prob); cst->state = cst->state < LZMA_NUM_LIT_STATES ? @@ -428,13 +433,13 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, rc_update_bit_1(rc, prob); prob = p + LZMA_IS_REP_G1 + cst->state; - if (rc_is_bit_0(rc, prob)) { + if (rc_is_bit_0(rc, prob, error)) { rc_update_bit_0(rc, prob); distance = cst->rep1; } else { rc_update_bit_1(rc, prob); prob = p + LZMA_IS_REP_G2 + cst->state; - if (rc_is_bit_0(rc, prob)) { + if (rc_is_bit_0(rc, prob, error)) { rc_update_bit_0(rc, prob); distance = cst->rep2; } else { @@ -452,7 +457,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, } prob_len = prob + LZMA_LEN_CHOICE; - if (rc_is_bit_0(rc, prob_len)) { + if (rc_is_bit_0(rc, prob_len, error)) { rc_update_bit_0(rc, prob_len); prob_len = (prob + LZMA_LEN_LOW + (pos_state << @@ -462,7 +467,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, } else { rc_update_bit_1(rc, prob_len); prob_len = prob + LZMA_LEN_CHOICE_2; - if (rc_is_bit_0(rc, prob_len)) { + if (rc_is_bit_0(rc, prob_len, error)) { rc_update_bit_0(rc, prob_len); prob_len = (prob + LZMA_LEN_MID + (pos_state << @@ -478,7 +483,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, } } - rc_bit_tree_decode(rc, prob_len, num_bits, &len); + rc_bit_tree_decode(rc, prob_len, num_bits, &len, error); len += offset; if (cst->state < 4) { @@ -493,7 +498,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, << LZMA_NUM_POS_SLOT_BITS); rc_bit_tree_decode(rc, prob, LZMA_NUM_POS_SLOT_BITS, - &pos_slot); + &pos_slot, error); if (pos_slot >= LZMA_START_POS_MODEL_INDEX) { int i, mi; num_bits = (pos_slot >> 1) - 1; @@ -506,7 +511,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, num_bits -= LZMA_NUM_ALIGN_BITS; while (num_bits--) cst->rep0 = (cst->rep0 << 1) | - rc_direct_bit(rc); + rc_direct_bit(rc, error); prob = p + LZMA_ALIGN; cst->rep0 <<= LZMA_NUM_ALIGN_BITS; num_bits = LZMA_NUM_ALIGN_BITS; @@ -514,7 +519,7 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, i = 1; mi = 1; while (num_bits--) { - if (rc_get_bit(rc, prob + mi, &mi)) + if (rc_get_bit(rc, prob + mi, &mi, error)) cst->rep0 |= i; i <<= 1; } @@ -531,12 +536,12 @@ static inline void INIT process_bit1(struct writer *wr, struct rc *rc, -STATIC inline int INIT unlzma(unsigned char *buf, int in_len, +STATIC int INIT unlzma(unsigned char *buf, int in_len, int(*fill)(void*, unsigned int), int(*flush)(void*, unsigned int), unsigned char *output, int *posp, - void(*error_fn)(char *x) + void(*error)(char *x) ) { struct lzma_header header; @@ -552,8 +557,6 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len, unsigned char *inbuf; int ret = -1; - set_error_fn(error_fn); - if (buf) inbuf = buf; else @@ -576,7 +579,7 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len, for (i = 0; i < sizeof(header); i++) { if (rc.ptr >= rc.buffer_end) - rc_read(&rc); + rc_read(&rc, error); ((unsigned char *)&header)[i] = *rc.ptr++; } @@ -621,17 +624,17 @@ STATIC inline int INIT unlzma(unsigned char *buf, int in_len, for (i = 0; i < num_probs; i++) p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1; - rc_init_code(&rc); + rc_init_code(&rc, error); while (get_pos(&wr) < header.dst_size) { int pos_state = get_pos(&wr) & pos_state_mask; uint16_t *prob = p + LZMA_IS_MATCH + (cst.state << LZMA_NUM_POS_BITS_MAX) + pos_state; - if (rc_is_bit_0(&rc, prob)) + if (rc_is_bit_0(&rc, prob, error)) process_bit0(&wr, &rc, &cst, p, pos_state, prob, - lc, literal_pos_mask); + lc, literal_pos_mask, error); else { - process_bit1(&wr, &rc, &cst, p, pos_state, prob); + process_bit1(&wr, &rc, &cst, p, pos_state, prob, error); if (cst.rep0 == 0) break; } @@ -664,4 +667,6 @@ STATIC int INIT decompress(unsigned char *buf, int in_len, { return unlzma(buf, in_len - 4, fill, flush, output, posp, error_fn); } +#elif defined(CONFIG_DECOMPRESS_LZMA_NEEDED) +EXPORT_SYMBOL(unlzma); #endif