Browse Source

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-linus

* git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-linus:
  Squashfs: Use vmalloc rather than kmalloc for zlib workspace
  Squashfs: handle corruption of directory structure
  Squashfs: wrap squashfs_mount() definition
  Squashfs: xz_wrapper doesn't need to include squashfs_fs_i.h anymore
  Squashfs: Update documentation to include compression options
  Squashfs: Update Kconfig help text to include xz compression
  Squashfs: add compression options support to xz decompressor
  Squashfs: extend decompressor framework to handle compression options
Linus Torvalds 14 years ago
parent
commit
5818fcc8bd

+ 19 - 9
Documentation/filesystems/squashfs.txt

@@ -59,12 +59,15 @@ obtained from this site also.
 3. SQUASHFS FILESYSTEM DESIGN
 3. SQUASHFS FILESYSTEM DESIGN
 -----------------------------
 -----------------------------
 
 
-A squashfs filesystem consists of a maximum of eight parts, packed together on a byte
-alignment:
+A squashfs filesystem consists of a maximum of nine parts, packed together on a
+byte alignment:
 
 
 	 ---------------
 	 ---------------
 	|  superblock 	|
 	|  superblock 	|
 	|---------------|
 	|---------------|
+	|  compression  |
+	|    options    |
+	|---------------|
 	|  datablocks   |
 	|  datablocks   |
 	|  & fragments  |
 	|  & fragments  |
 	|---------------|
 	|---------------|
@@ -91,7 +94,14 @@ the source directory, and checked for duplicates.  Once all file data has been
 written the completed inode, directory, fragment, export and uid/gid lookup
 written the completed inode, directory, fragment, export and uid/gid lookup
 tables are written.
 tables are written.
 
 
-3.1 Inodes
+3.1 Compression options
+-----------------------
+
+Compressors can optionally support compression specific options (e.g.
+dictionary size).  If non-default compression options have been used, then
+these are stored here.
+
+3.2 Inodes
 ----------
 ----------
 
 
 Metadata (inodes and directories) are compressed in 8Kbyte blocks.  Each
 Metadata (inodes and directories) are compressed in 8Kbyte blocks.  Each
@@ -114,7 +124,7 @@ directory inode are defined: inodes optimised for frequently occurring
 regular files and directories, and extended types where extra
 regular files and directories, and extended types where extra
 information has to be stored.
 information has to be stored.
 
 
-3.2 Directories
+3.3 Directories
 ---------------
 ---------------
 
 
 Like inodes, directories are packed into compressed metadata blocks, stored
 Like inodes, directories are packed into compressed metadata blocks, stored
@@ -144,7 +154,7 @@ decompressed to do a lookup irrespective of the length of the directory.
 This scheme has the advantage that it doesn't require extra memory overhead
 This scheme has the advantage that it doesn't require extra memory overhead
 and doesn't require much extra storage on disk.
 and doesn't require much extra storage on disk.
 
 
-3.3 File data
+3.4 File data
 -------------
 -------------
 
 
 Regular files consist of a sequence of contiguous compressed blocks, and/or a
 Regular files consist of a sequence of contiguous compressed blocks, and/or a
@@ -163,7 +173,7 @@ Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
 The index cache is designed to be memory efficient, and by default uses
 The index cache is designed to be memory efficient, and by default uses
 16 KiB.
 16 KiB.
 
 
-3.4 Fragment lookup table
+3.5 Fragment lookup table
 -------------------------
 -------------------------
 
 
 Regular files can contain a fragment index which is mapped to a fragment
 Regular files can contain a fragment index which is mapped to a fragment
@@ -173,7 +183,7 @@ A second index table is used to locate these.  This second index table for
 speed of access (and because it is small) is read at mount time and cached
 speed of access (and because it is small) is read at mount time and cached
 in memory.
 in memory.
 
 
-3.5 Uid/gid lookup table
+3.6 Uid/gid lookup table
 ------------------------
 ------------------------
 
 
 For space efficiency regular files store uid and gid indexes, which are
 For space efficiency regular files store uid and gid indexes, which are
