Re: RFC: direct MTD support for SquashFS

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

 



Phillip Lougher <phillip.lougher@xxxxxxxxx> writes:

> A couple of specific comments...
>
> +/* A backend is initialized for each SquashFS block read operation,
> + * making further sequential reads possible from the block.
> + */
> +static void *bdev_init(struct squashfs_sb_info *msblk, u64 index,
> size_t length)
> +{
> +	struct squashfs_bdev *bdev = msblk->backend_data;
> +	struct buffer_head *bh;
> +
> +	bh = kcalloc((msblk->block_size >> bdev->devblksize_log2) + 1,
> +			sizeof(*bh), GFP_KERNEL);
>
> You should alloc against the larger of msblk->block_size and
> METADATA_SIZE (8 Kbytes).  Block_size could be 4 Kbytes only.

I plugged in a max().  Couldn't that trailing +1 be converted into a +2
like this?

bh = kcalloc((max(msblk->block_size, METADATA_SIZE) + 2) >> bdev->devblksize_log2

> +static int fill_bdev_super(struct super_block *sb, void *data, int silent)
>
> This function looks rather 'back-to-front' to me.  I'm assuming that
> squashfs_fill_super2() will be the current fill superblock function?
> This function wants to read data off the filesystem through the
> backend, and yet the backend (bdev, mblk->backend_data) hasn't been
> initialised when it's called...

I solved it by introducing a callback function for adding the backend.
That may be overkill, but it seems to give the most shared code.

The attached patch series survived some testing here.  My only doubt:
the current backend interface necessitates a memory copy from the buffer
heads.  This is no problem for mtd and lzma which copy the data anyway,
but makes this code less efficient in the bdev+zlib case.

I've got one more patch, which I forgot to export, to pull out the
common logic from the backend init functions back into squashfs_read_data().
With the bdev backend, that entails reading the first block twice in a
row most of the time.  This again could be worked around by extending
the backend interface, but I'm not sure if it's worth it.

How does this look like now?
-- 
Regards,
Feri.

>From 68f194727a9905e01d216918200671140ecbc04e Mon Sep 17 00:00:00 2001
From: Ferenc Wagner <wferi@xxxxxxx>
Date: Thu, 18 Mar 2010 03:12:01 +0100
Subject: [PATCH] squashfs: add backend plugin framework

---
 fs/squashfs/Makefile         |    3 +-
 fs/squashfs/backend.c        |   24 +++++++++
 fs/squashfs/backend.h        |   22 ++++++++
 fs/squashfs/bdev.c           |  109 ++++++++++++++++++++++++++++++++++++++++++
 fs/squashfs/squashfs.h       |    9 +++-
 fs/squashfs/squashfs_fs_sb.h |    2 +
 6 files changed, 167 insertions(+), 2 deletions(-)
 create mode 100644 fs/squashfs/backend.c
 create mode 100644 fs/squashfs/backend.h
 create mode 100644 fs/squashfs/bdev.c

diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
index 45aaefd..0f02891 100644
--- a/fs/squashfs/Makefile
+++ b/fs/squashfs/Makefile
@@ -4,5 +4,6 @@
 
 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 zlib_wrapper.o decompressor.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) += bdev.o
diff --git a/fs/squashfs/backend.c b/fs/squashfs/backend.c
new file mode 100644
index 0000000..4411c60
--- /dev/null
+++ b/fs/squashfs/backend.c
@@ -0,0 +1,24 @@
+#include <linux/fs.h>
+
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+#include "backend.h"
+
+const struct squashfs_backend *backends[] = {
+#ifdef CONFIG_BLOCK
+	&squashfs_bdev_ops,
+#endif
+	NULL
+};
+
+const struct squashfs_backend *
+squashfs_find_backend(struct file_system_type *fs_type, int flags,
+			const char *dev_name, void *data, struct vfsmount *mnt)
+{
+	const struct squashfs_backend **b;
+
+	for (b = backends; *b; b++)
+		if (!(*b)->probe(fs_type, flags, dev_name, data, mnt))
+			break;
+	return *b;
+}
diff --git a/fs/squashfs/backend.h b/fs/squashfs/backend.h
new file mode 100644
index 0000000..ca3ffe1
--- /dev/null
+++ b/fs/squashfs/backend.h
@@ -0,0 +1,22 @@
+#include <linux/fs.h>
+#include <linux/vfs.h>
+
+#include "squashfs_fs_sb.h"
+
+struct squashfs_backend {
+	void	*(*init)(struct squashfs_sb_info *, u64, size_t);
+	void	(*free)(struct squashfs_sb_info *);
+	ssize_t	(*read)(struct squashfs_sb_info *, void **, size_t);
+	int	(*probe)(struct file_system_type *, int, const char *,
+				void*, struct vfsmount *);
+	void	(*kill)(struct squashfs_sb_info *);
+	loff_t  (*size)(const struct super_block *);
+	const char *(*devname)(const struct super_block *, char *);
+};
+
+/* Dummy, until the original is nuked */
+static inline int squashfs_fill_super2(struct super_block *sb, void *data,
+				int silent, int (*add_bend)(struct super_block *))
+{
+	return -1;
+}
diff --git a/fs/squashfs/bdev.c b/fs/squashfs/bdev.c
new file mode 100644
index 0000000..fcc3b83
--- /dev/null
+++ b/fs/squashfs/bdev.c
@@ -0,0 +1,109 @@
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+#include "backend.h"
+
+struct squashfs_bdev {
+	int devblksize;			/* FIXME: == s->s_blocksize(_bits)? */
+	unsigned short devblksize_log2;
+	size_t bytes_left;
+	struct buffer_head **bh;
+	int bh_index;			/* number of consumed buffer_heads */
+	int offset;			/* offset of next byte in buffer_head */
+};
+
+/* A backend is initialized for each SquashFS block read operation,
+ * making further sequential reads possible from the block.
+ */
+static void *bdev_init(struct squashfs_sb_info *msblk, u64 index, size_t length)
+{
+	struct squashfs_bdev *bdev = msblk->backend_data;
+	struct buffer_head *bh;
+
+	bh = kcalloc((max(msblk->block_size, METADATA_SIZE) >> bdev->devblksize_log2) + 1,
+			sizeof(*bh), GFP_KERNEL);
+	if (!bh)
+		goto nomem;
+
+	/* different preread for data blocks and metadata blocks */
+
+	bdev->bh_index = 0;
+	bdev->bytes_left = length;
+	return bdev;
+
+nomem:
+	ERROR("failed to allocate buffer_heads\n");
+	return NULL;
+}
+
+static void bdev_free(struct squashfs_sb_info *msblk)
+{
+	struct squashfs_bdev *bdev = msblk->backend_data;
+	kfree(bdev->bh);
+	bdev->bh = 0; /* FIXME: to make bdev_kill universal (see there) */
+}
+
+static ssize_t bdev_read(struct squashfs_sb_info *msblk, void **buf, size_t len)
+{
+	return -ENOSYS;
+}
+
+static int add_bdev_backend(struct super_block *sb)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	struct squashfs_bdev *bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
+
+	if (!bdev)
+		return -ENOMEM;
+
+	bdev->devblksize = sb_min_blocksize(sb, BLOCK_SIZE);
+	bdev->devblksize_log2 = ffz(~bdev->devblksize);
+
+	msblk->backend = &squashfs_bdev_ops;
+	msblk->backend_data = bdev;
+	return 0;
+}
+
+static int fill_bdev_super(struct super_block *sb, void *data, int silent)
+{
+	return squashfs_fill_super2(sb, data, silent, add_bdev_backend);
+}
+
+static int bdev_probe(struct file_system_type *fs_type, int flags,
+			const char *dev_name, void *data,
+			struct vfsmount *mnt)
+{
+	return get_sb_bdev(fs_type, flags, dev_name, data,
+				fill_bdev_super, mnt);
+}
+
+static void bdev_kill(struct squashfs_sb_info *msblk)
+{
+	struct squashfs_bdev *bdev = msblk->backend_data;
+	if (bdev) {
+		kfree(bdev->bh); /* FIXME: can this be nonzero? cf. bdev_free */
+		kfree(bdev);
+	}
+}
+
+static loff_t bdev_size(const struct super_block *sb)
+{
+	return i_size_read(sb->s_bdev->bd_inode);
+}
+
+static const char *bdev_devname(const struct super_block *sb, char *buffer)
+{
+	return bdevname(sb->s_bdev, buffer);
+}
+
+const struct squashfs_backend squashfs_bdev_ops = {
+	.init	= bdev_init,
+	.free	= bdev_free,
+	.read	= bdev_read,
+	.probe	= bdev_probe,
+	.kill	= bdev_kill,
+	.size	= bdev_size,
+	.devname= bdev_devname
+};
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index d094886..0e74175 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -73,8 +73,12 @@ extern struct inode *squashfs_iget(struct super_block *, long long,
 				unsigned int);
 extern int squashfs_read_inode(struct inode *, long long);
 
