--- fs/squashfs/Kconfig | 1 + fs/squashfs/Makefile | 1 + fs/squashfs/backend.c | 15 ++++ fs/squashfs/mtd.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/squashfs/squashfs.h | 4 + 5 files changed, 200 insertions(+), 0 deletions(-) create mode 100644 fs/squashfs/mtd.c diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index 40a3f15..6849e70 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig @@ -1,5 +1,6 @@ config SQUASHFS tristate "SquashFS 4.0 - Squashed file system support" + depends on BLOCK || MTD select ZLIB_INFLATE help Saying Y here includes support for SquashFS 4.0 (a Compressed diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 80f1cbe..8d5c0b8 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile @@ -7,3 +7,4 @@ squashfs-y += cache.o dir.o export.o file.o fragment.o id.o inode.o squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o backend.o squashfs-$(CONFIG_SQUASHFS_LZMA) += lzma_wrapper.o squashfs-$(CONFIG_BLOCK) += block.o +squashfs-$(CONFIG_MTD) += mtd.o diff --git a/fs/squashfs/backend.c b/fs/squashfs/backend.c index b83a5e2..a6136ca 100644 --- a/fs/squashfs/backend.c +++ b/fs/squashfs/backend.c @@ -1,6 +1,7 @@ #include <linux/fs.h> #include <linux/slab.h> #include <linux/buffer_head.h> +#include <linux/mtd/super.h> #include "squashfs_fs_i.h" #include "squashfs.h" @@ -13,6 +14,10 @@ int squashfs_find_backend(struct file_system_type *fs_type, int flags, if (!get_sb_bdev(fs_type, flags, dev_name, data, fill_bdev_super, mnt)) return 0; #endif +#ifdef CONFIG_MTD + if (!get_sb_mtd(fs_type, flags, dev_name, data, fill_mtd_super, mnt)) + return 0; +#endif WARNING("no suitable backend found\n"); return -EINVAL; } @@ -25,6 +30,12 @@ void squashfs_kill_super(struct super_block *sb) return; } #endif +#ifdef CONFIG_MTD + if (sb->s_mtd) { + kill_mtd_super(sb); + return; + } +#endif ERROR("squashfs_kill_super: no device behind the super block\n"); } @@ -43,6 +54,10 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, if (sb->s_bdev) return bdev_read_data(sb, buffer, index, length, next_index, srclength, pages); #endif +#ifdef CONFIG_MTD + if (sb->s_mtd) + return mtd_read_data(sb, buffer, index, length, next_index, srclength, pages); +#endif ERROR("squashfs_read_data: no device behind the super block\n"); return -EIO; } diff --git a/fs/squashfs/mtd.c b/fs/squashfs/mtd.c new file mode 100644 index 0000000..b067616 --- /dev/null +++ b/fs/squashfs/mtd.c @@ -0,0 +1,179 @@ +/* + * mtd.c + */ + +/* + * This file implements the low-level routines to read and decompress + * datablocks and metadata blocks from an MTD. + */ + +#include <linux/fs.h> +#include <linux/vfs.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/buffer_head.h> +#include <linux/mtd/mtd.h> + +#include "squashfs_fs.h" +#include "squashfs_fs_sb.h" +#include "squashfs_fs_i.h" +#include "squashfs.h" +#include "decompressor.h" + +static int checked_mtd_read(struct mtd_info *mi, u64 index, int length, + void *buf) +{ + int ret, retlen; + + TRACE("Entering checked_mtd_read: index=0x%llx, length=%d\n", + index, length); + ret = mi->read(mi, index, length, &retlen, buf); + if (ret) { + if (ret == -EUCLEAN || ret == -EBADMSG) + WARNING("checked_mtd_read(index=0x%llx, length=%d): " + "recoverable error %d\n", index, length, ret); + else { + ERROR("checked_mtd_read(index=0x%llx, length=%d): %d\n", + index, length, ret); + return ret; + } + } + if (retlen != length) { + ERROR("checked_mtd_read(index=0x%llx, length=%d) short read: %d\n", + index, length, retlen); + return -EIO; + } + return 0; +} + +static int update_buffer(struct buffer_head *bh) +{ + struct mtd_info *mi = (struct mtd_info *)bh->b_bdev; + int ret = checked_mtd_read(mi, bh->b_blocknr, bh->b_size, bh->b_data); + if (ret) + return 0; + return 1; +} + +static void put_buffer(struct buffer_head *bh) +{ +} + +/* + * Big buffer_heads require more memory, but if a single one is enough, + * that can be special-cased in unlzma to avoid the extra memcpy. + * A better unlzma interface would be preferable, though. + */ +int mtd_read_data(struct super_block *sb, void **buffer, u64 index, + int length, u64 *next_index, int srclength, int pages) +{ + struct squashfs_sb_info *msblk = sb->s_fs_info; + struct mtd_info *mi = sb->s_mtd; + u64 i = index; + int bytes_left, compressed; + + if (length) { /* Data block */ + compressed = SQUASHFS_COMPRESSED_BLOCK(length); + length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length); + + TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", + index, compressed ? "" : "un", length, srclength); + } else { /* Metadata block */ + u16 metalen; + if ((index + 2) > msblk->bytes_used) + goto read_failure; + if (checked_mtd_read(mi, index, 2, &metalen)) + goto read_failure; + i += 2; + length = le16_to_cpu(metalen); + compressed = SQUASHFS_COMPRESSED(length); + length = SQUASHFS_COMPRESSED_SIZE(length); + + TRACE("Block @ 0x%llx, %scompressed size %d\n", index, + compressed ? "" : "un", length); + } + if (next_index) + *next_index = i + length; + + if (length < 0 || length > srclength || i + length > msblk->bytes_used) + goto read_failure; + + if (compressed) { + struct buffer_head **bh, *bhs; + int bh_num = (max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE) >> + msblk->devblksize_log2) + 1; + u_char *data; + int b; + + bh = kmalloc(bh_num * sizeof(*bh), GFP_KERNEL); + if (bh == NULL) + return -ENOMEM; + bhs = kmalloc(bh_num * sizeof(*bhs), GFP_KERNEL); + if (bhs == NULL) { + kfree(bh); + return -ENOMEM; + } + data = kmalloc(msblk->devblksize, GFP_KERNEL); + if (data == NULL) { + kfree(bhs); + kfree(bh); + return -ENOMEM; + } + + bytes_left = length; + for (b = 0; bytes_left > 0; b++) { + bh[b] = &bhs[b]; + bhs[b].b_blocknr = i; + bhs[b].b_size = min(msblk->devblksize, bytes_left); + /* We know that the decompressors will use each buffer_head + * only once, so update_buffer may change the data under them. */ + bhs[b].b_data = data; + bhs[b].b_bdev = (void *)mi; + i += msblk->devblksize; + bytes_left -= msblk->devblksize; + } + + length = squashfs_decompress(msblk, buffer, bh, b, 0, + length, srclength, pages, update_buffer, put_buffer); + if (length < 0) { + kfree(data); + kfree(bhs); + kfree(bh); + goto read_failure; + } + } else { /* Not compressed */ + int page = 0; + bytes_left = length; + while (bytes_left > 0) { + int blk = min_t(int, bytes_left, PAGE_CACHE_SIZE); + if (checked_mtd_read(mi, i, blk, buffer[page++])) + goto read_failure; + bytes_left -= blk; + i += blk; + } + } + return length; + +read_failure: + ERROR("mtd_read_data failed to read block 0x%llx\n", + (unsigned long long) index); + return -EIO; +} + +int fill_mtd_super(struct super_block *sb, void *data, int silent) +{ + struct squashfs_sb_info *msblk; + char b[BDEVNAME_SIZE]; + + TRACE("Entering fill_mtd_super\n"); + + msblk = kzalloc(sizeof(*msblk), GFP_KERNEL); + if (!msblk) + return -ENOMEM; + + sb->s_fs_info = msblk; + msblk->devblksize = PAGE_CACHE_SIZE; + msblk->devblksize_log2 = PAGE_CACHE_SHIFT; + snprintf(b, sizeof b, "mtd%d", sb->s_mtd->index); + return squashfs_fill_super(sb, data, silent, b, sb->s_mtd->size); +} diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 7c5cd72..05a85e2 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -105,6 +105,10 @@ extern const struct squashfs_decompressor squashfs_lzma_comp_ops; extern int fill_bdev_super(struct super_block *, void *, int); extern int bdev_read_data(struct super_block *, void **, u64, int, u64 *, int, int); +/* mtd.c */ +extern int fill_mtd_super(struct super_block *, void *, int); +extern int mtd_read_data(struct super_block *, void **, u64, int, u64 *, int, int); + #ifndef CONFIG_BLOCK struct buffer_head { sector_t b_blocknr; -- 1.6.5 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html