diff --git a/common/filetype.c b/common/filetype.c index 4728f87..8d72933 100644 --- a/common/filetype.c +++ b/common/filetype.c @@ -40,6 +40,7 @@ [filetype_uimage] = { "U-Boot uImage", "u-boot" }, [filetype_ubi] = { "UBI image", "ubi" }, [filetype_jffs2] = { "JFFS2 image", "jffs2" }, + [filetype_squashfs] = { "Squashfs image", "squashfs" }, [filetype_gzip] = { "GZIP compressed", "gzip" }, [filetype_bzip2] = { "BZIP2 compressed", "bzip2" }, [filetype_oftree] = { "open firmware Device Tree flattened Binary", "dtb" }, @@ -278,6 +279,9 @@ if (buf8[0] == 0xfd && buf8[1] == 0x37 && buf8[2] == 0x7a && buf8[3] == 0x58 && buf8[4] == 0x5a && buf8[5] == 0x00) return filetype_xz_compressed; + if (buf8[0] == 'h' && buf8[1] == 's' && buf8[2] == 'q' && + buf8[3] == 's') + return filetype_squashfs; if (buf[0] == be32_to_cpu(0xd00dfeed)) return filetype_oftree; if (strncmp(buf8, "ANDROID!", 8) == 0) diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index dc25d93..2362ba1 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig @@ -17,16 +17,43 @@ embedded systems where low overhead is needed. Further information and tools are available from http://squashfs.sourceforge.net. -config SQUASHFS_XZ - bool "Include support for XZ compressed file systems" - default y +config SQUASHFS_ZLIB + bool depends on FS_SQUASHFS - select XZ_DECOMPRESS - help - Saying Y here includes support for reading Squashfs file systems - compressed with XZ compression. XZ gives better compression than - the default zlib compression, at the expense of greater CPU and - memory overhead. + depends on ZLIB + default y - XZ is not the standard compression used in Squashfs and so most - file systems will be readable without selecting this option. +if !SQUASHFS_ZLIB + comment "ZLIB support disabled" +endif + +config SQUASHFS_LZ4 + bool + depends on FS_SQUASHFS + depends on LZ4_DECOMPRESS + default y + +if !SQUASHFS_LZ4 + comment "LZ4 support disabled" +endif + +config SQUASHFS_LZO + bool + depends on FS_SQUASHFS + depends on LZO_DECOMPRESS + default y + +if !SQUASHFS_LZO + comment "LZO support disabled" +endif + +config SQUASHFS_XZ + bool + depends on FS_SQUASHFS + depends on XZ_DECOMPRESS + default y + +if !SQUASHFS_XZ + comment "XZ support disabled" +endif + diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index c0d024c..7976e3f 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile @@ -11,3 +11,6 @@ obj-y += namei.o obj-y += super.o obj-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o +obj-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o +obj-$(CONFIG_SQUASHFS_LZ4) += lz4_wrapper.o +obj-$(CONFIG_SQUASHFS_ZLIB) += zlib_wrapper.o diff --git a/fs/squashfs/lz4_wrapper.c b/fs/squashfs/lz4_wrapper.c new file mode 100644 index 0000000..6ca6a32 --- /dev/null +++ b/fs/squashfs/lz4_wrapper.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2013, 2014 + * Phillip Lougher + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include +#include +#include + +#include "squashfs_fs.h" +#include "squashfs_fs_sb.h" +#include "squashfs.h" +#include "decompressor.h" +#include "page_actor.h" + +#define LZ4_LEGACY 1 + +struct lz4_comp_opts { + __le32 version; + __le32 flags; +}; + +struct squashfs_lz4 { + void *input; + void *output; +}; + + +static void *lz4_comp_opts(struct squashfs_sb_info *msblk, + void *buff, int len) +{ + struct lz4_comp_opts *comp_opts = buff; + + /* LZ4 compressed filesystems always have compression options */ + if (comp_opts == NULL || len < sizeof(*comp_opts)) + return ERR_PTR(-EIO); + + if (le32_to_cpu(comp_opts->version) != LZ4_LEGACY) { + /* LZ4 format currently used by the kernel is the 'legacy' + * format */ + ERROR("Unknown LZ4 version\n"); + return ERR_PTR(-EINVAL); + } + + return NULL; +} + + +static void *lz4_init(struct squashfs_sb_info *msblk, void *buff) +{ + int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); + struct squashfs_lz4 *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (stream == NULL) + goto failed; + stream->input = vmalloc(block_size); + if (stream->input == NULL) + goto failed2; + stream->output = vmalloc(block_size); + if (stream->output == NULL) + goto failed3; + + return stream; + +failed3: + vfree(stream->input); +failed2: + kfree(stream); +failed: + ERROR("Failed to initialise LZ4 decompressor\n"); + return ERR_PTR(-ENOMEM); +} + + +static void lz4_free(void *strm) +{ + struct squashfs_lz4 *stream = strm; + + if (stream) { + vfree(stream->input); + vfree(stream->output); + } + kfree(stream); +} + + +static int lz4_uncompress(struct squashfs_sb_info *msblk, void *strm, + char **bh, int b, int offset, int length, + struct squashfs_page_actor *output) +{ + struct squashfs_lz4 *stream = strm; + void *buff = stream->input, *data; + int avail, i, bytes = length, res; + size_t dest_len = output->length; + + for (i = 0; i < b; i++) { + avail = min(bytes, msblk->devblksize - offset); + memcpy(buff, bh[i] + offset, avail); + buff += avail; + bytes -= avail; + offset = 0; + kfree(bh[i]); + } + + res = lz4_decompress_unknownoutputsize(stream->input, length, + stream->output, &dest_len); + if (res) + return -EIO; + + bytes = dest_len; + data = squashfs_first_page(output); + buff = stream->output; + while (data) { + if (bytes <= PAGE_CACHE_SIZE) { + memcpy(data, buff, bytes); + break; + } + memcpy(data, buff, PAGE_CACHE_SIZE); + buff += PAGE_CACHE_SIZE; + bytes -= PAGE_CACHE_SIZE; + data = squashfs_next_page(output); + } + squashfs_finish_page(output); + + return dest_len; +} + +const struct squashfs_decompressor squashfs_lz4_comp_ops = { + .init = lz4_init, + .comp_opts = lz4_comp_opts, + .free = lz4_free, + .decompress = lz4_uncompress, + .id = LZ4_COMPRESSION, + .name = "lz4", + .supported = 1 +}; diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c new file mode 100644 index 0000000..834ee29 --- /dev/null +++ b/fs/squashfs/lzo_wrapper.c @@ -0,0 +1,129 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2010 LG Electronics + * Chan Jeong + * + * 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. + * + * lzo_wrapper.c + */ + +#include +#include +#include +#include + +#include "squashfs_fs.h" +#include "squashfs_fs_sb.h" +#include "squashfs.h" +#include "decompressor.h" +#include "page_actor.h" + +struct squashfs_lzo { + void *input; + void *output; +}; + +static void *lzo_init(struct squashfs_sb_info *msblk, void *buff) +{ + int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); + + struct squashfs_lzo *stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (stream == NULL) + goto failed; + stream->input = vmalloc(block_size); + if (stream->input == NULL) + goto failed; + stream->output = vmalloc(block_size); + if (stream->output == NULL) + goto failed2; + + return stream; + +failed2: + vfree(stream->input); +failed: + ERROR("Failed to allocate lzo workspace\n"); + kfree(stream); + return ERR_PTR(-ENOMEM); +} + + +static void lzo_free(void *strm) +{ + struct squashfs_lzo *stream = strm; + + if (stream) { + vfree(stream->input); + vfree(stream->output); + } + kfree(stream); +} + + +static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, + char **bh, int b, int offset, int length, + struct squashfs_page_actor *output) +{ + struct squashfs_lzo *stream = strm; + void *buff = stream->input, *data; + int avail, i, bytes = length, res; + size_t out_len = output->length; + + for (i = 0; i < b; i++) { + avail = min(bytes, msblk->devblksize - offset); + memcpy(buff, bh[i] + offset, avail); + buff += avail; + bytes -= avail; + offset = 0; + kfree(bh[i]); + } + + res = lzo1x_decompress_safe(stream->input, (size_t)length, + stream->output, &out_len); + if (res != LZO_E_OK) + goto failed; + + res = bytes = (int)out_len; + data = squashfs_first_page(output); + buff = stream->output; + while (data) { + if (bytes <= PAGE_CACHE_SIZE) { + memcpy(data, buff, bytes); + break; + } else { + memcpy(data, buff, PAGE_CACHE_SIZE); + buff += PAGE_CACHE_SIZE; + bytes -= PAGE_CACHE_SIZE; + data = squashfs_next_page(output); + } + } + squashfs_finish_page(output); + + return res; + +failed: + return -EIO; +} + +const struct squashfs_decompressor squashfs_lzo_comp_ops = { + .init = lzo_init, + .free = lzo_free, + .decompress = lzo_uncompress, + .id = LZO_COMPRESSION, + .name = "lzo", + .supported = 1 +}; diff --git a/fs/squashfs/squashfs.c b/fs/squashfs/squashfs.c index d00dee6..c4d0bac 100644 --- a/fs/squashfs/squashfs.c +++ b/fs/squashfs/squashfs.c @@ -9,12 +9,16 @@ #include #include #include +#include +#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" #include "squashfs_fs_i.h" #include "squashfs.h" +struct ubi_volume_desc; + char *squashfs_devread(struct squashfs_sb_info *fs, int byte_offset, int byte_len) { @@ -108,6 +112,31 @@ return NULL; } +void squashfs_set_rootarg(struct squashfs_priv *priv, struct fs_device_d *fsdev) +{ + struct ubi_volume_desc *ubi_vol; + struct ubi_volume_info vi = {}; + struct ubi_device_info di = {}; + struct mtd_info *mtd; + char *str; + + ubi_vol = ubi_open_volume_cdev(fsdev->cdev, UBI_READONLY); + + if (IS_ERR(ubi_vol)) + return; + + ubi_get_volume_info(ubi_vol, &vi); + ubi_get_device_info(vi.ubi_num, &di); + mtd = di.mtd; + + str = basprintf("root=/dev/ubiblock%d_%d ubi.mtd=%s ubi.block=%d,%d rootfstype=squashfs", + vi.ubi_num, vi.vol_id, mtd->cdev.partname, vi.ubi_num, vi.vol_id); + + fsdev_set_linux_rootarg(fsdev, str); + + free(str); +} + static int squashfs_probe(struct device_d *dev) { struct fs_device_d *fsdev; @@ -130,6 +159,8 @@ goto err_out; } + squashfs_set_rootarg(priv, fsdev); + return 0; err_out: @@ -353,6 +384,7 @@ .readdir = squashfs_readdir, .closedir = squashfs_closedir, .stat = squashfs_stat, + .type = filetype_squashfs, .drv = { .probe = squashfs_probe, .remove = squashfs_remove, diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c new file mode 100644 index 0000000..f422515 --- /dev/null +++ b/fs/squashfs/zlib_wrapper.c @@ -0,0 +1,132 @@ +/* + * 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 "squashfs_fs.h" +#include "squashfs_fs_sb.h" +#include "squashfs.h" +#include "decompressor.h" +#include "page_actor.h" + +static void *zlib_init(struct squashfs_sb_info *dummy, void *buff) +{ + z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); + if (stream == NULL) + goto failed; + stream->workspace = vmalloc(zlib_inflate_workspacesize()); + if (stream->workspace == NULL) + goto failed; + + return stream; + +failed: + ERROR("Failed to allocate zlib workspace\n"); + kfree(stream); + return ERR_PTR(-ENOMEM); +} + + +static void zlib_free(void *strm) +{ + z_stream *stream = strm; + + if (stream) + vfree(stream->workspace); + kfree(stream); +} + + +static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, + char **bh, int b, int offset, int length, + struct squashfs_page_actor *output) +{ + int zlib_err, zlib_init = 0, k = 0; + z_stream *stream = strm; + + stream->avail_out = PAGE_CACHE_SIZE; + stream->next_out = squashfs_first_page(output); + stream->avail_in = 0; + + do { + if (stream->avail_in == 0 && k < b) { + int avail = min(length, msblk->devblksize - offset); + length -= avail; + stream->next_in = bh[k] + offset; + stream->avail_in = avail; + offset = 0; + } + + if (stream->avail_out == 0) { + stream->next_out = squashfs_next_page(output); + if (stream->next_out != NULL) + stream->avail_out = PAGE_CACHE_SIZE; + } + + if (!zlib_init) { + zlib_err = zlib_inflateInit(stream); + if (zlib_err != Z_OK) { + squashfs_finish_page(output); + goto out; + } + zlib_init = 1; + } + + zlib_err = zlib_inflate(stream, Z_SYNC_FLUSH); + + if (stream->avail_in == 0 && k < b) + kfree(bh[k++]); + } while (zlib_err == Z_OK); + + squashfs_finish_page(output); + + if (zlib_err != Z_STREAM_END) + goto out; + + zlib_err = zlib_inflateEnd(stream); + if (zlib_err != Z_OK) + goto out; + + if (k < b) + goto out; + + return stream->total_out; + +out: + for (; k < b; k++) + kfree(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/filetype.h b/include/filetype.h index cde73c1..65bd6ef 100644 --- a/include/filetype.h +++ b/include/filetype.h @@ -16,6 +16,7 @@ filetype_uimage, filetype_ubi, filetype_jffs2, + filetype_squashfs, filetype_gzip, filetype_bzip2, filetype_oftree,