+/* super.c
+int squashfs_fill_super(struct super_block *, void *, int,
+                        struct squashfs_backend *);
+*/
 /*
- * Inodes, files and decompressor operations
+ * Inodes, files, backend and decompressor operations
  */
 
 /* dir.c */
@@ -97,3 +101,6 @@ extern const struct squashfs_decompressor squashfs_zlib_comp_ops;
 
 /* lzma wrapper.c */
 extern const struct squashfs_decompressor squashfs_lzma_comp_ops;
+
+/* bdev.c */
+extern const struct squashfs_backend squashfs_bdev_ops;
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index 7533350..5662d9b 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -53,6 +53,8 @@ struct squashfs_cache_entry {
 
 struct squashfs_sb_info {
 	const struct squashfs_decompressor	*decompressor;
+	const struct squashfs_backend		*backend;
+	void					*backend_data;
 	int					devblksize;
 	int					devblksize_log2;
 	struct squashfs_cache			*block_cache;
-- 
1.5.6.5


>From 7f3d85818017d128cc438fefdf300f770dd7869e Mon Sep 17 00:00:00 2001
From: Ferenc Wagner <wferi@xxxxxxx>
Date: Sat, 20 Mar 2010 15:58:21 +0100
Subject: [PATCH] squashfs: thread initialization through the backend framework

This necessitated introducing the killsb callback.
The read path is still untouched.
---
 fs/squashfs/backend.c  |   22 +++++++++++++++-----
 fs/squashfs/backend.h  |   11 ++++-----
 fs/squashfs/bdev.c     |    3 +-
 fs/squashfs/squashfs.h |    6 ++--
 fs/squashfs/super.c    |   49 +++++++++++++++++++++--------------------------
 5 files changed, 48 insertions(+), 43 deletions(-)

diff --git a/fs/squashfs/backend.c b/fs/squashfs/backend.c
index 4411c60..49fa6fa 100644
--- a/fs/squashfs/backend.c
+++ b/fs/squashfs/backend.c
@@ -11,14 +11,24 @@ const struct squashfs_backend *backends[] = {
 	NULL
 };
 
-const struct squashfs_backend *
-squashfs_find_backend(struct file_system_type *fs_type, int flags,
+int squashfs_find_backend(struct file_system_type *fs_type, int flags,
 			const char *dev_name, void *data, struct vfsmount *mnt)
 {
 	const struct squashfs_backend **b;
+	int err;
 
-	for (b = backends; *b; b++)
-		if (!(*b)->probe(fs_type, flags, dev_name, data, mnt))
-			break;
-	return *b;
+	for (b = backends; *b; b++) {
+		err = (*b)->probe(fs_type, flags, dev_name, data, mnt);
+		if (!err)
+			return 0;
+	}
+	ERROR("No suitable backend found\n");
+	return -ENODEV;
+}
+
+void squashfs_kill_super(struct super_block *sb)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+
+	msblk->backend->killsb(sb);
 }
diff --git a/fs/squashfs/backend.h b/fs/squashfs/backend.h
index ca3ffe1..02c4bcd 100644
--- a/fs/squashfs/backend.h
+++ b/fs/squashfs/backend.h
@@ -9,14 +9,13 @@ struct squashfs_backend {
 	ssize_t	(*read)(struct squashfs_sb_info *, void **, size_t);
 	int	(*probe)(struct file_system_type *, int, const char *,
 				void*, struct vfsmount *);
+	void    (*killsb)(struct super_block *);
 	void	(*kill)(struct squashfs_sb_info *);
 	loff_t  (*size)(const struct super_block *);
 	const char *(*devname)(const struct super_block *, char *);
 };
 
-/* Dummy, until the original is nuked */
-static inline int squashfs_fill_super2(struct super_block *sb, void *data,
-				int silent, int (*add_bend)(struct super_block *))
-{
-	return -1;
-}
+int squashfs_find_backend(struct file_system_type *, int,
+			const char *, void *, struct vfsmount *);
+
+void squashfs_kill_super(struct super_block *);
diff --git a/fs/squashfs/bdev.c b/fs/squashfs/bdev.c
index fcc3b83..81652e8 100644
--- a/fs/squashfs/bdev.c
+++ b/fs/squashfs/bdev.c
@@ -68,7 +68,7 @@ static int add_bdev_backend(struct super_block *sb)
 
 static int fill_bdev_super(struct super_block *sb, void *data, int silent)
 {
-	return squashfs_fill_super2(sb, data, silent, add_bdev_backend);
+	return squashfs_fill_super(sb, data, silent, add_bdev_backend);
 }
 
 static int bdev_probe(struct file_system_type *fs_type, int flags,
@@ -103,6 +103,7 @@ const struct squashfs_backend squashfs_bdev_ops = {
 	.free	= bdev_free,
 	.read	= bdev_read,
 	.probe	= bdev_probe,
+	.killsb	= kill_block_super,
 	.kill	= bdev_kill,
 	.size	= bdev_size,
 	.devname= bdev_devname
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 0e74175..24c4b9e 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -73,10 +73,10 @@ extern struct inode *squashfs_iget(struct super_block *, long long,
 				unsigned int);
 extern int squashfs_read_inode(struct inode *, long long);
 
-/* super.c
+/* super.c */
 int squashfs_fill_super(struct super_block *, void *, int,
-                        struct squashfs_backend *);
-*/
+                        int (*)(struct super_block *));
+
 /*
  * Inodes, files, backend and decompressor operations
  */
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 3550aec..710179f 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -42,6 +42,7 @@
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "backend.h"
 
 static struct file_system_type squashfs_fs_type;
 static const struct super_operations squashfs_super_ops;
@@ -73,7 +74,8 @@ static const struct squashfs_decompressor *supported_squashfs_filesystem(short
 }
 
 
-static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
+int squashfs_fill_super(struct super_block *sb, void *data, int silent,
+			int (*add_backend)(struct super_block *))
 {
 	struct squashfs_sb_info *msblk;
 	struct squashfs_super_block *sblk = NULL;
@@ -97,11 +99,15 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
 	if (sblk == NULL) {
 		ERROR("Failed to allocate squashfs_super_block\n");
-		goto failure;
+		err = -ENOMEM;
+		goto free_msblk;
 	}
 
 	msblk->devblksize = sb_min_blocksize(sb, BLOCK_SIZE);
 	msblk->devblksize_log2 = ffz(~msblk->devblksize);
+	err = add_backend(sb);
+	if (err)
+		goto free_sblk;
 
 	mutex_init(&msblk->read_data_mutex);
 	mutex_init(&msblk->meta_index_mutex);
@@ -127,7 +133,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	if (sb->s_magic != SQUASHFS_MAGIC) {
 		if (!silent)
 			ERROR("Can't find a SQUASHFS superblock on %s\n",
-						bdevname(sb->s_bdev, b));
+					msblk->backend->devname(sb, b));
 		goto failed_mount;
 	}
 
@@ -146,11 +152,10 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	if (le64_to_cpu(sblk->xattr_table_start) != SQUASHFS_INVALID_BLK)
 		ERROR("Xattrs in filesystem, these will be ignored\n");
 
-	/* Check the filesystem does not extend beyond the end of the
-	   block device */
+	/* Check the filesystem does not extend beyond the end of the device */
 	msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
 	if (msblk->bytes_used < 0 || msblk->bytes_used >
-			i_size_read(sb->s_bdev->bd_inode))
+			msblk->backend->size(sb))
 		goto failed_mount;
 
 	/* Check block size for sanity */
@@ -182,7 +187,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	msblk->inodes = le32_to_cpu(sblk->inodes);
 	flags = le16_to_cpu(sblk->flags);
 
-	TRACE("Found valid superblock on %s\n", bdevname(sb->s_bdev, b));
+	TRACE("Found valid superblock on %s\n", msblk->backend->devname(sb, b));
 	TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
 				? "un" : "");
 	TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags)