@@ -182,7 +192,7 @@ stored compressed into metadata blocks.  A second index table is used to
 locate these.  This second index table for speed of access (and because it
 locate these.  This second index table for speed of access (and because it
 is small) is read at mount time and cached in memory.
 is small) is read at mount time and cached in memory.
 
 
-3.6 Export table
+3.7 Export table
 ----------------
 ----------------
 
 
 To enable Squashfs filesystems to be exportable (via NFS etc.) filesystems
 To enable Squashfs filesystems to be exportable (via NFS etc.) filesystems
@@ -196,7 +206,7 @@ This table is stored compressed into metadata blocks.  A second index table is
 used to locate these.  This second index table for speed of access (and because
 used to locate these.  This second index table for speed of access (and because
 it is small) is read at mount time and cached in memory.
 it is small) is read at mount time and cached in memory.
 
 
-3.7 Xattr table
+3.8 Xattr table
 ---------------
 ---------------
 
 
 The xattr table contains extended attributes for each inode.  The xattrs
 The xattr table contains extended attributes for each inode.  The xattrs

+ 6 - 6
fs/squashfs/Kconfig

@@ -5,12 +5,12 @@ config SQUASHFS
 	help
 	help
 	  Saying Y here includes support for SquashFS 4.0 (a Compressed
 	  Saying Y here includes support for SquashFS 4.0 (a Compressed
 	  Read-Only File System).  Squashfs is a highly compressed read-only
 	  Read-Only File System).  Squashfs is a highly compressed read-only
-	  filesystem for Linux.  It uses zlib/lzo compression to compress both
-	  files, inodes and directories.  Inodes in the system are very small
-	  and all blocks are packed to minimise data overhead. Block sizes
-	  greater than 4K are supported up to a maximum of 1 Mbytes (default
-	  block size 128K).  SquashFS 4.0 supports 64 bit filesystems and files
-	  (larger than 4GB), full uid/gid information, hard links and
+	  filesystem for Linux.  It uses zlib, lzo or xz compression to
+	  compress both files, inodes and directories.  Inodes in the system
+	  are very small and all blocks are packed to minimise data overhead.
+	  Block sizes greater than 4K are supported up to a maximum of 1 Mbytes
+	  (default block size 128K).  SquashFS 4.0 supports 64 bit filesystems
+	  and files (larger than 4GB), full uid/gid information, hard links and
 	  timestamps.
 	  timestamps.
 
 
 	  Squashfs is intended for general read-only filesystem use, for
 	  Squashfs is intended for general read-only filesystem use, for

+ 34 - 0
fs/squashfs/decompressor.c

@@ -23,6 +23,7 @@
 
 
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
+#include <linux/slab.h>
 #include <linux/buffer_head.h>
 #include <linux/buffer_head.h>
 
 
 #include "squashfs_fs.h"
 #include "squashfs_fs.h"
@@ -74,3 +75,36 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
 
 
 	return decompressor[i];
 	return decompressor[i];
 }
 }
+
+
+void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	void *strm, *buffer = NULL;
+	int length = 0;
+
+	/*
+	 * Read decompressor specific options from file system if present
+	 */
+	if (SQUASHFS_COMP_OPTS(flags)) {
+		buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
+		if (buffer == NULL)
+			return ERR_PTR(-ENOMEM);
+
+		length = squashfs_read_data(sb, &buffer,
+			sizeof(struct squashfs_super_block), 0, NULL,
+			PAGE_CACHE_SIZE, 1);
+
+		if (length < 0) {
+			strm = ERR_PTR(length);
+			goto finished;
+		}
+	}
+
+	strm = msblk->decompressor->init(msblk, buffer, length);
+
+finished:
+	kfree(buffer);
+
+	return strm;
+}

+ 1 - 6
fs/squashfs/decompressor.h

