[PATCH 3/3] squashfs: add MTD backend

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 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-embedded" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Gstreamer Embedded]     [Linux MMC Devel]     [U-Boot V2]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux ARM Kernel]     [Linux OMAP]     [Linux SCSI]

  Powered by Linux