@@ -297,18 +302,16 @@ failed_mount:
 	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->inode_lookup_table); /* FIXME: squashfs_put_super has meta_index instead */
 	kfree(msblk->fragment_index);
 	kfree(msblk->id_table);
-	kfree(sb->s_fs_info);
-	sb->s_fs_info = NULL;
+	msblk->backend->kill(msblk);
+free_sblk:
 	kfree(sblk);
-	return err;
-
-failure:
-	kfree(sb->s_fs_info);
+free_msblk:
+	kfree(msblk);
 	sb->s_fs_info = NULL;
-	return -ENOMEM;
+	return err;
 }
 
 
@@ -353,7 +356,8 @@ static void squashfs_put_super(struct super_block *sb)
 		kfree(sbi->id_table);
 		kfree(sbi->fragment_index);
 		kfree(sbi->meta_index);
-		kfree(sb->s_fs_info);
+		sbi->backend->kill(sbi);
+		kfree(sbi);
 		sb->s_fs_info = NULL;
 	}
 
@@ -361,15 +365,6 @@ static void squashfs_put_super(struct super_block *sb)
 }
 
 
-static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
-				const char *dev_name, void *data,
-				struct vfsmount *mnt)
-{
-	return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
-				mnt);
-}
-
-
 static struct kmem_cache *squashfs_inode_cachep;
 
 
@@ -442,8 +437,8 @@ static void squashfs_destroy_inode(struct inode *inode)
 static struct file_system_type squashfs_fs_type = {
 	.owner = THIS_MODULE,
 	.name = "squashfs",
-	.get_sb = squashfs_get_sb,
-	.kill_sb = kill_block_super,
+	.get_sb = squashfs_find_backend,
+	.kill_sb = squashfs_kill_super,
 	.fs_flags = FS_REQUIRES_DEV
 };
 
-- 
1.5.6.5


>From 4b150ae9d6c3ae77b139f75a1466aa10e1ef1b5a Mon Sep 17 00:00:00 2001
From: Ferenc Wagner <wferi@xxxxxxx>
Date: Sun, 21 Mar 2010 02:08:58 +0100
Subject: [PATCH] squashfs: read uncompressed blocks through the backend framework

Avoid double free on the temporary decompression hack-path; this may
leak buffer heads if decompression exits early.
---
 fs/squashfs/backend.h        |    4 +-
 fs/squashfs/bdev.c           |  191 ++++++++++++++++++++++++++++++++++++++----
 fs/squashfs/block.c          |  167 ++++++++-----------------------------
 fs/squashfs/lzma_wrapper.c   |    2 +
 fs/squashfs/squashfs_fs_sb.h |    2 -
 fs/squashfs/super.c          |    5 +-
 6 files changed, 216 insertions(+), 155 deletions(-)