@@ -24,7 +24,7 @@
  */
  */
 
 
 struct squashfs_decompressor {
 struct squashfs_decompressor {
-	void	*(*init)(struct squashfs_sb_info *);
+	void	*(*init)(struct squashfs_sb_info *, void *, int);
 	void	(*free)(void *);
 	void	(*free)(void *);
 	int	(*decompress)(struct squashfs_sb_info *, void **,
 	int	(*decompress)(struct squashfs_sb_info *, void **,
 		struct buffer_head **, int, int, int, int, int);
 		struct buffer_head **, int, int, int, int, int);
@@ -33,11 +33,6 @@ struct squashfs_decompressor {
 	int	supported;
 	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,
 static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk,
 	void *s)
 	void *s)
 {
 {

+ 9 - 0
fs/squashfs/dir.c

@@ -172,6 +172,11 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
 		length += sizeof(dirh);
 		length += sizeof(dirh);
 
 
 		dir_count = le32_to_cpu(dirh.count) + 1;
 		dir_count = le32_to_cpu(dirh.count) + 1;
+
+		/* dir_count should never be larger than 256 */
+		if (dir_count > 256)
+			goto failed_read;
+
 		while (dir_count--) {
 		while (dir_count--) {
 			/*
 			/*
 			 * Read directory entry.
 			 * Read directory entry.
@@ -183,6 +188,10 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
 
 
 			size = le16_to_cpu(dire->size) + 1;
 			size = le16_to_cpu(dire->size) + 1;
 
 
+			/* size should never be larger than SQUASHFS_NAME_LEN */
+			if (size > SQUASHFS_NAME_LEN)
+				goto failed_read;
+
 			err = squashfs_read_metadata(inode->i_sb, dire->name,
 			err = squashfs_read_metadata(inode->i_sb, dire->name,
 					&block, &offset, size);
 					&block, &offset, size);
 			if (err < 0)
 			if (err < 0)

+ 2 - 2
fs/squashfs/lzo_wrapper.c

@@ -37,7 +37,7 @@ struct squashfs_lzo {
 	void	*output;
 	void	*output;
 };
 };
 
 
-static void *lzo_init(struct squashfs_sb_info *msblk)
+static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len)
 {
 {
 	int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
 	int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
 
 
@@ -58,7 +58,7 @@ failed2:
 failed:
 failed:
 	ERROR("Failed to allocate lzo workspace\n");
 	ERROR("Failed to allocate lzo workspace\n");
 	kfree(stream);
 	kfree(stream);
-	return NULL;
+	return ERR_PTR(-ENOMEM);
 }
 }
 
 
 
 

+ 12 - 0
fs/squashfs/namei.c

@@ -176,6 +176,11 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
 		length += sizeof(dirh);
 		length += sizeof(dirh);
 
 
 		dir_count = le32_to_cpu(dirh.count) + 1;
 		dir_count = le32_to_cpu(dirh.count) + 1;
+
+		/* dir_count should never be larger than 256 */
+		if (dir_count > 256)
+			goto data_error;
+
 		while (dir_count--) {
 		while (dir_count--) {
 			/*
 			/*
 			 * Read directory entry.
 			 * Read directory entry.
@@ -187,6 +192,10 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
 
 
 			size = le16_to_cpu(dire->size) + 1;
 			size = le16_to_cpu(dire->size) + 1;
 
 
+			/* size should never be larger than SQUASHFS_NAME_LEN */
+			if (size > SQUASHFS_NAME_LEN)
+				goto data_error;
+
 			err = squashfs_read_metadata(dir->i_sb, dire->name,
 			err = squashfs_read_metadata(dir->i_sb, dire->name,
 					&block, &offset, size);
 					&block, &offset, size);
 			if (err < 0)
 			if (err < 0)
@@ -228,6 +237,9 @@ exit_lookup:
 	d_add(dentry, inode);
 	d_add(dentry, inode);
 	return ERR_PTR(0);
 	return ERR_PTR(0);
 
 
+data_error:
+	err = -EIO;
+
 read_failure:
 read_failure:
 	ERROR("Unable to read directory block [%llx:%x]\n",
 	ERROR("Unable to read directory block [%llx:%x]\n",
 		squashfs_i(dir)->start + msblk->directory_table,
 		squashfs_i(dir)->start + msblk->directory_table,

+ 1 - 0
fs/squashfs/squashfs.h

@@ -48,6 +48,7 @@ extern int squashfs_read_table(struct super_block *, void *, u64, int);
 
 
 /* decompressor.c */
 /* decompressor.c */
 extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
 extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
+extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
 
 
 /* export.c */
 /* export.c */
 extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,
 extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,

+ 4 - 0
fs/squashfs/squashfs_fs.h

@@ -57,6 +57,7 @@
 #define SQUASHFS_ALWAYS_FRAG		5
 #define SQUASHFS_ALWAYS_FRAG		5
 #define SQUASHFS_DUPLICATE		6
 #define SQUASHFS_DUPLICATE		6
 #define SQUASHFS_EXPORT			7
 #define SQUASHFS_EXPORT			7
+#define SQUASHFS_COMP_OPT		10
 
 
 #define SQUASHFS_BIT(flag, bit)		((flag >> bit) & 1)
 #define SQUASHFS_BIT(flag, bit)		((flag >> bit) & 1)
 
 
@@ -81,6 +82,9 @@
 #define SQUASHFS_EXPORTABLE(flags)		SQUASHFS_BIT(flags, \
 #define SQUASHFS_EXPORTABLE(flags)		SQUASHFS_BIT(flags, \
 						SQUASHFS_EXPORT)
 						SQUASHFS_EXPORT)
 
 
+#define SQUASHFS_COMP_OPTS(flags)		SQUASHFS_BIT(flags, \
+						SQUASHFS_COMP_OPT)
+
 /* Max number of types and file types */
 /* Max number of types and file types */
 #define SQUASHFS_DIR_TYPE		1
 #define SQUASHFS_DIR_TYPE		1
 #define SQUASHFS_REG_TYPE		2
 #define SQUASHFS_REG_TYPE		2

+ 9 - 6
fs/squashfs/super.c

@@ -199,10 +199,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 
 
 	err = -ENOMEM;
 	err = -ENOMEM;
 
 
-	msblk->stream = squashfs_decompressor_init(msblk);
-	if (msblk->stream == NULL)
-		goto failed_mount;
-
 	msblk->block_cache = squashfs_cache_init("metadata",
 	msblk->block_cache = squashfs_cache_init("metadata",
 			SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
 			SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
 	if (msblk->block_cache == NULL)
 	if (msblk->block_cache == NULL)
@@ -215,6 +211,13 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 		goto failed_mount;
 		goto failed_mount;
 	}
 	}
 
 
+	msblk->stream = squashfs_decompressor_init(sb, flags);
+	if (IS_ERR(msblk->stream)) {
+		err = PTR_ERR(msblk->stream);
+		msblk->stream = NULL;
+		goto failed_mount;
+	}
+
 	/* Allocate and read id index table */
 	/* Allocate and read id index table */
 	msblk->id_table = squashfs_read_id_index_table(sb,
 	msblk->id_table = squashfs_read_id_index_table(sb,
 		le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids));
 		le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids));
@@ -370,8 +373,8 @@ static void squashfs_put_super(struct super_block *sb)
 }
 }
 
 
 
 
