Merge git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-linus
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Mar 2011 15:02:21 +0000 (08:02 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Mar 2011 15:02:21 +0000 (08:02 -0700)
* 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

12 files changed:
Documentation/filesystems/squashfs.txt
fs/squashfs/Kconfig
fs/squashfs/decompressor.c
fs/squashfs/decompressor.h
fs/squashfs/dir.c
fs/squashfs/lzo_wrapper.c
fs/squashfs/namei.c
fs/squashfs/squashfs.h
fs/squashfs/squashfs_fs.h
fs/squashfs/super.c
fs/squashfs/xz_wrapper.c
fs/squashfs/zlib_wrapper.c

index 66699af..2d78f19 100644 (file)
@@ -59,12 +59,15 @@ obtained from this site also.
 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   |
        |---------------|
+       |  compression  |
+       |    options    |
+       |---------------|
        |  datablocks   |
        |  & 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
 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
@@ -114,7 +124,7 @@ directory inode are defined: inodes optimised for frequently occurring
 regular files and directories, and extended types where extra
 information has to be stored.
 
-3.2 Directories
+3.3 Directories
 ---------------
 
 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
 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
@@ -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
 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
@@ -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
 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
@@ -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
 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
@@ -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
 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
index aa68a8a..efc309f 100644 (file)
@@ -5,12 +5,12 @@ config SQUASHFS
        help
          Saying Y here includes support for SquashFS 4.0 (a Compressed
          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.
 
          Squashfs is intended for general read-only filesystem use, for
index a5940e5..e921bd2 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <linux/types.h>
 #include <linux/mutex.h>
+#include <linux/slab.h>
 #include <linux/buffer_head.h>
 
 #include "squashfs_fs.h"
@@ -74,3 +75,36 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
 
        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;
+}
index 3b305a7..099745a 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 struct squashfs_decompressor {
-       void    *(*init)(struct squashfs_sb_info *);
+       void    *(*init)(struct squashfs_sb_info *, void *, int);
        void    (*free)(void *);
        int     (*decompress)(struct squashfs_sb_info *, void **,
                struct buffer_head **, int, int, int, int, int);
@@ -33,11 +33,6 @@ struct squashfs_decompressor {
        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,
        void *s)
 {
index 0dc340a..3f79cd1 100644 (file)
@@ -172,6 +172,11 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
                length += sizeof(dirh);
 
                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--) {
                        /*
                         * 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 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,
                                        &block, &offset, size);
                        if (err < 0)
index 7da759e..00f4dfc 100644 (file)
@@ -37,7 +37,7 @@ struct squashfs_lzo {
        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);
 
@@ -58,7 +58,7 @@ failed2:
 failed:
        ERROR("Failed to allocate lzo workspace\n");
        kfree(stream);
-       return NULL;
+       return ERR_PTR(-ENOMEM);
 }
 
 
index 7a9464d..5d922a6 100644 (file)
@@ -176,6 +176,11 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
                length += sizeof(dirh);
 
                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--) {
                        /*
                         * 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 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,
                                        &block, &offset, size);
                        if (err < 0)
@@ -228,6 +237,9 @@ exit_lookup:
        d_add(dentry, inode);
        return ERR_PTR(0);
 
+data_error:
+       err = -EIO;
+
 read_failure:
        ERROR("Unable to read directory block [%llx:%x]\n",
                squashfs_i(dir)->start + msblk->directory_table,
index ba729d8..1f2e608 100644 (file)
@@ -48,6 +48,7 @@ extern int squashfs_read_table(struct super_block *, void *, u64, int);
 
 /* decompressor.c */
 extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
+extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
 
 /* export.c */
 extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,
index 39533fe..4582c56 100644 (file)
@@ -57,6 +57,7 @@
 #define SQUASHFS_ALWAYS_FRAG           5
 #define SQUASHFS_DUPLICATE             6
 #define SQUASHFS_EXPORT                        7
+#define SQUASHFS_COMP_OPT              10
 
 #define SQUASHFS_BIT(flag, bit)                ((flag >> bit) & 1)
 
@@ -81,6 +82,9 @@
 #define SQUASHFS_EXPORTABLE(flags)             SQUASHFS_BIT(flags, \
                                                SQUASHFS_EXPORT)
 
+#define SQUASHFS_COMP_OPTS(flags)              SQUASHFS_BIT(flags, \
+                                               SQUASHFS_COMP_OPT)
+
 /* Max number of types and file types */
 #define SQUASHFS_DIR_TYPE              1
 #define SQUASHFS_REG_TYPE              2
index 20700b9..5c8184c 100644 (file)
@@ -199,10 +199,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 
        err = -ENOMEM;
 
-       msblk->stream = squashfs_decompressor_init(msblk);
-       if (msblk->stream == NULL)
-               goto failed_mount;
-
        msblk->block_cache = squashfs_cache_init("metadata",
                        SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
        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;
        }
 
+       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 */
        msblk->id_table = squashfs_read_id_index_table(sb,
                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);
 }
index c4eb400..aa47a28 100644 (file)
 #include <linux/buffer_head.h>
 #include <linux/slab.h>
 #include <linux/xz.h>
+#include <linux/bitops.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
-#include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "decompressor.h"
 
@@ -38,24 +38,57 @@ struct squashfs_xz {
        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;
+       }
 
-       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;
+       }
 
        return stream;
 
 failed:
-       ERROR("Failed to allocate xz workspace\n");
-       kfree(stream);
-       return NULL;
+       ERROR("Failed to initialise xz decompressor\n");
+       return ERR_PTR(err);
 }
 
 
index 4661ae2..517688b 100644 (file)
 #include <linux/buffer_head.h>
 #include <linux/slab.h>
 #include <linux/zlib.h>
+#include <linux/vmalloc.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs.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);
        if (stream == NULL)
                goto failed;
-       stream->workspace = kmalloc(zlib_inflate_workspacesize(),
-               GFP_KERNEL);
+       stream->workspace = vmalloc(zlib_inflate_workspacesize());
        if (stream->workspace == NULL)
                goto failed;
 
@@ -47,7 +47,7 @@ static void *zlib_init(struct squashfs_sb_info *dummy)
 failed:
        ERROR("Failed to allocate zlib workspace\n");
        kfree(stream);
-       return NULL;
+       return ERR_PTR(-ENOMEM);
 }
 
 
@@ -56,7 +56,7 @@ static void zlib_free(void *strm)
        z_stream *stream = strm;
 
        if (stream)
-               kfree(stream->workspace);
+               vfree(stream->workspace);
        kfree(stream);
 }