diff --git a/fs/squashfs/backend.h b/fs/squashfs/backend.h
index 02c4bcd..308fe6d 100644
--- a/fs/squashfs/backend.h
+++ b/fs/squashfs/backend.h
@@ -4,9 +4,9 @@
 #include "squashfs_fs_sb.h"
 
 struct squashfs_backend {
-	void	*(*init)(struct squashfs_sb_info *, u64, size_t);
+	int	(*init)(struct super_block *, u64, int, int, u64 *, int *);
 	void	(*free)(struct squashfs_sb_info *);
-	ssize_t	(*read)(struct squashfs_sb_info *, void **, size_t);
+	int	(*read)(struct super_block *, void *, int);
 	int	(*probe)(struct file_system_type *, int, const char *,
 				void*, struct vfsmount *);
 	void    (*killsb)(struct super_block *);
diff --git a/fs/squashfs/bdev.c b/fs/squashfs/bdev.c
index 81652e8..59f79ee 100644
--- a/fs/squashfs/bdev.c
+++ b/fs/squashfs/bdev.c
@@ -8,46 +8,199 @@
 struct squashfs_bdev {
 	int devblksize;			/* FIXME: == s->s_blocksize(_bits)? */
 	unsigned short devblksize_log2;
-	size_t bytes_left;
+	int length;
+	int bytes_read;
 	struct buffer_head **bh;
 	int bh_index;			/* number of consumed buffer_heads */
 	int offset;			/* offset of next byte in buffer_head */
+	int b;				/* total number of buffer heads */
 };
 
-/* A backend is initialized for each SquashFS block read operation,
- * making further sequential reads possible from the block.
+/*
+ * Read the metadata block length, this is stored in the first two
+ * bytes of the metadata block.
  */
-static void *bdev_init(struct squashfs_sb_info *msblk, u64 index, size_t length)
+static struct buffer_head *get_block_length(struct super_block *sb,
+			u64 *cur_index, int *offset, int *length)
 {
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
 	struct squashfs_bdev *bdev = msblk->backend_data;
 	struct buffer_head *bh;
 
+	TRACE("get_block_length: cur_index=0x%llx, offset=%d\n",
+		(unsigned long long)*cur_index, *offset);
+	bh = sb_bread(sb, *cur_index);
+	if (bh == NULL)
+		return NULL;
+
+	if (bdev->devblksize - *offset == 1) {
+		*length = (unsigned char) bh->b_data[*offset];
+		put_bh(bh);
+		bh = sb_bread(sb, ++(*cur_index));
+		if (bh == NULL)
+			return NULL;
+		*length |= (unsigned char) bh->b_data[0] << 8;
+		*offset = 1;
+	} else {
+		*length = (unsigned char) bh->b_data[*offset] |
+			(unsigned char) bh->b_data[*offset + 1] << 8;
+		*offset += 2;
+	}
+
+	TRACE("get_block_length: length=%d, offset=%d\n", *length, *offset);
+	return bh;
+}
+
+/*
+ * Read a metadata block or datablock into memory.  Length is non-zero
+ * if a datablock is being read (the size is stored elsewhere in the
+ * filesystem), otherwise the length is obtained from the first two bytes of
+ * the metadata block.  A bit in the length field indicates if the block
+ * is stored uncompressed in the filesystem (usually because compression
+ * generated a larger block - this does occasionally happen with zlib).
+ */
+static int bdev_init(struct super_block *sb, u64 index, int length,
+			int srclength, u64 *next_index, int *compressed)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	struct squashfs_bdev *bdev = msblk->backend_data;
+	struct buffer_head **bh;
+	u64 cur_index;			/* sector of next byte */
+	int bytes;			/* number of bytes handled */
+	int b;				/* for counting buffer heads */
+
+	TRACE("Entering bdev_init: index=0x%llx, length=0x%x, srclength=%d\n",
+		index, length, srclength);
 	bh = kcalloc((max(msblk->block_size, METADATA_SIZE) >> bdev->devblksize_log2) + 1,
 			sizeof(*bh), GFP_KERNEL);
-	if (!bh)
-		goto nomem;
+	if (bh == NULL)
+		return -ENOMEM;
+
+	bdev->offset = index & ((1 << bdev->devblksize_log2) - 1);
+	cur_index = index >> bdev->devblksize_log2;
+	if (length) { /* FIXME: this logic and the comment above should be pulled out! */
+		/*
+		 * Datablock.
+		 */
+		bytes = -bdev->offset;
+		*compressed = SQUASHFS_COMPRESSED_BLOCK(length);
+		length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
+		if (next_index)
+			*next_index = index + length;
+
+		TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
+			index, *compressed ? "" : "un", length, srclength);
 
-	/* different preread for data blocks and metadata blocks */
+		if (length < 0 || length > srclength ||
+				(index + length) > msblk->bytes_used)
+			goto read_failure;
 
+		for (b = 0; bytes < length; b++, cur_index++) {
+			bh[b] = sb_getblk(sb, cur_index);
+			if (bh[b] == NULL)
+				goto block_release;
+			bytes += bdev->devblksize;
+		}
+		ll_rw_block(READ, b, bh);
+	} else {
+		/*
+		 * Metadata block.
+		 */
+		if ((index + 2) > msblk->bytes_used)
+			goto read_failure;
+
+		bh[0] = get_block_length(sb, &cur_index, &bdev->offset, &length);
+		if (bh[0] == NULL)
+			goto read_failure;
+		b = 1;
+
+		bytes = bdev->devblksize - bdev->offset;
+		*compressed = SQUASHFS_COMPRESSED(length);
+		length = SQUASHFS_COMPRESSED_SIZE(length);
+		if (next_index)
+			*next_index = index + length + 2;
+
+		TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
+				*compressed ? "" : "un", length);
+
+		if (length < 0 || length > srclength ||
+					(index + length) > msblk->bytes_used)
+			goto block_release;
+
+		for (; bytes < length; b++) {
+			bh[b] = sb_getblk(sb, ++cur_index);
+			if (bh[b] == NULL)
+				goto block_release;
+			bytes += bdev->devblksize;
+		}
+		ll_rw_block(READ, b - 1, bh + 1);
+	}
+
+	bdev->bh = bh;
+	bdev->length = length;
+	bdev->bytes_read = 0;
+	bdev->b = b;
 	bdev->bh_index = 0;