-static struct dentry *squashfs_mount(struct file_system_type *fs_type, int flags,
-				const char *dev_name, void *data)
+static struct dentry *squashfs_mount(struct file_system_type *fs_type,
+				int flags, const char *dev_name, void *data)
 {
 {
 	return mount_bdev(fs_type, flags, dev_name, data, squashfs_fill_super);
 	return mount_bdev(fs_type, flags, dev_name, data, squashfs_fill_super);
 }
 }

+ 43 - 10
fs/squashfs/xz_wrapper.c

@@ -26,10 +26,10 @@
 #include <linux/buffer_head.h>
 #include <linux/buffer_head.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/xz.h>
 #include <linux/xz.h>
+#include <linux/bitops.h>
 
 
 #include "squashfs_fs.h"
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_sb.h"
-#include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "squashfs.h"
 #include "decompressor.h"
 #include "decompressor.h"
 
 
@@ -38,24 +38,57 @@ struct squashfs_xz {
 	struct xz_buf buf;
 	struct xz_buf buf;
 };
 };
 
 
-static void *squashfs_xz_init(struct squashfs_sb_info *msblk)
+struct comp_opts {
+	__le32 dictionary_size;
+	__le32 flags;
+};
+
+static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff,
+	int len)
 {
 {
-	int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE);
+	struct comp_opts *comp_opts = buff;
+	struct squashfs_xz *stream;
+	int dict_size = msblk->block_size;
+	int err, n;
+
+	if (comp_opts) {
+		/* check compressor options are the expected length */
+		if (len < sizeof(*comp_opts)) {
+			err = -EIO;
+			goto failed;
+		}
 
 
-	struct squashfs_xz *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
-	if (stream == NULL)
+		dict_size = le32_to_cpu(comp_opts->dictionary_size);
+
+		/* the dictionary size should be 2^n or 2^n+2^(n+1) */
+		n = ffs(dict_size) - 1;
+		if (dict_size != (1 << n) && dict_size != (1 << n) +
+						(1 << (n + 1))) {
+			err = -EIO;
+			goto failed;
+		}
+	}
+
+	dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE);
+
+	stream = kmalloc(sizeof(*stream), GFP_KERNEL);
+	if (stream == NULL) {
+		err = -ENOMEM;
 		goto failed;
 		goto failed;
+	}
 
 
-	stream->state = xz_dec_init(XZ_PREALLOC, block_size);
-	if (stream->state == NULL)
+	stream->state = xz_dec_init(XZ_PREALLOC, dict_size);
+	if (stream->state == NULL) {
+		kfree(stream);
+		err = -ENOMEM;
 		goto failed;
 		goto failed;
+	}
 
 
 	return stream;
 	return stream;
 
 
 failed:
 failed:
-	ERROR("Failed to allocate xz workspace\n");
-	kfree(stream);
-	return NULL;
+	ERROR("Failed to initialise xz decompressor\n");
+	return ERR_PTR(err);
 }
 }
 
 
 
 

+ 5 - 5
fs/squashfs/zlib_wrapper.c

@@ -26,19 +26,19 @@
 #include <linux/buffer_head.h>
 #include <linux/buffer_head.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/zlib.h>
 #include <linux/zlib.h>
+#include <linux/vmalloc.h>
 
 
 #include "squashfs_fs.h"
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs.h"
 #include "squashfs.h"
 #include "decompressor.h"
 #include "decompressor.h"
 
 
-static void *zlib_init(struct squashfs_sb_info *dummy)
+static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len)
 {
 {
 	z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
 	z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL);
 	if (stream == NULL)
 	if (stream == NULL)
 		goto failed;
 		goto failed;
-	stream->workspace = kmalloc(zlib_inflate_workspacesize(),
-		GFP_KERNEL);
+	stream->workspace = vmalloc(zlib_inflate_workspacesize());
 	if (stream->workspace == NULL)
 	if (stream->workspace == NULL)
 		goto failed;
 		goto failed;
 
 
@@ -47,7 +47,7 @@ static void *zlib_init(struct squashfs_sb_info *dummy)
 failed:
 failed:
 	ERROR("Failed to allocate zlib workspace\n");
 	ERROR("Failed to allocate zlib workspace\n");
 	kfree(stream);
 	kfree(stream);
-	return NULL;
+	return ERR_PTR(-ENOMEM);
 }
 }
 
 
 
 
@@ -56,7 +56,7 @@ static void zlib_free(void *strm)
 	z_stream *stream = strm;
 	z_stream *stream = strm;
 
 
 	if (stream)
 	if (stream)
-		kfree(stream->workspace);
+		vfree(stream->workspace);
 	kfree(stream);
 	kfree(stream);
 }
 }