-	bdev->bytes_left = length;
-	return bdev;
+	TRACE("bdev_init: allocated %d buffer heads at %p, returning length=%d",
+	      b, bh, length);
+	return length;
 
-nomem:
-	ERROR("failed to allocate buffer_heads\n");
-	return NULL;
+block_release:
+	while (b--)
+		put_bh(bh[b]);
+read_failure:
+	ERROR("bdev_init failed to read block 0x%llx\n",
+					(unsigned long long) index);
+	kfree(bh);
+	return -EIO;
 }
 
 static void bdev_free(struct squashfs_sb_info *msblk)
 {
 	struct squashfs_bdev *bdev = msblk->backend_data;
+
+	TRACE("bdev_free: bh=%p, bh_index=%d, b=%d\n",
+		bdev->bh, bdev->bh_index, bdev->b);
+	while (bdev->bh_index < bdev->b)
+		put_bh(bdev->bh[bdev->bh_index++]);
 	kfree(bdev->bh);
-	bdev->bh = 0; /* FIXME: to make bdev_kill universal (see there) */
+	bdev->bh = NULL;
 }
 
-static ssize_t bdev_read(struct squashfs_sb_info *msblk, void **buf, size_t len)
+static int bdev_read(struct super_block *sb, void *buf, int len)
 {
-	return -ENOSYS;
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	struct squashfs_bdev *bdev = msblk->backend_data;
+	struct buffer_head *bh = bdev->bh[bdev->bh_index];
+	int in_block = bdev->devblksize - bdev->offset;
+
+	TRACE("bdev_read: buf=%p, len=%d, length=%d, bytes_read=%d, in_block=%d, bh_index=%d, offset=%d\n",
+	      buf, len, bdev->length, bdev->bytes_read, in_block, bdev->bh_index, bdev->offset);
+	if (bdev->bytes_read == bdev->length) /* EOF */
+		return 0;
+	if (bdev->bytes_read + len > bdev->length)
+		len = bdev->length - bdev->bytes_read;
+	if (bdev->bytes_read == 0 || bdev->offset == 0) {
+		TRACE("bdev_read: wait_on_buffer %d\n", bdev->bh_index);
+		wait_on_buffer(bh);
+		if (!buffer_uptodate(bh))
+			return -EIO;
+	}
+	if (len < in_block) {
+		TRACE("bdev_read: copying %d bytes", len);
+		memcpy(buf, bh->b_data + bdev->offset, len);
+		bdev->offset += len;
+		bdev->bytes_read += len;
+		return len;
+	} else {
+		TRACE("bdev_read: copying %d bytes", in_block);
+		memcpy(buf, bh->b_data + bdev->offset, in_block);
+		put_bh(bh);
+		bdev->bh_index++;
+		bdev->offset = 0;
+		bdev->bytes_read += in_block;
+		return in_block;
+	}
 }
 
 static int add_bdev_backend(struct super_block *sb)
@@ -55,6 +208,7 @@ static int add_bdev_backend(struct super_block *sb)
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
 	struct squashfs_bdev *bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
 
+	TRACE("add_bdev_backend\n");
 	if (!bdev)
 		return -ENOMEM;
 
@@ -82,9 +236,12 @@ static int bdev_probe(struct file_system_type *fs_type, int flags,
 static void bdev_kill(struct squashfs_sb_info *msblk)
 {
 	struct squashfs_bdev *bdev = msblk->backend_data;
+
+	TRACE("bdev_kill: bdev=%p\n", bdev);
 	if (bdev) {
-		kfree(bdev->bh); /* FIXME: can this be nonzero? cf. bdev_free */
+		bdev_free(msblk);
 		kfree(bdev);
+		bdev = NULL;
 	}
 }
 
@@ -104,7 +261,7 @@ const struct squashfs_backend squashfs_bdev_ops = {
 	.read	= bdev_read,
 	.probe	= bdev_probe,
 	.killsb	= kill_block_super,
-	.kill	= bdev_kill,
+	.kill	= bdev_kill, /* FIXME: is this needed at all? */
 	.size	= bdev_size,
 	.devname= bdev_devname
 };
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index 6f9914d..8787aac 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -37,37 +37,19 @@
 #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.
- */
-static struct buffer_head *get_block_length(struct super_block *sb,
-			u64 *cur_index, int *offset, int *length)
-{
-	struct squashfs_sb_info *msblk = sb->s_fs_info;
-	struct buffer_head *bh;
-
-	bh = sb_bread(sb, *cur_index);
-	if (bh == NULL)
-		return NULL;
-
-	if (msblk->devblksize - *offset == 1) {
-		*length = (unsigned char) bh->b_data[*offset];
-		put_bh(bh);
-		bh = sb_bread(sb, ++(*cur_index));
-		if (bh == NULL)
-			return NULL;
-		*length |= (unsigned char) bh->b_data[0] << 8;
-		*offset = 1;
-	} else {
-		*length = (unsigned char) bh->b_data[*offset] |
-			(unsigned char) bh->b_data[*offset + 1] << 8;
-		*offset += 2;
-	}
-
-	return bh;
-}
-
+#include "backend.h"
+
+/* FIXME: here for a temporary cheat only */
+struct squashfs_bdev {
+	int devblksize;			/* FIXME: == s->s_blocksize(_bits)? */
+	unsigned short devblksize_log2;
+	int length;
+	int bytes_read;
+	struct buffer_head **bh;
+	int bh_index;			/* number of consumed buffer_heads */
+	int offset;			/* offset of next byte in buffer_head */
+	int b;				/* total number of buffer heads */
+};
 
 /*
  * Read and decompress a metadata block or datablock.  Length is non-zero
@@ -80,124 +62,47 @@ static struct buffer_head *get_block_length(struct super_block *sb,
 int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
 			int length, u64 *next_index, int srclength, int pages)
 {
+	int compressed;
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
-	struct buffer_head **bh;
-	int offset = index & ((1 << msblk->devblksize_log2) - 1);
-	u64 cur_index = index >> msblk->devblksize_log2;
-	int bytes, compressed, b = 0, k = 0, page = 0, avail;
-
-
-	bh = kcalloc((msblk->block_size >> msblk->devblksize_log2) + 1,
-				sizeof(*bh), GFP_KERNEL);
-	if (bh == NULL)
-		return -ENOMEM;
-
-	if (length) {
-		/*
-		 * Datablock.
-		 */
-		bytes = -offset;
-		compressed = SQUASHFS_COMPRESSED_BLOCK(length);
-		length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
-		if (next_index)
-			*next_index = index + length;
-
-		TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
-			index, compressed ? "" : "un", length, srclength);
 
-		if (length < 0 || length > srclength ||
-				(index + length) > msblk->bytes_used)
-			goto read_failure;
-
-		for (b = 0; bytes < length; b++, cur_index++) {
-			bh[b] = sb_getblk(sb, cur_index);
-			if (bh[b] == NULL)
-				goto block_release;
-			bytes += msblk->devblksize;
-		}
-		ll_rw_block(READ, b, bh);
-	} else {
-		/*
-		 * Metadata block.
-		 */
-		if ((index + 2) > msblk->bytes_used)
-			goto read_failure;
-
-		bh[0] = get_block_length(sb, &cur_index, &offset, &length);
-		if (bh[0] == NULL)
-			goto read_failure;
-		b = 1;
-
-		bytes = msblk->devblksize - offset;
-		compressed = SQUASHFS_COMPRESSED(length);
-		length = SQUASHFS_COMPRESSED_SIZE(length);
-		if (next_index)
-			*next_index = index + length + 2;
-
-		TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
-				compressed ? "" : "un", length);
-
-		if (length < 0 || length > srclength ||
-					(index + length) > msblk->bytes_used)
-			goto block_release;
-
-		for (; bytes < length; b++) {
-			bh[b] = sb_getblk(sb, ++cur_index);
-			if (bh[b] == NULL)
-				goto block_release;
-			bytes += msblk->devblksize;
-		}
-		ll_rw_block(READ, b - 1, bh + 1);
-	}
+	length = msblk->backend->init(sb, index, length, srclength,
+					next_index, &compressed);
+	if (length < 0)
+		return length;
 
 	if (compressed) {
-		length = squashfs_decompress(msblk, buffer, bh, b, offset,
-			length, srclength, pages);
+		struct squashfs_bdev *bdev = msblk->backend_data;
+		length = squashfs_decompress(msblk, buffer, bdev->bh, bdev->b,
+				bdev->offset, length, srclength, pages);
+		bdev->bh_index = bdev->b; /* temporary hack to avoid double free */
 		if (length < 0)
 			goto read_failure;
 	} else {
 		/*
 		 * Block is uncompressed.
 		 */
-		int i, in, pg_offset = 0;
-
-		for (i = 0; i < b; i++) {
-			wait_on_buffer(bh[i]);
-			if (!buffer_uptodate(bh[i]))
-				goto block_release;
-		}
-
-		for (bytes = length; k < b; k++) {
-			in = min(bytes, msblk->devblksize - offset);
-			bytes -= in;
-			while (in) {
-				if (pg_offset == PAGE_CACHE_SIZE) {
-					page++;
-					pg_offset = 0;
-				}
-				avail = min_t(int, in, PAGE_CACHE_SIZE -
-						pg_offset);
-				memcpy(buffer[page] + pg_offset,
-						bh[k]->b_data + offset, avail);
-				in -= avail;
-				pg_offset += avail;
-				offset += avail;
+		int read, page = 0, pg_offset = 0;
+
+		while ((read = msblk->backend->read(sb, buffer[page] + pg_offset,
+						PAGE_CACHE_SIZE - pg_offset))) {
+			if (read < 0)
+				goto read_failure;
+			TRACE("copied %d bytes\n", read);
+			pg_offset += read;
+			if (pg_offset == PAGE_CACHE_SIZE) {
+				page++;
+				pg_offset = 0;
 			}
-			offset = 0;
-			put_bh(bh[k]);
 		}
 	}
 
-	kfree(bh);
+	msblk->backend->free(msblk);
+	TRACE("squashfs_read_data: returning length=%d\n", length);
 	return length;
 
-block_release:
-	for (; k < b; k++)
-		put_bh(bh[k]);
-
 read_failure:
 	ERROR("squashfs_read_data failed to read block 0x%llx\n",
 					(unsigned long long) index);
-	kfree(bh);
+	msblk->backend->free(msblk);
 	return -EIO;
 }
diff --git a/fs/squashfs/lzma_wrapper.c b/fs/squashfs/lzma_wrapper.c
index 9fa617d..439798f 100644
--- a/fs/squashfs/lzma_wrapper.c
+++ b/fs/squashfs/lzma_wrapper.c
@@ -94,6 +94,8 @@ static int lzma_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 	void *buff = stream->input;
 	int avail, i, bytes = length, res;
 
+	TRACE("lzma_uncompress: bh=%p, b=%d, offset=%d, length=%d, srclength=%d,"
+		" pages=%d\n", bh, b, offset, length, srclength, pages);
 	mutex_lock(&lzma_mutex);
 
 	for (i = 0; i < b; i++) {
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index 5662d9b..87958fc 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -55,8 +55,6 @@ struct squashfs_sb_info {
 	const struct squashfs_decompressor	*decompressor;
 	const struct squashfs_backend		*backend;
 	void					*backend_data;
-	int					devblksize;
-	int					devblksize_log2;
 	struct squashfs_cache			*block_cache;
 	struct squashfs_cache			*fragment_cache;
 	struct squashfs_cache			*read_page;
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index 710179f..cb561bf 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -87,7 +87,7 @@ int squashfs_fill_super(struct super_block *sb, void *data, int silent,
 	u64 lookup_table_start;
 	int err;
 
-	TRACE("Entered squashfs_fill_superblock\n");
+	TRACE("Entered squashfs_fill_superblock, silent=%d\n", silent);
 
 	sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL);
 	if (sb->s_fs_info == NULL) {
@@ -103,8 +103,6 @@ int squashfs_fill_super(struct super_block *sb, void *data, int silent,
 		goto free_msblk;
 	}
 
-	msblk->devblksize = sb_min_blocksize(sb, BLOCK_SIZE);
-	msblk->devblksize_log2 = ffz(~msblk->devblksize);
 	err = add_backend(sb);
 	if (err)
 		goto free_sblk;
@@ -305,6 +303,7 @@ failed_mount:
 	kfree(msblk->inode_lookup_table); /* FIXME: squashfs_put_super has meta_index instead */
 	kfree(msblk->fragment_index);
 	kfree(msblk->id_table);
+	TRACE("squasfs_fill_super: killing backend, err=%d\n", err);
 	msblk->backend->kill(msblk);
 free_sblk:
 	kfree(sblk);
-- 
1.5.6.5


>From a139798e093314d4fd6fe121a84ae6b30a33af43 Mon Sep 17 00:00:00 2001
From: Ferenc Wagner <wferi@xxxxxxx>
Date: Sun, 21 Mar 2010 22:38:00 +0100
Subject: [PATCH] squashfs: move the decompressors to the backend framework

---
 fs/squashfs/block.c        |   17 +----------------
 fs/squashfs/decompressor.h |   12 +++++-------
 fs/squashfs/lzma_wrapper.c |   27 +++++++++------------------
 fs/squashfs/zlib_wrapper.c |   44 ++++++++++++++++++--------------------------
 4 files changed, 33 insertions(+), 67 deletions(-)

diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index 8787aac..1c85063 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -39,18 +39,6 @@
 #include "decompressor.h"
 #include "backend.h"
 
-/* FIXME: here for a temporary cheat only */
-struct squashfs_bdev {
-	int devblksize;			/* FIXME: == s->s_blocksize(_bits)? */
-	unsigned short devblksize_log2;
-	int length;
-	int bytes_read;
-	struct buffer_head **bh;
-	int bh_index;			/* number of consumed buffer_heads */
-	int offset;			/* offset of next byte in buffer_head */
-	int b;				/* total number of buffer heads */
-};
-
 /*
  * Read and decompress a metadata block or datablock.  Length is non-zero
  * if a datablock is being read (the size is stored elsewhere in the
@@ -71,10 +59,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
 		return length;
 
 	if (compressed) {
-		struct squashfs_bdev *bdev = msblk->backend_data;
-		length = squashfs_decompress(msblk, buffer, bdev->bh, bdev->b,
-				bdev->offset, length, srclength, pages);
-		bdev->bh_index = bdev->b; /* temporary hack to avoid double free */
+		length = squashfs_decompress(sb, buffer, length, pages);
 		if (length < 0)
 			goto read_failure;
 	} else {
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
index 7425f80..06b4233 100644
--- a/fs/squashfs/decompressor.h
+++ b/fs/squashfs/decompressor.h
@@ -26,8 +26,7 @@
 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	(*decompress)(struct super_block *, void **, int, int);
 	int	id;
 	char	*name;
 	int	supported;
@@ -45,11 +44,10 @@ static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
 		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)
+static inline int squashfs_decompress(struct super_block *sb,
+	void **buffer, int length, int pages)
 {
-	return msblk->decompressor->decompress(msblk, buffer, bh, b, offset,
-		length, srclength, pages);
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	return msblk->decompressor->decompress(sb, buffer, length, pages);
 }
 #endif
diff --git a/fs/squashfs/lzma_wrapper.c b/fs/squashfs/lzma_wrapper.c
index 439798f..da571ce 100644
--- a/fs/squashfs/lzma_wrapper.c
+++ b/fs/squashfs/lzma_wrapper.c
@@ -32,6 +32,7 @@
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "backend.h"
 
 struct squashfs_lzma {
 	void	*input;
@@ -86,29 +87,23 @@ static void lzma_free(void *strm)
 }
 
 
-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)
+static int lzma_uncompress(struct super_block *sb, void **buffer,
+			   int length, int pages)
 {
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
 	struct squashfs_lzma *stream = msblk->stream;
 	void *buff = stream->input;
 	int avail, i, bytes = length, res;
 
-	TRACE("lzma_uncompress: bh=%p, b=%d, offset=%d, length=%d, srclength=%d,"
-		" pages=%d\n", bh, b, offset, length, srclength, pages);
+	TRACE("lzma_uncompress: length=%d, pages=%d\n", length, pages);
 	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);
+	while ((avail = msblk->backend->read(sb, buff, length))) {
+		if (avail < 0)
+			goto failed;
+		TRACE("read %d bytes\n", avail);
 		buff += avail;
 		bytes -= avail;
-		offset = 0;
-		put_bh(bh[i]);
 	}
 
 	lzma_error = 0;
@@ -131,10 +126,6 @@ static int lzma_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 	mutex_unlock(&lzma_mutex);
 	return res;
 
-block_release:
-	for (; i < b; i++)
-		put_bh(bh[i]);
-
 failed:
 	mutex_unlock(&lzma_mutex);
 
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
index 4dd70e0..70c7571 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -31,6 +31,7 @@
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "backend.h"
 
 static void *zlib_init(struct squashfs_sb_info *dummy)
 {
@@ -61,13 +62,18 @@ static void zlib_free(void *strm)
 }
 
 
-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)
+static int zlib_uncompress(struct super_block *sb, void **buffer,
+	int length, int pages)
 {
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
 	int zlib_err = 0, zlib_init = 0;
-	int avail, bytes, k = 0, page = 0;
+	int bytes, page = 0;
 	z_stream *stream = msblk->stream;
+	/* Copying is required by the backend interface for now. */
+	void *input = kmalloc (PAGE_CACHE_SIZE, GFP_KERNEL);
+
+	if (!input)
+		return -ENOMEM;
 
 	mutex_lock(&msblk->read_data_mutex);
 
@@ -76,22 +82,13 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 
 	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]))
+		if (stream->avail_in == 0) {
+			stream->avail_in = msblk->backend->read(sb, input, PAGE_CACHE_SIZE);
+			if (!stream->avail_in) {
+				ERROR("unexpected end of input\n");
 				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;
+			stream->next_in = input;
 		}
 
 		if (stream->avail_out == 0 && page < pages) {
@@ -103,8 +100,7 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 			zlib_err = zlib_inflateInit(stream);
 			if (zlib_err != Z_OK) {
 				ERROR("zlib_inflateInit returned unexpected "
-					"result 0x%x, srclength %d\n",
-					zlib_err, srclength);
+					"result 0x%x\n", zlib_err);
 				goto release_mutex;
 			}
 			zlib_init = 1;
@@ -112,8 +108,6 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 
 		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) {
@@ -128,14 +122,12 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer,
 	}
 
 	mutex_unlock(&msblk->read_data_mutex);
+	kfree(input);
 	return stream->total_out;
 
 release_mutex:
 	mutex_unlock(&msblk->read_data_mutex);
-
-	for (; k < b; k++)
-		put_bh(bh[k]);
-
+	kfree(input);
 	return -EIO;
 }
 
-- 
1.5.6.5


>From d04e9006614dceb61f26ed462d4e0682de0b31af Mon Sep 17 00:00:00 2001
From: Ferenc Wagner <wferi@xxxxxxx>
Date: Tue, 23 Mar 2010 02:21:27 +0100
Subject: [PATCH] squashfs: add mtd backend

---
 fs/squashfs/Kconfig    |    2 +-
 fs/squashfs/Makefile   |    1 +
 fs/squashfs/backend.c  |    3 +
 fs/squashfs/mtd.c      |  167 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/squashfs/squashfs.h |    3 +
 5 files changed, 175 insertions(+), 1 deletions(-)
 create mode 100644 fs/squashfs/mtd.c

diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
index 7ec5d7e..6849e70 100644
--- a/fs/squashfs/Kconfig
+++ b/fs/squashfs/Kconfig
@@ -1,6 +1,6 @@
 config SQUASHFS
 	tristate "SquashFS 4.0 - Squashed file system support"
-	depends on BLOCK
+	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 0f02891..b6dd60e 100644
--- a/fs/squashfs/Makefile
+++ b/fs/squashfs/Makefile
@@ -7,3 +7,4 @@ 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 zlib_wrapper.o decompressor.o backend.o
 squashfs-$(CONFIG_SQUASHFS_LZMA) += lzma_wrapper.o
 squashfs-$(CONFIG_BLOCK) += bdev.o
+squashfs-$(CONFIG_MTD) += mtd.o
diff --git a/fs/squashfs/backend.c b/fs/squashfs/backend.c
index 49fa6fa..56037e3 100644
--- a/fs/squashfs/backend.c
+++ b/fs/squashfs/backend.c
@@ -8,6 +8,9 @@ const struct squashfs_backend *backends[] = {
 #ifdef CONFIG_BLOCK
 	&squashfs_bdev_ops,
 #endif
+#ifdef CONFIG_MTD
+	&squashfs_mtd_ops,
+#endif
 	NULL
 };
 
diff --git a/fs/squashfs/mtd.c b/fs/squashfs/mtd.c
new file mode 100644
index 0000000..c9ba361
--- /dev/null
+++ b/fs/squashfs/mtd.c
@@ -0,0 +1,167 @@
+#include <linux/fs.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/super.h>
+
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+#include "backend.h"
+
+struct squashfs_mtd {
+	u64 index;
+	int length;
+	int bytes_read;
+};
+
+static int checked_mtd_read(struct super_block *sb, u64 index, int length,
+			    void *buf)
+{
+	struct mtd_info *mi = sb->s_mtd;
+	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) {
+			ERROR("checked_mtd_read(index=0x%llx, length=%d): %d\n",
+			      index, length, ret);
+			return ret;
+		} else
+			WARNING("checked_mtd_read(index=0x%llx, length=%d): "
+				"recoverable error %d\n", index, length, 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 mtd_init(struct super_block *sb, u64 index, int length,
+		    int srclength, u64 *next_index, int *compressed)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	struct squashfs_mtd *mtd = msblk->backend_data;
+
+	TRACE("Entering mtd_init: index=0x%llx, length=0x%x, srclength=%d\n",
+		index, length, srclength);
+
+	if (length) { /* datablock */
+		*compressed = SQUASHFS_COMPRESSED_BLOCK(length);
+		length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
+		if (next_index)
+			*next_index = index + 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(sb, index, 2, &metalen))
+			goto read_failure;
+		length = le16_to_cpu(metalen);
+		*compressed = SQUASHFS_COMPRESSED(length);
+		length = SQUASHFS_COMPRESSED_SIZE(length);
+		if (next_index)
+			*next_index = index + length + 2;
+		TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
+				*compressed ? "" : "un", length);
+		index += 2;
+	}
+	if (length < 0 || length > srclength ||
+			(index + length) > msblk->bytes_used)
+		goto read_failure;
+
+	mtd->index = index;
+	mtd->length = length;
+	mtd->bytes_read = 0;
+	return length;
+
+read_failure:
+	ERROR("mtd_init failed to read block 0x%llx\n",
+					(unsigned long long) index);
+	return -EIO;
+}
+
+static void mtd_free(struct squashfs_sb_info *msblk)
+{
+	TRACE("mtd_free: no op\n");
+}
+
+static int mtd_read(struct super_block *sb, void *buf, int len)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	struct squashfs_mtd *mtd = msblk->backend_data;
+	int err;
+
+	TRACE("mtd_read: buf=%p, len=%d, length=%d, bytes_read=%d\n",
+	      buf, len, mtd->length, mtd->bytes_read);
+
+	if (mtd->bytes_read == mtd->length) /* EOF */
+		return 0;
+	if (mtd->bytes_read + len > mtd->length)
+		len = mtd->length - mtd->bytes_read;
+	TRACE("mtd_read: copying %d bytes", len);
+	err = checked_mtd_read(sb, mtd->index + mtd->bytes_read, len, buf);
+	if (err)
+		return err;
+	mtd->bytes_read += len;
+	return len;
+}
+
+static int add_mtd_backend(struct super_block *sb)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	struct squashfs_mtd *mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
+
+	TRACE("add_mtd_backend\n");
+	if (!mtd)
+		return -ENOMEM;
+
+	msblk->backend = &squashfs_mtd_ops;
+	msblk->backend_data = mtd;
+	return 0;
+}
+
+static int fill_mtd_super(struct super_block *sb, void *data, int silent)
+{
+	return squashfs_fill_super(sb, data, silent, add_mtd_backend);
+}
+
+static int mtd_probe(struct file_system_type *fs_type, int flags,
+			const char *dev_name, void *data,
+			struct vfsmount *mnt)
+{
+	return get_sb_mtd(fs_type, flags, dev_name, data,
+				fill_mtd_super, mnt);
+}
+
+static void mtd_kill(struct squashfs_sb_info *msblk)
+{
+	TRACE("mtd_kill: mtd=%p\n", msblk->backend_data);
+	mtd_free(msblk);
+	kfree(msblk->backend_data);
+}
+
+static loff_t mtd_size(const struct super_block *sb)
+{
+	return sb->s_mtd->size;
+}
+
+static const char *mtd_devname(const struct super_block *sb, char *buffer)
+{
+	snprintf(buffer, BDEVNAME_SIZE, "MTD%d", sb->s_mtd->index);
+	return buffer;
+}
+
+const struct squashfs_backend squashfs_mtd_ops = {
+	.init	= mtd_init,
+	.free	= mtd_free,
+	.read	= mtd_read,
+	.probe	= mtd_probe,
+	.killsb = kill_mtd_super,
+	.kill	= mtd_kill,
+	.size	= mtd_size,
+	.devname= mtd_devname
+};
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 24c4b9e..fc3a57a 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -104,3 +104,6 @@ extern const struct squashfs_decompressor squashfs_lzma_comp_ops;
 
 /* bdev.c */
 extern const struct squashfs_backend squashfs_bdev_ops;
+
+/* mtd.c */
+extern const struct squashfs_backend squashfs_mtd_ops;
-- 
1.5.6.5


[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