rename generic-2.6/patches to generic-2.6/patches-2.6.21
authorFelix Fietkau <nbd@openwrt.org>
Sun, 21 Sep 2008 11:25:17 +0000 (11:25 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 21 Sep 2008 11:25:17 +0000 (11:25 +0000)
SVN-Revision: 12636

88 files changed:
target/linux/generic-2.6/patches-2.6.21/001-squashfs.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/002-lzma_decompress.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/003-squashfs_lzma.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/004-extra_optimization.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/006-gcc4_inline_fix.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/007-samsung_flash.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/009-revert_intel_flash_breakage.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/010-disable_old_squashfs_compatibility.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/011-mips_boot.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/012-mips_cpu_tlb.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/013-mips_generic_gpio_support.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/060-rootfs_split.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/065-block2mtd_init.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/100-netfilter_layer7_2.17.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/101-netfilter_layer7_pktmatch.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/110-ipp2p_0.8.1rc1.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/120-openswan-2.4.0.kernel-2.6-natt.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/130-netfilter_ipset.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/140-netfilter_time.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/150-netfilter_imq.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/160-netfilter_route.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/170-netfilter_chaostables_0.8.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/171-netfilter_tarpit.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/180-netfilter_depends.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/200-sched_esfq.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/201-multiple_default_gateways.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/202-mips-freestanding.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/204-jffs2_eofdetect.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/207-powerpc_asm_segment_h.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/208-rtl8110sb_fix.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/209-mini_fo.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/210-d80211_compat.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/211-no_block2mtd_readahead.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/212-block2mtd_erase_scan.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/213-kobject_uevent.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/420-gpiodev.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/510-yaffs_support.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/511-yaffs_reduce_compiler_warnings.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/900-headers_type_and_time.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/902-darwin_scripts_include.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/903-hostap_txpower.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/903-stddef_include.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/904-ls_time_locale.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.21/999-backport_ip_hdr_and_friends.patch [new file with mode: 0644]
target/linux/generic-2.6/patches/001-squashfs.patch [deleted file]
target/linux/generic-2.6/patches/002-lzma_decompress.patch [deleted file]
target/linux/generic-2.6/patches/003-squashfs_lzma.patch [deleted file]
target/linux/generic-2.6/patches/004-extra_optimization.patch [deleted file]
target/linux/generic-2.6/patches/006-gcc4_inline_fix.patch [deleted file]
target/linux/generic-2.6/patches/007-samsung_flash.patch [deleted file]
target/linux/generic-2.6/patches/009-revert_intel_flash_breakage.patch [deleted file]
target/linux/generic-2.6/patches/010-disable_old_squashfs_compatibility.patch [deleted file]
target/linux/generic-2.6/patches/011-mips_boot.patch [deleted file]
target/linux/generic-2.6/patches/012-mips_cpu_tlb.patch [deleted file]
target/linux/generic-2.6/patches/013-mips_generic_gpio_support.patch [deleted file]
target/linux/generic-2.6/patches/060-rootfs_split.patch [deleted file]
target/linux/generic-2.6/patches/065-block2mtd_init.patch [deleted file]
target/linux/generic-2.6/patches/100-netfilter_layer7_2.17.patch [deleted file]
target/linux/generic-2.6/patches/101-netfilter_layer7_pktmatch.patch [deleted file]
target/linux/generic-2.6/patches/110-ipp2p_0.8.1rc1.patch [deleted file]
target/linux/generic-2.6/patches/120-openswan-2.4.0.kernel-2.6-natt.patch [deleted file]
target/linux/generic-2.6/patches/130-netfilter_ipset.patch [deleted file]
target/linux/generic-2.6/patches/140-netfilter_time.patch [deleted file]
target/linux/generic-2.6/patches/150-netfilter_imq.patch [deleted file]
target/linux/generic-2.6/patches/160-netfilter_route.patch [deleted file]
target/linux/generic-2.6/patches/170-netfilter_chaostables_0.8.patch [deleted file]
target/linux/generic-2.6/patches/171-netfilter_tarpit.patch [deleted file]
target/linux/generic-2.6/patches/180-netfilter_depends.patch [deleted file]
target/linux/generic-2.6/patches/200-sched_esfq.patch [deleted file]
target/linux/generic-2.6/patches/201-multiple_default_gateways.patch [deleted file]
target/linux/generic-2.6/patches/202-mips-freestanding.patch [deleted file]
target/linux/generic-2.6/patches/204-jffs2_eofdetect.patch [deleted file]
target/linux/generic-2.6/patches/207-powerpc_asm_segment_h.patch [deleted file]
target/linux/generic-2.6/patches/208-rtl8110sb_fix.patch [deleted file]
target/linux/generic-2.6/patches/209-mini_fo.patch [deleted file]
target/linux/generic-2.6/patches/210-d80211_compat.patch [deleted file]
target/linux/generic-2.6/patches/211-no_block2mtd_readahead.patch [deleted file]
target/linux/generic-2.6/patches/212-block2mtd_erase_scan.patch [deleted file]
target/linux/generic-2.6/patches/213-kobject_uevent.patch [deleted file]
target/linux/generic-2.6/patches/420-gpiodev.patch [deleted file]
target/linux/generic-2.6/patches/510-yaffs_support.patch [deleted file]
target/linux/generic-2.6/patches/511-yaffs_reduce_compiler_warnings.patch [deleted file]
target/linux/generic-2.6/patches/900-headers_type_and_time.patch [deleted file]
target/linux/generic-2.6/patches/902-darwin_scripts_include.patch [deleted file]
target/linux/generic-2.6/patches/903-hostap_txpower.patch [deleted file]
target/linux/generic-2.6/patches/903-stddef_include.patch [deleted file]
target/linux/generic-2.6/patches/904-ls_time_locale.patch [deleted file]
target/linux/generic-2.6/patches/999-backport_ip_hdr_and_friends.patch [deleted file]

diff --git a/target/linux/generic-2.6/patches-2.6.21/001-squashfs.patch b/target/linux/generic-2.6/patches-2.6.21/001-squashfs.patch
new file mode 100644 (file)
index 0000000..e64db46
--- /dev/null
@@ -0,0 +1,4180 @@
+Index: linux-2.6.21.7/fs/Kconfig
+===================================================================
+--- linux-2.6.21.7.orig/fs/Kconfig
++++ linux-2.6.21.7/fs/Kconfig
+@@ -1371,6 +1371,71 @@ config CRAMFS
+         If unsure, say N.
++config SQUASHFS
++      tristate "SquashFS 3.0 - Squashed file system support"
++      select ZLIB_INFLATE
++      help
++        Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File
++        System).  Squashfs is a highly compressed read-only filesystem for Linux.
++        It uses zlib 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 64K.
++        SquashFS 3.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 archival
++        use (i.e. in cases where a .tar.gz file may be used), and in embedded
++        systems where low overhead is needed.  Further information and filesystem tools
++        are available from http://squashfs.sourceforge.net.
++
++        If you want to compile this as a module ( = code which can be
++        inserted in and removed from the running kernel whenever you want),
++        say M here and read <file:Documentation/modules.txt>.  The module
++        will be called squashfs.  Note that the root file system (the one
++        containing the directory /) cannot be compiled as a module.
++
++        If unsure, say N.
++
++config SQUASHFS_EMBEDDED
++
++      bool "Additional options for memory-constrained systems"
++      depends on SQUASHFS
++      default n
++      help
++        Saying Y here allows you to specify cache sizes and how Squashfs
++        allocates memory.  This is only intended for memory constrained
++        systems.
++
++        If unsure, say N.
++
++config SQUASHFS_FRAGMENT_CACHE_SIZE
++      int "Number of fragments cached" if SQUASHFS_EMBEDDED
++      depends on SQUASHFS
++      default "3"
++      help
++        By default SquashFS caches the last 3 fragments read from
++        the filesystem.  Increasing this amount may mean SquashFS
++        has to re-read fragments less often from disk, at the expense
++        of extra system memory.  Decreasing this amount will mean
++        SquashFS uses less memory at the expense of extra reads from disk.
++
++        Note there must be at least one cached fragment.  Anything
++        much more than three will probably not make much difference.
++
++config SQUASHFS_VMALLOC
++      bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED
++      depends on SQUASHFS
++      default n
++      help
++        By default SquashFS uses kmalloc to obtain fragment cache memory.
++        Kmalloc memory is the standard kernel allocator, but it can fail
++        on memory constrained systems.  Because of the way Vmalloc works,
++        Vmalloc can succeed when kmalloc fails.  Specifying this option
++        will make SquashFS always use Vmalloc to allocate the
++        fragment cache memory.
++
++        If unsure, say N.
++
+ config VXFS_FS
+       tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
+       depends on BLOCK
+Index: linux-2.6.21.7/fs/Makefile
+===================================================================
+--- linux-2.6.21.7.orig/fs/Makefile
++++ linux-2.6.21.7/fs/Makefile
+@@ -68,6 +68,7 @@ obj-$(CONFIG_JBD)            += jbd/
+ obj-$(CONFIG_JBD2)            += jbd2/
+ obj-$(CONFIG_EXT2_FS)         += ext2/
+ obj-$(CONFIG_CRAMFS)          += cramfs/
++obj-$(CONFIG_SQUASHFS)                += squashfs/
+ obj-$(CONFIG_RAMFS)           += ramfs/
+ obj-$(CONFIG_HUGETLBFS)               += hugetlbfs/
+ obj-$(CONFIG_CODA_FS)         += coda/
+Index: linux-2.6.21.7/fs/squashfs/inode.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/fs/squashfs/inode.c
+@@ -0,0 +1,2124 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * inode.c
++ */
++
++#include <linux/types.h>
++#include <linux/squashfs_fs.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++#include <linux/squashfs_fs_sb.h>
++#include <linux/squashfs_fs_i.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include <linux/init.h>
++#include <linux/dcache.h>
++#include <linux/wait.h>
++#include <linux/zlib.h>
++#include <linux/blkdev.h>
++#include <linux/vmalloc.h>
++#include <asm/uaccess.h>
++#include <asm/semaphore.h>
++
++#include "squashfs.h"
++
++static void squashfs_put_super(struct super_block *);
++static int squashfs_statfs(struct dentry *, struct kstatfs *);
++static int squashfs_symlink_readpage(struct file *file, struct page *page);
++static int squashfs_readpage(struct file *file, struct page *page);
++static int squashfs_readpage4K(struct file *file, struct page *page);
++static int squashfs_readdir(struct file *, void *, filldir_t);
++static struct inode *squashfs_alloc_inode(struct super_block *sb);
++static void squashfs_destroy_inode(struct inode *inode);
++static int init_inodecache(void);
++static void destroy_inodecache(void);
++static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
++                              struct nameidata *);
++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode);
++static long long read_blocklist(struct inode *inode, int index,
++                              int readahead_blks, char *block_list,
++                              unsigned short **block_p, unsigned int *bsize);
++static int squashfs_get_sb(struct file_system_type *, int,
++                      const char *, void *, struct vfsmount *);
++
++
++static z_stream stream;
++
++static struct file_system_type squashfs_fs_type = {
++      .owner = THIS_MODULE,
++      .name = "squashfs",
++      .get_sb = squashfs_get_sb,
++      .kill_sb = kill_block_super,
++      .fs_flags = FS_REQUIRES_DEV
++};
++
++static unsigned char squashfs_filetype_table[] = {
++      DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
++};
++
++static struct super_operations squashfs_ops = {
++      .alloc_inode = squashfs_alloc_inode,
++      .destroy_inode = squashfs_destroy_inode,
++      .statfs = squashfs_statfs,
++      .put_super = squashfs_put_super,
++};
++
++SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = {
++      .readpage = squashfs_symlink_readpage
++};
++
++SQSH_EXTERN struct address_space_operations squashfs_aops = {
++      .readpage = squashfs_readpage
++};
++
++SQSH_EXTERN struct address_space_operations squashfs_aops_4K = {
++      .readpage = squashfs_readpage4K
++};
++
++static struct file_operations squashfs_dir_ops = {
++      .read = generic_read_dir,
++      .readdir = squashfs_readdir
++};
++
++SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = {
++      .lookup = squashfs_lookup
++};
++
++
++static struct buffer_head *get_block_length(struct super_block *s,
++                              int *cur_index, int *offset, int *c_byte)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      unsigned short temp;
++      struct buffer_head *bh;
++
++      if (!(bh = sb_bread(s, *cur_index)))
++              goto out;
++
++      if (msblk->devblksize - *offset == 1) {
++              if (msblk->swap)
++                      ((unsigned char *) &temp)[1] = *((unsigned char *)
++                              (bh->b_data + *offset));
++              else
++                      ((unsigned char *) &temp)[0] = *((unsigned char *)
++                              (bh->b_data + *offset));
++              brelse(bh);
++              if (!(bh = sb_bread(s, ++(*cur_index))))
++                      goto out;
++              if (msblk->swap)
++                      ((unsigned char *) &temp)[0] = *((unsigned char *)
++                              bh->b_data);
++              else
++                      ((unsigned char *) &temp)[1] = *((unsigned char *)
++                              bh->b_data);
++              *c_byte = temp;
++              *offset = 1;
++      } else {
++              if (msblk->swap) {
++                      ((unsigned char *) &temp)[1] = *((unsigned char *)
++                              (bh->b_data + *offset));
++                      ((unsigned char *) &temp)[0] = *((unsigned char *)
++                              (bh->b_data + *offset + 1));
++              } else {
++                      ((unsigned char *) &temp)[0] = *((unsigned char *)
++                              (bh->b_data + *offset));
++                      ((unsigned char *) &temp)[1] = *((unsigned char *)
++                              (bh->b_data + *offset + 1));
++              }
++              *c_byte = temp;
++              *offset += 2;
++      }
++
++      if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
++              if (*offset == msblk->devblksize) {
++                      brelse(bh);
++                      if (!(bh = sb_bread(s, ++(*cur_index))))
++                              goto out;
++                      *offset = 0;
++              }
++              if (*((unsigned char *) (bh->b_data + *offset)) !=
++                                              SQUASHFS_MARKER_BYTE) {
++                      ERROR("Metadata block marker corrupt @ %x\n",
++                                              *cur_index);
++                      brelse(bh);
++                      goto out;
++              }
++              (*offset)++;
++      }
++      return bh;
++
++out:
++      return NULL;
++}
++
++
++SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
++                      long long index, unsigned int length,
++                      long long *next_index)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
++                      msblk->devblksize_log2) + 2];
++      unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
++      unsigned int cur_index = index >> msblk->devblksize_log2;
++      int bytes, avail_bytes, b = 0, k;
++      char *c_buffer;
++      unsigned int compressed;
++      unsigned int c_byte = length;
++
++      if (c_byte) {
++              bytes = msblk->devblksize - offset;
++              compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
++              c_buffer = compressed ? msblk->read_data : buffer;
++              c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
++
++              TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
++                                      ? "" : "un", (unsigned int) c_byte);
++
++              if (!(bh[0] = sb_getblk(s, cur_index)))
++                      goto block_release;
++
++              for (b = 1; bytes < c_byte; b++) {
++                      if (!(bh[b] = sb_getblk(s, ++cur_index)))
++                              goto block_release;
++                      bytes += msblk->devblksize;
++              }
++              ll_rw_block(READ, b, bh);
++      } else {
++              if (!(bh[0] = get_block_length(s, &cur_index, &offset,
++                                                              &c_byte)))
++                      goto read_failure;
++
++              bytes = msblk->devblksize - offset;
++              compressed = SQUASHFS_COMPRESSED(c_byte);
++              c_buffer = compressed ? msblk->read_data : buffer;
++              c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
++
++              TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
++                                      ? "" : "un", (unsigned int) c_byte);
++
++              for (b = 1; bytes < c_byte; b++) {
++                      if (!(bh[b] = sb_getblk(s, ++cur_index)))
++                              goto block_release;
++                      bytes += msblk->devblksize;
++              }
++              ll_rw_block(READ, b - 1, bh + 1);
++      }
++
++      if (compressed)
++              down(&msblk->read_data_mutex);
++
++      for (bytes = 0, k = 0; k < b; k++) {
++              avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
++                                      msblk->devblksize - offset :
++                                      c_byte - bytes;
++              wait_on_buffer(bh[k]);
++              if (!buffer_uptodate(bh[k]))
++                      goto block_release;
++              memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes);
++              bytes += avail_bytes;
++              offset = 0;
++              brelse(bh[k]);
++      }
++
++      /*
++       * uncompress block
++       */
++      if (compressed) {
++              int zlib_err;
++
++              stream.next_in = c_buffer;
++              stream.avail_in = c_byte;
++              stream.next_out = buffer;
++              stream.avail_out = msblk->read_size;
++
++              if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) ||
++                              ((zlib_err = zlib_inflate(&stream, Z_FINISH))
++                               != Z_STREAM_END) || ((zlib_err =
++                              zlib_inflateEnd(&stream)) != Z_OK)) {
++                      ERROR("zlib_fs returned unexpected result 0x%x\n",
++                              zlib_err);
++                      bytes = 0;
++              } else
++                      bytes = stream.total_out;
++
++              up(&msblk->read_data_mutex);
++      }
++
++      if (next_index)
++              *next_index = index + c_byte + (length ? 0 :
++                              (SQUASHFS_CHECK_DATA(msblk->sblk.flags)
++                               ? 3 : 2));
++      return bytes;
++
++block_release:
++      while (--b >= 0)
++              brelse(bh[b]);
++
++read_failure:
++      ERROR("sb_bread failed reading block 0x%x\n", cur_index);
++      return 0;
++}
++
++
++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
++                              long long block, unsigned int offset,
++                              int length, long long *next_block,
++                              unsigned int *next_offset)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      int n, i, bytes, return_length = length;
++      long long next_index;
++
++      TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
++
++      while ( 1 ) {
++              for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++                      if (msblk->block_cache[i].block == block)
++                              break;
++
++              down(&msblk->block_cache_mutex);
++
++              if (i == SQUASHFS_CACHED_BLKS) {
++                      /* read inode header block */
++                      for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS;
++                                      n ; n --, i = (i + 1) %
++                                      SQUASHFS_CACHED_BLKS)
++                              if (msblk->block_cache[i].block !=
++                                                      SQUASHFS_USED_BLK)
++                                      break;
++
++                      if (n == 0) {
++                              wait_queue_t wait;
++
++                              init_waitqueue_entry(&wait, current);
++                              add_wait_queue(&msblk->waitq, &wait);
++                              set_current_state(TASK_UNINTERRUPTIBLE);
++                              up(&msblk->block_cache_mutex);
++                              schedule();
++                              set_current_state(TASK_RUNNING);
++                              remove_wait_queue(&msblk->waitq, &wait);
++                              continue;
++                      }
++                      msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
++
++                      if (msblk->block_cache[i].block ==
++                                                      SQUASHFS_INVALID_BLK) {
++                              if (!(msblk->block_cache[i].data =
++                                              kmalloc(SQUASHFS_METADATA_SIZE,
++                                              GFP_KERNEL))) {
++                                      ERROR("Failed to allocate cache"
++                                                      "block\n");
++                                      up(&msblk->block_cache_mutex);
++                                      goto out;
++                              }
++                      }
++
++                      msblk->block_cache[i].block = SQUASHFS_USED_BLK;
++                      up(&msblk->block_cache_mutex);
++
++                      if (!(msblk->block_cache[i].length =
++                                              squashfs_read_data(s,
++                                              msblk->block_cache[i].data,
++                                              block, 0, &next_index))) {
++                              ERROR("Unable to read cache block [%llx:%x]\n",
++                                              block, offset);
++                              goto out;
++                      }
++
++                      down(&msblk->block_cache_mutex);
++                      wake_up(&msblk->waitq);
++                      msblk->block_cache[i].block = block;
++                      msblk->block_cache[i].next_index = next_index;
++                      TRACE("Read cache block [%llx:%x]\n", block, offset);
++              }
++
++              if (msblk->block_cache[i].block != block) {
++                      up(&msblk->block_cache_mutex);
++                      continue;
++              }
++
++              if ((bytes = msblk->block_cache[i].length - offset) >= length) {
++                      if (buffer)
++                              memcpy(buffer, msblk->block_cache[i].data +
++                                              offset, length);
++                      if (msblk->block_cache[i].length - offset == length) {
++                              *next_block = msblk->block_cache[i].next_index;
++                              *next_offset = 0;
++                      } else {
++                              *next_block = block;
++                              *next_offset = offset + length;
++                      }
++                      up(&msblk->block_cache_mutex);
++                      goto finish;
++              } else {
++                      if (buffer) {
++                              memcpy(buffer, msblk->block_cache[i].data +
++                                              offset, bytes);
++                              buffer += bytes;
++                      }
++                      block = msblk->block_cache[i].next_index;
++                      up(&msblk->block_cache_mutex);
++                      length -= bytes;
++                      offset = 0;
++              }
++      }
++
++finish:
++      return return_length;
++out:
++      return 0;
++}
++
++
++static int get_fragment_location(struct super_block *s, unsigned int fragment,
++                              long long *fragment_start_block,
++                              unsigned int *fragment_size)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      long long start_block =
++              msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
++      int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
++      struct squashfs_fragment_entry fragment_entry;
++
++      if (msblk->swap) {
++              struct squashfs_fragment_entry sfragment_entry;
++
++              if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
++                                      start_block, offset,
++                                      sizeof(sfragment_entry), &start_block,
++                                      &offset))
++                      goto out;
++              SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry);
++      } else
++              if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
++                                      start_block, offset,
++                                      sizeof(fragment_entry), &start_block,
++                                      &offset))
++                      goto out;
++
++      *fragment_start_block = fragment_entry.start_block;
++      *fragment_size = fragment_entry.size;
++
++      return 1;
++
++out:
++      return 0;
++}
++
++
++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct
++                                      squashfs_fragment_cache *fragment)
++{
++      down(&msblk->fragment_mutex);
++      fragment->locked --;
++      wake_up(&msblk->fragment_wait_queue);
++      up(&msblk->fragment_mutex);
++}
++
++
++SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block
++                                      *s, long long start_block,
++                                      int length)
++{
++      int i, n;
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++
++      while ( 1 ) {
++              down(&msblk->fragment_mutex);
++
++              for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
++                              msblk->fragment[i].block != start_block; i++);
++
++              if (i == SQUASHFS_CACHED_FRAGMENTS) {
++                      for (i = msblk->next_fragment, n =
++                              SQUASHFS_CACHED_FRAGMENTS; n &&
++                              msblk->fragment[i].locked; n--, i = (i + 1) %
++                              SQUASHFS_CACHED_FRAGMENTS);
++
++                      if (n == 0) {
++                              wait_queue_t wait;
++
++                              init_waitqueue_entry(&wait, current);
++                              add_wait_queue(&msblk->fragment_wait_queue,
++                                                                      &wait);
++                              set_current_state(TASK_UNINTERRUPTIBLE);
++                              up(&msblk->fragment_mutex);
++                              schedule();
++                              set_current_state(TASK_RUNNING);
++                              remove_wait_queue(&msblk->fragment_wait_queue,
++                                                                      &wait);
++                              continue;
++                      }
++                      msblk->next_fragment = (msblk->next_fragment + 1) %
++                              SQUASHFS_CACHED_FRAGMENTS;
++
++                      if (msblk->fragment[i].data == NULL)
++                              if (!(msblk->fragment[i].data = SQUASHFS_ALLOC
++                                              (SQUASHFS_FILE_MAX_SIZE))) {
++                                      ERROR("Failed to allocate fragment "
++                                                      "cache block\n");
++                                      up(&msblk->fragment_mutex);
++                                      goto out;
++                              }
++
++                      msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
++                      msblk->fragment[i].locked = 1;
++                      up(&msblk->fragment_mutex);
++
++                      if (!(msblk->fragment[i].length = squashfs_read_data(s,
++                                              msblk->fragment[i].data,
++                                              start_block, length, NULL))) {
++                              ERROR("Unable to read fragment cache block "
++                                                      "[%llx]\n", start_block);
++                              msblk->fragment[i].locked = 0;
++                              goto out;
++                      }
++
++                      msblk->fragment[i].block = start_block;
++                      TRACE("New fragment %d, start block %lld, locked %d\n",
++                                              i, msblk->fragment[i].block,
++                                              msblk->fragment[i].locked);
++                      break;
++              }
++
++              msblk->fragment[i].locked++;
++              up(&msblk->fragment_mutex);
++              TRACE("Got fragment %d, start block %lld, locked %d\n", i,
++                                              msblk->fragment[i].block,
++                                              msblk->fragment[i].locked);
++              break;
++      }
++
++      return &msblk->fragment[i];
++
++out:
++      return NULL;
++}
++
++
++static struct inode *squashfs_new_inode(struct super_block *s,
++              struct squashfs_base_inode_header *inodeb)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct inode *i = new_inode(s);
++
++      if (i) {
++              i->i_ino = inodeb->inode_number;
++              i->i_mtime.tv_sec = inodeb->mtime;
++              i->i_atime.tv_sec = inodeb->mtime;
++              i->i_ctime.tv_sec = inodeb->mtime;
++              i->i_uid = msblk->uid[inodeb->uid];
++              i->i_mode = inodeb->mode;
++              i->i_size = 0;
++              if (inodeb->guid == SQUASHFS_GUIDS)
++                      i->i_gid = i->i_uid;
++              else
++                      i->i_gid = msblk->guid[inodeb->guid];
++      }
++
++      return i;
++}
++
++
++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode)
++{
++      struct inode *i;
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      long long block = SQUASHFS_INODE_BLK(inode) +
++              sblk->inode_table_start;
++      unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
++      long long next_block;
++      unsigned int next_offset;
++      union squashfs_inode_header id, sid;
++      struct squashfs_base_inode_header *inodeb = &id.base,
++                                        *sinodeb = &sid.base;
++
++      TRACE("Entered squashfs_iget\n");
++
++      if (msblk->swap) {
++              if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
++                                      offset, sizeof(*sinodeb), &next_block,
++                                      &next_offset))
++                      goto failed_read;
++              SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb,
++                                      sizeof(*sinodeb));
++      } else
++              if (!squashfs_get_cached_block(s, (char *) inodeb, block,
++                                      offset, sizeof(*inodeb), &next_block,
++                                      &next_offset))
++                      goto failed_read;
++
++      switch(inodeb->inode_type) {
++              case SQUASHFS_FILE_TYPE: {
++                      unsigned int frag_size;
++                      long long frag_blk;
++                      struct squashfs_reg_inode_header *inodep = &id.reg;
++                      struct squashfs_reg_inode_header *sinodep = &sid.reg;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      frag_blk = SQUASHFS_INVALID_BLK;
++                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++                                      !get_fragment_location(s,
++                                      inodep->fragment, &frag_blk, &frag_size))
++                              goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb)) == NULL)
++                              goto failed_read1;
++
++                      i->i_nlink = 1;
++                      i->i_size = inodep->file_size;
++                      i->i_fop = &generic_ro_fops;
++                      i->i_mode |= S_IFREG;
++                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++                      SQUASHFS_I(i)->start_block = inodep->start_block;
++                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++                      SQUASHFS_I(i)->offset = next_offset;
++                      if (sblk->block_size > 4096)
++                              i->i_data.a_ops = &squashfs_aops;
++                      else
++                              i->i_data.a_ops = &squashfs_aops_4K;
++
++                      TRACE("File inode %x:%x, start_block %llx, "
++                                      "block_list_start %llx, offset %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      inodep->start_block, next_block,
++                                      next_offset);
++                      break;
++              }
++              case SQUASHFS_LREG_TYPE: {
++                      unsigned int frag_size;
++                      long long frag_blk;
++                      struct squashfs_lreg_inode_header *inodep = &id.lreg;
++                      struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      frag_blk = SQUASHFS_INVALID_BLK;
++                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++                                      !get_fragment_location(s,
++                                      inodep->fragment, &frag_blk, &frag_size))
++                              goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb)) == NULL)
++                              goto failed_read1;
++
++                      i->i_nlink = inodep->nlink;
++                      i->i_size = inodep->file_size;
++                      i->i_fop = &generic_ro_fops;
++                      i->i_mode |= S_IFREG;
++                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++                      SQUASHFS_I(i)->start_block = inodep->start_block;
++                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++                      SQUASHFS_I(i)->offset = next_offset;
++                      if (sblk->block_size > 4096)
++                              i->i_data.a_ops = &squashfs_aops;
++                      else
++                              i->i_data.a_ops = &squashfs_aops_4K;
++
++                      TRACE("File inode %x:%x, start_block %llx, "
++                                      "block_list_start %llx, offset %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      inodep->start_block, next_block,
++                                      next_offset);
++                      break;
++              }
++              case SQUASHFS_DIR_TYPE: {
++                      struct squashfs_dir_inode_header *inodep = &id.dir;
++                      struct squashfs_dir_inode_header *sinodep = &sid.dir;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb)) == NULL)
++                              goto failed_read1;
++
++                      i->i_nlink = inodep->nlink;
++                      i->i_size = inodep->file_size;
++                      i->i_op = &squashfs_dir_inode_ops;
++                      i->i_fop = &squashfs_dir_ops;
++                      i->i_mode |= S_IFDIR;
++                      SQUASHFS_I(i)->start_block = inodep->start_block;
++                      SQUASHFS_I(i)->offset = inodep->offset;
++                      SQUASHFS_I(i)->u.s2.directory_index_count = 0;
++                      SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
++
++                      TRACE("Directory inode %x:%x, start_block %x, offset "
++                                      "%x\n", SQUASHFS_INODE_BLK(inode),
++                                      offset, inodep->start_block,
++                                      inodep->offset);
++                      break;
++              }
++              case SQUASHFS_LDIR_TYPE: {
++                      struct squashfs_ldir_inode_header *inodep = &id.ldir;
++                      struct squashfs_ldir_inode_header *sinodep = &sid.ldir;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep,
++                                              sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb)) == NULL)
++                              goto failed_read1;
++
++                      i->i_nlink = inodep->nlink;
++                      i->i_size = inodep->file_size;
++                      i->i_op = &squashfs_dir_inode_ops;
++                      i->i_fop = &squashfs_dir_ops;
++                      i->i_mode |= S_IFDIR;
++                      SQUASHFS_I(i)->start_block = inodep->start_block;
++                      SQUASHFS_I(i)->offset = inodep->offset;
++                      SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
++                      SQUASHFS_I(i)->u.s2.directory_index_offset =
++                                                              next_offset;
++                      SQUASHFS_I(i)->u.s2.directory_index_count =
++                                                              inodep->i_count;
++                      SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
++
++                      TRACE("Long directory inode %x:%x, start_block %x, "
++                                      "offset %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      inodep->start_block, inodep->offset);
++                      break;
++              }
++              case SQUASHFS_SYMLINK_TYPE: {
++                      struct squashfs_symlink_inode_header *inodep =
++                                                              &id.symlink;
++                      struct squashfs_symlink_inode_header *sinodep =
++                                                              &sid.symlink;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep,
++                                                              sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb)) == NULL)
++                              goto failed_read1;
++
++                      i->i_nlink = inodep->nlink;
++                      i->i_size = inodep->symlink_size;
++                      i->i_op = &page_symlink_inode_operations;
++                      i->i_data.a_ops = &squashfs_symlink_aops;
++                      i->i_mode |= S_IFLNK;
++                      SQUASHFS_I(i)->start_block = next_block;
++                      SQUASHFS_I(i)->offset = next_offset;
++
++                      TRACE("Symbolic link inode %x:%x, start_block %llx, "
++                                      "offset %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      next_block, next_offset);
++                      break;
++               }
++               case SQUASHFS_BLKDEV_TYPE:
++               case SQUASHFS_CHRDEV_TYPE: {
++                      struct squashfs_dev_inode_header *inodep = &id.dev;
++                      struct squashfs_dev_inode_header *sinodep = &sid.dev;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if ((i = squashfs_new_inode(s, inodeb)) == NULL)
++                              goto failed_read1;
++
++                      i->i_nlink = inodep->nlink;
++                      i->i_mode |= (inodeb->inode_type ==
++                                      SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
++                                      S_IFBLK;
++                      init_special_inode(i, i->i_mode,
++                                      old_decode_dev(inodep->rdev));
++
++                      TRACE("Device inode %x:%x, rdev %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      inodep->rdev);
++                      break;
++               }
++               case SQUASHFS_FIFO_TYPE:
++               case SQUASHFS_SOCKET_TYPE: {
++                      struct squashfs_ipc_inode_header *inodep = &id.ipc;
++                      struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if ((i = squashfs_new_inode(s, inodeb)) == NULL)
++                              goto failed_read1;
++
++                      i->i_nlink = inodep->nlink;
++                      i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
++                                                      ? S_IFIFO : S_IFSOCK;
++                      init_special_inode(i, i->i_mode, 0);
++                      break;
++               }
++               default:
++                      ERROR("Unknown inode type %d in squashfs_iget!\n",
++                                      inodeb->inode_type);
++                      goto failed_read1;
++      }
++
++      insert_inode_hash(i);
++      return i;
++
++failed_read:
++      ERROR("Unable to read inode [%llx:%x]\n", block, offset);
++
++failed_read1:
++      return NULL;
++}
++
++
++static int read_fragment_index_table(struct super_block *s)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++
++      /* Allocate fragment index table */
++      if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES
++                                      (sblk->fragments), GFP_KERNEL))) {
++              ERROR("Failed to allocate uid/gid table\n");
++              return 0;
++      }
++
++      if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) &&
++                                      !squashfs_read_data(s, (char *)
++                                      msblk->fragment_index,
++                                      sblk->fragment_table_start,
++                                      SQUASHFS_FRAGMENT_INDEX_BYTES
++                                      (sblk->fragments) |
++                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
++              ERROR("unable to read fragment index table\n");
++              return 0;
++      }
++
++      if (msblk->swap) {
++              int i;
++              long long fragment;
++
++              for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments);
++                                                                      i++) {
++                      SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
++                                              &msblk->fragment_index[i], 1);
++                      msblk->fragment_index[i] = fragment;
++              }
++      }
++
++      return 1;
++}
++
++
++static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
++{
++      struct squashfs_super_block *sblk = &msblk->sblk;
++
++      msblk->iget = squashfs_iget;
++      msblk->read_blocklist = read_blocklist;
++      msblk->read_fragment_index_table = read_fragment_index_table;
++
++      if (sblk->s_major == 1) {
++              if (!squashfs_1_0_supported(msblk)) {
++                      SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
++                              "are unsupported\n");
++                      SERROR("Please recompile with "
++                              "Squashfs 1.0 support enabled\n");
++                      return 0;
++              }
++      } else if (sblk->s_major == 2) {
++              if (!squashfs_2_0_supported(msblk)) {
++                      SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
++                              "are unsupported\n");
++                      SERROR("Please recompile with "
++                              "Squashfs 2.0 support enabled\n");
++                      return 0;
++              }
++      } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
++                      SQUASHFS_MINOR) {
++              SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
++                              "filesystem\n", sblk->s_major, sblk->s_minor);
++              SERROR("Please update your kernel\n");
++              return 0;
++      }
++
++      return 1;
++}
++
++
++static int squashfs_fill_super(struct super_block *s, void *data, int silent)
++{
++      struct squashfs_sb_info *msblk;
++      struct squashfs_super_block *sblk;
++      int i;
++      char b[BDEVNAME_SIZE];
++      struct inode *root;
++
++      TRACE("Entered squashfs_read_superblock\n");
++
++      if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info),
++                                              GFP_KERNEL))) {
++              ERROR("Failed to allocate superblock\n");
++              goto failure;
++      }
++      memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info));
++      msblk = s->s_fs_info;
++      sblk = &msblk->sblk;
++
++      msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE);
++      msblk->devblksize_log2 = ffz(~msblk->devblksize);
++
++      init_MUTEX(&msblk->read_data_mutex);
++      init_MUTEX(&msblk->read_page_mutex);
++      init_MUTEX(&msblk->block_cache_mutex);
++      init_MUTEX(&msblk->fragment_mutex);
++      init_MUTEX(&msblk->meta_index_mutex);
++
++      init_waitqueue_head(&msblk->waitq);
++      init_waitqueue_head(&msblk->fragment_wait_queue);
++
++      if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
++                                      sizeof(struct squashfs_super_block) |
++                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
++              SERROR("unable to read superblock\n");
++              goto failed_mount;
++      }
++
++      /* Check it is a SQUASHFS superblock */
++      msblk->swap = 0;
++      if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
++              if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
++                      struct squashfs_super_block ssblk;
++
++                      WARNING("Mounting a different endian SQUASHFS "
++                              "filesystem on %s\n", bdevname(s->s_bdev, b));
++
++                      SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
++                      memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
++                      msblk->swap = 1;
++              } else  {
++                      SERROR("Can't find a SQUASHFS superblock on %s\n",
++                                                      bdevname(s->s_bdev, b));
++                      goto failed_mount;
++              }
++      }
++
++      /* Check the MAJOR & MINOR versions */
++      if(!supported_squashfs_filesystem(msblk, silent))
++              goto failed_mount;
++
++      TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
++      TRACE("Inodes are %scompressed\n",
++                                      SQUASHFS_UNCOMPRESSED_INODES
++                                      (sblk->flags) ? "un" : "");
++      TRACE("Data is %scompressed\n",
++                                      SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
++                                      ? "un" : "");
++      TRACE("Check data is %s present in the filesystem\n",
++                                      SQUASHFS_CHECK_DATA(sblk->flags) ?
++                                      "" : "not");
++      TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
++      TRACE("Block size %d\n", sblk->block_size);
++      TRACE("Number of inodes %d\n", sblk->inodes);
++      if (sblk->s_major > 1)
++              TRACE("Number of fragments %d\n", sblk->fragments);
++      TRACE("Number of uids %d\n", sblk->no_uids);
++      TRACE("Number of gids %d\n", sblk->no_guids);
++      TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
++      TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
++      if (sblk->s_major > 1)
++              TRACE("sblk->fragment_table_start %llx\n",
++                                      sblk->fragment_table_start);
++      TRACE("sblk->uid_start %llx\n", sblk->uid_start);
++
++      s->s_flags |= MS_RDONLY;
++      s->s_op = &squashfs_ops;
++
++      /* Init inode_table block pointer array */
++      if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
++                                      SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
++              ERROR("Failed to allocate block cache\n");
++              goto failed_mount;
++      }
++
++      for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++              msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
++
++      msblk->next_cache = 0;
++
++      /* Allocate read_data block */
++      msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ?
++                                      SQUASHFS_METADATA_SIZE :
++                                      sblk->block_size;
++
++      if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) {
++              ERROR("Failed to allocate read_data block\n");
++              goto failed_mount;
++      }
++
++      /* Allocate read_page block */
++      if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) {
++              ERROR("Failed to allocate read_page block\n");
++              goto failed_mount;
++      }
++
++      /* Allocate uid and gid tables */
++      if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
++                                      sizeof(unsigned int), GFP_KERNEL))) {
++              ERROR("Failed to allocate uid/gid table\n");
++              goto failed_mount;
++      }
++      msblk->guid = msblk->uid + sblk->no_uids;
++
++      if (msblk->swap) {
++              unsigned int suid[sblk->no_uids + sblk->no_guids];
++
++              if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
++                                      ((sblk->no_uids + sblk->no_guids) *
++                                       sizeof(unsigned int)) |
++                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
++                      ERROR("unable to read uid/gid table\n");
++                      goto failed_mount;
++              }
++
++              SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
++                      sblk->no_guids), (sizeof(unsigned int) * 8));
++      } else
++              if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
++                                      ((sblk->no_uids + sblk->no_guids) *
++                                       sizeof(unsigned int)) |
++                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
++                      ERROR("unable to read uid/gid table\n");
++                      goto failed_mount;
++              }
++
++
++      if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
++              goto allocate_root;
++
++      if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) *
++                              SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) {
++              ERROR("Failed to allocate fragment block cache\n");
++              goto failed_mount;
++      }
++
++      for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
++              msblk->fragment[i].locked = 0;
++              msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
++              msblk->fragment[i].data = NULL;
++      }
++
++      msblk->next_fragment = 0;
++
++      /* Allocate fragment index table */
++      if (msblk->read_fragment_index_table(s) == 0)
++              goto failed_mount;
++
++allocate_root:
++      if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL)
++              goto failed_mount;
++
++      if ((s->s_root = d_alloc_root(root)) == NULL) {
++              ERROR("Root inode create failed\n");
++              iput(root);
++              goto failed_mount;
++      }
++
++      TRACE("Leaving squashfs_read_super\n");
++      return 0;
++
++failed_mount:
++      kfree(msblk->fragment_index);
++      kfree(msblk->fragment);
++      kfree(msblk->uid);
++      kfree(msblk->read_page);
++      kfree(msblk->read_data);
++      kfree(msblk->block_cache);
++      kfree(msblk->fragment_index_2);
++      kfree(s->s_fs_info);
++      s->s_fs_info = NULL;
++      return -EINVAL;
++
++failure:
++      return -ENOMEM;
++}
++
++
++static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
++{
++      struct squashfs_sb_info *msblk = dentry->d_inode->i_sb->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++
++      TRACE("Entered squashfs_statfs\n");
++
++      buf->f_type = SQUASHFS_MAGIC;
++      buf->f_bsize = sblk->block_size;
++      buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
++      buf->f_bfree = buf->f_bavail = 0;
++      buf->f_files = sblk->inodes;
++      buf->f_ffree = 0;
++      buf->f_namelen = SQUASHFS_NAME_LEN;
++
++      return 0;
++}
++
++
++static int squashfs_symlink_readpage(struct file *file, struct page *page)
++{
++      struct inode *inode = page->mapping->host;
++      int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
++      long long block = SQUASHFS_I(inode)->start_block;
++      int offset = SQUASHFS_I(inode)->offset;
++      void *pageaddr = kmap(page);
++
++      TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
++                              "%llx, offset %x\n", page->index,
++                              SQUASHFS_I(inode)->start_block,
++                              SQUASHFS_I(inode)->offset);
++
++      for (length = 0; length < index; length += bytes) {
++              if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL,
++                              block, offset, PAGE_CACHE_SIZE, &block,
++                              &offset))) {
++                      ERROR("Unable to read symbolic link [%llx:%x]\n", block,
++                                      offset);
++                      goto skip_read;
++              }
++      }
++
++      if (length != index) {
++              ERROR("(squashfs_symlink_readpage) length != index\n");
++              bytes = 0;
++              goto skip_read;
++      }
++
++      bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
++                                      i_size_read(inode) - length;
++
++      if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block,
++                                      offset, bytes, &block, &offset)))
++              ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
++
++skip_read:
++      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++      kunmap(page);
++      SetPageUptodate(page);
++      unlock_page(page);
++
++      return 0;
++}
++
++
++struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
++{
++      struct meta_index *meta = NULL;
++      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++      int i;
++
++      down(&msblk->meta_index_mutex);
++
++      TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
++
++      if(msblk->meta_index == NULL)
++              goto not_allocated;
++
++      for (i = 0; i < SQUASHFS_META_NUMBER; i ++)
++              if (msblk->meta_index[i].inode_number == inode->i_ino &&
++                              msblk->meta_index[i].offset >= offset &&
++                              msblk->meta_index[i].offset <= index &&
++                              msblk->meta_index[i].locked == 0) {
++                      TRACE("locate_meta_index: entry %d, offset %d\n", i,
++                                      msblk->meta_index[i].offset);
++                      meta = &msblk->meta_index[i];
++                      offset = meta->offset;
++              }
++
++      if (meta)
++              meta->locked = 1;
++
++not_allocated:
++      up(&msblk->meta_index_mutex);
++
++      return meta;
++}
++
++
++struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
++{
++      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++      struct meta_index *meta = NULL;
++      int i;
++
++      down(&msblk->meta_index_mutex);
++
++      TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
++
++      if(msblk->meta_index == NULL) {
++              if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) *
++                                      SQUASHFS_META_NUMBER, GFP_KERNEL))) {
++                      ERROR("Failed to allocate meta_index\n");
++                      goto failed;
++              }
++              for(i = 0; i < SQUASHFS_META_NUMBER; i++) {
++                      msblk->meta_index[i].inode_number = 0;
++                      msblk->meta_index[i].locked = 0;
++              }
++              msblk->next_meta_index = 0;
++      }
++
++      for(i = SQUASHFS_META_NUMBER; i &&
++                      msblk->meta_index[msblk->next_meta_index].locked; i --)
++              msblk->next_meta_index = (msblk->next_meta_index + 1) %
++                      SQUASHFS_META_NUMBER;
++
++      if(i == 0) {
++              TRACE("empty_meta_index: failed!\n");
++              goto failed;
++      }
++
++      TRACE("empty_meta_index: returned meta entry %d, %p\n",
++                      msblk->next_meta_index,
++                      &msblk->meta_index[msblk->next_meta_index]);
++
++      meta = &msblk->meta_index[msblk->next_meta_index];
++      msblk->next_meta_index = (msblk->next_meta_index + 1) %
++                      SQUASHFS_META_NUMBER;
++
++      meta->inode_number = inode->i_ino;
++      meta->offset = offset;
++      meta->skip = skip;
++      meta->entries = 0;
++      meta->locked = 1;
++
++failed:
++      up(&msblk->meta_index_mutex);
++      return meta;
++}
++
++
++void release_meta_index(struct inode *inode, struct meta_index *meta)
++{
++      meta->locked = 0;
++}
++
++
++static int read_block_index(struct super_block *s, int blocks, char *block_list,
++              long long *start_block, int *offset)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      unsigned int *block_listp;
++      int block = 0;
++
++      if (msblk->swap) {
++              char sblock_list[blocks << 2];
++
++              if (!squashfs_get_cached_block(s, sblock_list, *start_block,
++                              *offset, blocks << 2, start_block, offset)) {
++                      ERROR("Unable to read block list [%llx:%x]\n",
++                              *start_block, *offset);
++                      goto failure;
++              }
++              SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
++                              ((unsigned int *)sblock_list), blocks);
++      } else
++              if (!squashfs_get_cached_block(s, block_list, *start_block,
++                              *offset, blocks << 2, start_block, offset)) {
++                      ERROR("Unable to read block list [%llx:%x]\n",
++                              *start_block, *offset);
++                      goto failure;
++              }
++
++      for (block_listp = (unsigned int *) block_list; blocks;
++                              block_listp++, blocks --)
++              block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
++
++      return block;
++
++failure:
++      return -1;
++}
++
++
++#define SIZE 256
++
++static inline int calculate_skip(int blocks) {
++      int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
++      return skip >= 7 ? 7 : skip + 1;
++}
++
++
++static int get_meta_index(struct inode *inode, int index,
++              long long *index_block, int *index_offset,
++              long long *data_block, char *block_list)
++{
++      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
++      int offset = 0;
++      struct meta_index *meta;
++      struct meta_entry *meta_entry;
++      long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
++      int cur_offset = SQUASHFS_I(inode)->offset;
++      long long cur_data_block = SQUASHFS_I(inode)->start_block;
++      int i;
++
++      index /= SQUASHFS_META_INDEXES * skip;
++
++      while ( offset < index ) {
++              meta = locate_meta_index(inode, index, offset + 1);
++
++              if (meta == NULL) {
++                      if ((meta = empty_meta_index(inode, offset + 1,
++                                                      skip)) == NULL)
++                              goto all_done;
++              } else {
++                      offset = index < meta->offset + meta->entries ? index :
++                              meta->offset + meta->entries - 1;
++                      meta_entry = &meta->meta_entry[offset - meta->offset];
++                      cur_index_block = meta_entry->index_block + sblk->inode_table_start;
++                      cur_offset = meta_entry->offset;
++                      cur_data_block = meta_entry->data_block;
++                      TRACE("get_meta_index: offset %d, meta->offset %d, "
++                              "meta->entries %d\n", offset, meta->offset,
++                              meta->entries);
++                      TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
++                              " data_block 0x%llx\n", cur_index_block,
++                              cur_offset, cur_data_block);
++              }
++
++              for (i = meta->offset + meta->entries; i <= index &&
++                              i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
++                      int blocks = skip * SQUASHFS_META_INDEXES;
++
++                      while (blocks) {
++                              int block = blocks > (SIZE >> 2) ? (SIZE >> 2) :
++                                      blocks;
++                              int res = read_block_index(inode->i_sb, block,
++                                      block_list, &cur_index_block,
++                                      &cur_offset);
++
++                              if (res == -1)
++                                      goto failed;
++
++                              cur_data_block += res;
++                              blocks -= block;
++                      }
++
++                      meta_entry = &meta->meta_entry[i - meta->offset];
++                      meta_entry->index_block = cur_index_block - sblk->inode_table_start;
++                      meta_entry->offset = cur_offset;
++                      meta_entry->data_block = cur_data_block;
++                      meta->entries ++;
++                      offset ++;
++              }
++
++              TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
++                              meta->offset, meta->entries);
++
++              release_meta_index(inode, meta);
++      }
++
++all_done:
++      *index_block = cur_index_block;
++      *index_offset = cur_offset;
++      *data_block = cur_data_block;
++
++      return offset * SQUASHFS_META_INDEXES * skip;
++
++failed:
++      release_meta_index(inode, meta);
++      return -1;
++}
++
++
++static long long read_blocklist(struct inode *inode, int index,
++                              int readahead_blks, char *block_list,
++                              unsigned short **block_p, unsigned int *bsize)
++{
++      long long block_ptr;
++      int offset;
++      long long block;
++      int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
++              block_list);
++
++      TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
++                     " 0x%x, block 0x%llx\n", res, index, block_ptr, offset,
++                     block);
++
++      if(res == -1)
++              goto failure;
++
++      index -= res;
++
++      while ( index ) {
++              int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
++              int res = read_block_index(inode->i_sb, blocks, block_list,
++                      &block_ptr, &offset);
++              if (res == -1)
++                      goto failure;
++              block += res;
++              index -= blocks;
++      }
++
++      if (read_block_index(inode->i_sb, 1, block_list,
++                      &block_ptr, &offset) == -1)
++              goto failure;
++      *bsize = *((unsigned int *) block_list);
++
++      return block;
++
++failure:
++      return 0;
++}
++
++
++static int squashfs_readpage(struct file *file, struct page *page)
++{
++      struct inode *inode = page->mapping->host;
++      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      unsigned char block_list[SIZE];
++      long long block;
++      unsigned int bsize, i = 0, bytes = 0, byte_offset = 0;
++      int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
++      void *pageaddr;
++      struct squashfs_fragment_cache *fragment = NULL;
++      char *data_ptr = msblk->read_page;
++
++      int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
++      int start_index = page->index & ~mask;
++      int end_index = start_index | mask;
++
++      TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
++                                      page->index,
++                                      SQUASHFS_I(inode)->start_block);
++
++      if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
++                                      PAGE_CACHE_SHIFT))
++              goto skip_read;
++
++      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++                                      || index < (i_size_read(inode) >>
++                                      sblk->block_log)) {
++              if ((block = (msblk->read_blocklist)(inode, index, 1,
++                                      block_list, NULL, &bsize)) == 0)
++                      goto skip_read;
++
++              down(&msblk->read_page_mutex);
++
++              if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
++                                      block, bsize, NULL))) {
++                      ERROR("Unable to read page, block %llx, size %x\n", block,
++                                      bsize);
++                      up(&msblk->read_page_mutex);
++                      goto skip_read;
++              }
++      } else {
++              if ((fragment = get_cached_fragment(inode->i_sb,
++                                      SQUASHFS_I(inode)->
++                                      u.s1.fragment_start_block,
++                                      SQUASHFS_I(inode)->u.s1.fragment_size))
++                                      == NULL) {
++                      ERROR("Unable to read page, block %llx, size %x\n",
++                                      SQUASHFS_I(inode)->
++                                      u.s1.fragment_start_block,
++                                      (int) SQUASHFS_I(inode)->
++                                      u.s1.fragment_size);
++                      goto skip_read;
++              }
++              bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
++                                      (i_size_read(inode) & (sblk->block_size
++                                      - 1));
++              byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
++              data_ptr = fragment->data;
++      }
++
++      for (i = start_index; i <= end_index && byte_offset < bytes;
++                                      i++, byte_offset += PAGE_CACHE_SIZE) {
++              struct page *push_page;
++              int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
++                                      PAGE_CACHE_SIZE : bytes - byte_offset;
++
++              TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
++                                      bytes, i, byte_offset, available_bytes);
++
++              if (i == page->index)  {
++                      pageaddr = kmap_atomic(page, KM_USER0);
++                      memcpy(pageaddr, data_ptr + byte_offset,
++                                      available_bytes);
++                      memset(pageaddr + available_bytes, 0,
++                                      PAGE_CACHE_SIZE - available_bytes);
++                      kunmap_atomic(pageaddr, KM_USER0);
++                      flush_dcache_page(page);
++                      SetPageUptodate(page);
++                      unlock_page(page);
++              } else if ((push_page =
++                              grab_cache_page_nowait(page->mapping, i))) {
++                      pageaddr = kmap_atomic(push_page, KM_USER0);
++
++                      memcpy(pageaddr, data_ptr + byte_offset,
++                                      available_bytes);
++                      memset(pageaddr + available_bytes, 0,
++                                      PAGE_CACHE_SIZE - available_bytes);
++                      kunmap_atomic(pageaddr, KM_USER0);
++                      flush_dcache_page(push_page);
++                      SetPageUptodate(push_page);
++                      unlock_page(push_page);
++                      page_cache_release(push_page);
++              }
++      }
++
++      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++                                      || index < (i_size_read(inode) >>
++                                      sblk->block_log))
++              up(&msblk->read_page_mutex);
++      else
++              release_cached_fragment(msblk, fragment);
++
++      return 0;
++
++skip_read:
++      pageaddr = kmap_atomic(page, KM_USER0);
++      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++      kunmap_atomic(pageaddr, KM_USER0);
++      flush_dcache_page(page);
++      SetPageUptodate(page);
++      unlock_page(page);
++
++      return 0;
++}
++
++
++static int squashfs_readpage4K(struct file *file, struct page *page)
++{
++      struct inode *inode = page->mapping->host;
++      struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      unsigned char block_list[SIZE];
++      long long block;
++      unsigned int bsize, bytes = 0;
++      void *pageaddr;
++
++      TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n",
++                                      page->index,
++                                      SQUASHFS_I(inode)->start_block);
++
++      if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
++                                      PAGE_CACHE_SHIFT)) {
++              pageaddr = kmap_atomic(page, KM_USER0);
++              goto skip_read;
++      }
++
++      if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++                                      || page->index < (i_size_read(inode) >>
++                                      sblk->block_log)) {
++              block = (msblk->read_blocklist)(inode, page->index, 1,
++                                      block_list, NULL, &bsize);
++
++              down(&msblk->read_page_mutex);
++              bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
++                                      bsize, NULL);
++              pageaddr = kmap_atomic(page, KM_USER0);
++              if (bytes)
++                      memcpy(pageaddr, msblk->read_page, bytes);
++              else
++                      ERROR("Unable to read page, block %llx, size %x\n",
++                                      block, bsize);
++              up(&msblk->read_page_mutex);
++      } else {
++              struct squashfs_fragment_cache *fragment =
++                      get_cached_fragment(inode->i_sb,
++                                      SQUASHFS_I(inode)->
++                                      u.s1.fragment_start_block,
++                                      SQUASHFS_I(inode)-> u.s1.fragment_size);
++              pageaddr = kmap_atomic(page, KM_USER0);
++              if (fragment) {
++                      bytes = i_size_read(inode) & (sblk->block_size - 1);
++                      memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
++                                      u.s1.fragment_offset, bytes);
++                      release_cached_fragment(msblk, fragment);
++              } else
++                      ERROR("Unable to read page, block %llx, size %x\n",
++                                      SQUASHFS_I(inode)->
++                                      u.s1.fragment_start_block, (int)
++                                      SQUASHFS_I(inode)-> u.s1.fragment_size);
++      }
++
++skip_read:
++      memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++      kunmap_atomic(pageaddr, KM_USER0);
++      flush_dcache_page(page);
++      SetPageUptodate(page);
++      unlock_page(page);
++
++      return 0;
++}
++
++
++static int get_dir_index_using_offset(struct super_block *s, long long
++                              *next_block, unsigned int *next_offset,
++                              long long index_start,
++                              unsigned int index_offset, int i_count,
++                              long long f_pos)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      int i, length = 0;
++      struct squashfs_dir_index index;
++
++      TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
++                                      i_count, (unsigned int) f_pos);
++
++      f_pos =- 3;
++      if (f_pos == 0)
++              goto finish;
++
++      for (i = 0; i < i_count; i++) {
++              if (msblk->swap) {
++                      struct squashfs_dir_index sindex;
++                      squashfs_get_cached_block(s, (char *) &sindex,
++                                      index_start, index_offset,
++                                      sizeof(sindex), &index_start,
++                                      &index_offset);
++                      SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
++              } else
++                      squashfs_get_cached_block(s, (char *) &index,
++                                      index_start, index_offset,
++                                      sizeof(index), &index_start,
++                                      &index_offset);
++
++              if (index.index > f_pos)
++                      break;
++
++              squashfs_get_cached_block(s, NULL, index_start, index_offset,
++                                      index.size + 1, &index_start,
++                                      &index_offset);
++
++              length = index.index;
++              *next_block = index.start_block + sblk->directory_table_start;
++      }
++
++      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++
++finish:
++      return length + 3;
++}
++
++
++static int get_dir_index_using_name(struct super_block *s, long long
++                              *next_block, unsigned int *next_offset,
++                              long long index_start,
++                              unsigned int index_offset, int i_count,
++                              const char *name, int size)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      int i, length = 0;
++      char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1];
++      struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer;
++      char str[SQUASHFS_NAME_LEN + 1];
++
++      TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
++
++      strncpy(str, name, size);
++      str[size] = '\0';
++
++      for (i = 0; i < i_count; i++) {
++              if (msblk->swap) {
++                      struct squashfs_dir_index sindex;
++                      squashfs_get_cached_block(s, (char *) &sindex,
++                                      index_start, index_offset,
++                                      sizeof(sindex), &index_start,
++                                      &index_offset);
++                      SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
++              } else
++                      squashfs_get_cached_block(s, (char *) index,
++                                      index_start, index_offset,
++                                      sizeof(struct squashfs_dir_index),
++                                      &index_start, &index_offset);
++
++              squashfs_get_cached_block(s, index->name, index_start,
++                                      index_offset, index->size + 1,
++                                      &index_start, &index_offset);
++
++              index->name[index->size + 1] = '\0';
++
++              if (strcmp(index->name, str) > 0)
++                      break;
++
++              length = index->index;
++              *next_block = index->start_block + sblk->directory_table_start;
++      }
++
++      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++      return length + 3;
++}
++
++
++static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
++{
++      struct inode *i = file->f_dentry->d_inode;
++      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      long long next_block = SQUASHFS_I(i)->start_block +
++              sblk->directory_table_start;
++      int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0,
++              dir_count;
++      struct squashfs_dir_header dirh;
++      char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1];
++      struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
++
++      TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
++
++      while(file->f_pos < 3) {
++              char *name;
++              int size, i_ino;
++
++              if(file->f_pos == 0) {
++                      name = ".";
++                      size = 1;
++                      i_ino = i->i_ino;
++              } else {
++                      name = "..";
++                      size = 2;
++                      i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
++              }
++              TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
++                              (unsigned int) dirent, name, size, (int)
++                              file->f_pos, i_ino,
++                              squashfs_filetype_table[1]);
++
++              if (filldir(dirent, name, size,
++                              file->f_pos, i_ino,
++                              squashfs_filetype_table[1]) < 0) {
++                              TRACE("Filldir returned less than 0\n");
++                              goto finish;
++              }
++              file->f_pos += size;
++              dirs_read++;
++      }
++
++      length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
++                              SQUASHFS_I(i)->u.s2.directory_index_start,
++                              SQUASHFS_I(i)->u.s2.directory_index_offset,
++                              SQUASHFS_I(i)->u.s2.directory_index_count,
++                              file->f_pos);
++
++      while (length < i_size_read(i)) {
++              /* read directory header */
++              if (msblk->swap) {
++                      struct squashfs_dir_header sdirh;
++
++                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++                                      next_block, next_offset, sizeof(sdirh),
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += sizeof(sdirh);
++                      SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
++              } else {
++                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++                                      next_block, next_offset, sizeof(dirh),
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += sizeof(dirh);
++              }
++
++              dir_count = dirh.count + 1;
++              while (dir_count--) {
++                      if (msblk->swap) {
++                              struct squashfs_dir_entry sdire;
++                              if (!squashfs_get_cached_block(i->i_sb, (char *)
++                                              &sdire, next_block, next_offset,
++                                              sizeof(sdire), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                              length += sizeof(sdire);
++                              SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
++                      } else {
++                              if (!squashfs_get_cached_block(i->i_sb, (char *)
++                                              dire, next_block, next_offset,
++                                              sizeof(*dire), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                              length += sizeof(*dire);
++                      }
++
++                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
++                                              next_block, next_offset,
++                                              dire->size + 1, &next_block,
++                                              &next_offset))
++                              goto failed_read;
++
++                      length += dire->size + 1;
++
++                      if (file->f_pos >= length)
++                              continue;
++
++                      dire->name[dire->size + 1] = '\0';
++
++                      TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n",
++                                      (unsigned int) dirent, dire->name,
++                                      dire->size + 1, (int) file->f_pos,
++                                      dirh.start_block, dire->offset,
++                                      dirh.inode_number + dire->inode_number,
++                                      squashfs_filetype_table[dire->type]);
++
++                      if (filldir(dirent, dire->name, dire->size + 1,
++                                      file->f_pos,
++                                      dirh.inode_number + dire->inode_number,
++                                      squashfs_filetype_table[dire->type])
++                                      < 0) {
++                              TRACE("Filldir returned less than 0\n");
++                              goto finish;
++                      }
++                      file->f_pos = length;
++                      dirs_read++;
++              }
++      }
++
++finish:
++      return dirs_read;
++
++failed_read:
++      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++              next_offset);
++      return 0;
++}
++
++
++static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry,
++                              struct nameidata *nd)
++{
++      const unsigned char *name = dentry->d_name.name;
++      int len = dentry->d_name.len;
++      struct inode *inode = NULL;
++      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      long long next_block = SQUASHFS_I(i)->start_block +
++                              sblk->directory_table_start;
++      int next_offset = SQUASHFS_I(i)->offset, length = 0,
++                              dir_count;
++      struct squashfs_dir_header dirh;
++      char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN];
++      struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
++
++      TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
++
++      if (len > SQUASHFS_NAME_LEN)
++              goto exit_loop;
++
++      length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
++                              SQUASHFS_I(i)->u.s2.directory_index_start,
++                              SQUASHFS_I(i)->u.s2.directory_index_offset,
++                              SQUASHFS_I(i)->u.s2.directory_index_count, name,
++                              len);
++
++      while (length < i_size_read(i)) {
++              /* read directory header */
++              if (msblk->swap) {
++                      struct squashfs_dir_header sdirh;
++                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++                                      next_block, next_offset, sizeof(sdirh),
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += sizeof(sdirh);
++                      SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
++              } else {
++                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++                                      next_block, next_offset, sizeof(dirh),
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += sizeof(dirh);
++              }
++
++              dir_count = dirh.count + 1;
++              while (dir_count--) {
++                      if (msblk->swap) {
++                              struct squashfs_dir_entry sdire;
++                              if (!squashfs_get_cached_block(i->i_sb, (char *)
++                                              &sdire, next_block,next_offset,
++                                              sizeof(sdire), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                              length += sizeof(sdire);
++                              SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
++                      } else {
++                              if (!squashfs_get_cached_block(i->i_sb, (char *)
++                                              dire, next_block,next_offset,
++                                              sizeof(*dire), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                              length += sizeof(*dire);
++                      }
++
++                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
++                                      next_block, next_offset, dire->size + 1,
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += dire->size + 1;
++
++                      if (name[0] < dire->name[0])
++                              goto exit_loop;
++
++                      if ((len == dire->size + 1) && !strncmp(name,
++                                              dire->name, len)) {
++                              squashfs_inode_t ino =
++                                      SQUASHFS_MKINODE(dirh.start_block,
++                                      dire->offset);
++
++                              TRACE("calling squashfs_iget for directory "
++                                      "entry %s, inode %x:%x, %d\n", name,
++                                      dirh.start_block, dire->offset,
++                                      dirh.inode_number + dire->inode_number);
++
++                              inode = (msblk->iget)(i->i_sb, ino);
++
++                              goto exit_loop;
++                      }
++              }
++      }
++
++exit_loop:
++      d_add(dentry, inode);
++      return ERR_PTR(0);
++
++failed_read:
++      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++              next_offset);
++      goto exit_loop;
++}
++
++
++static void squashfs_put_super(struct super_block *s)
++{
++      int i;
++
++      if (s->s_fs_info) {
++              struct squashfs_sb_info *sbi = s->s_fs_info;
++              if (sbi->block_cache)
++                      for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++                              if (sbi->block_cache[i].block !=
++                                                      SQUASHFS_INVALID_BLK)
++                                      kfree(sbi->block_cache[i].data);
++              if (sbi->fragment)
++                      for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++)
++                              SQUASHFS_FREE(sbi->fragment[i].data);
++              kfree(sbi->fragment);
++              kfree(sbi->block_cache);
++              kfree(sbi->read_data);
++              kfree(sbi->read_page);
++              kfree(sbi->uid);
++              kfree(sbi->fragment_index);
++              kfree(sbi->fragment_index_2);
++              kfree(sbi->meta_index);
++              kfree(s->s_fs_info);
++              s->s_fs_info = NULL;
++      }
++}
++
++
++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 int __init init_squashfs_fs(void)
++{
++      int err = init_inodecache();
++      if (err)
++              goto out;
++
++      printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) "
++              "Phillip Lougher\n");
++
++      if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
++              ERROR("Failed to allocate zlib workspace\n");
++              destroy_inodecache();
++              err = -ENOMEM;
++              goto out;
++      }
++
++      if ((err = register_filesystem(&squashfs_fs_type))) {
++              vfree(stream.workspace);
++              destroy_inodecache();
++      }
++
++out:
++      return err;
++}
++
++
++static void __exit exit_squashfs_fs(void)
++{
++      vfree(stream.workspace);
++      unregister_filesystem(&squashfs_fs_type);
++      destroy_inodecache();
++}
++
++
++static struct kmem_cache * squashfs_inode_cachep;
++
++
++static struct inode *squashfs_alloc_inode(struct super_block *sb)
++{
++      struct squashfs_inode_info *ei;
++      ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
++      if (!ei)
++              return NULL;
++      return &ei->vfs_inode;
++}
++
++
++static void squashfs_destroy_inode(struct inode *inode)
++{
++      kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode));
++}
++
++
++static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
++{
++      struct squashfs_inode_info *ei = foo;
++
++      if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++                                                      SLAB_CTOR_CONSTRUCTOR)
++              inode_init_once(&ei->vfs_inode);
++}
++
++
++static int __init init_inodecache(void)
++{
++      squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
++           sizeof(struct squashfs_inode_info),
++           0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
++           init_once, NULL);
++      if (squashfs_inode_cachep == NULL)
++              return -ENOMEM;
++      return 0;
++}
++
++
++static void destroy_inodecache(void)
++{
++      kmem_cache_destroy(squashfs_inode_cachep);
++}
++
++
++module_init(init_squashfs_fs);
++module_exit(exit_squashfs_fs);
++MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem");
++MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.21.7/fs/squashfs/Makefile
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/fs/squashfs/Makefile
+@@ -0,0 +1,7 @@
++#
++# Makefile for the linux squashfs routines.
++#
++
++obj-$(CONFIG_SQUASHFS) += squashfs.o
++squashfs-y += inode.o
++squashfs-y += squashfs2_0.o
+Index: linux-2.6.21.7/fs/squashfs/squashfs2_0.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/fs/squashfs/squashfs2_0.c
+@@ -0,0 +1,758 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs2_0.c
++ */
++
++#include <linux/types.h>
++#include <linux/squashfs_fs.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/smp_lock.h>
++#include <linux/slab.h>
++#include <linux/squashfs_fs_sb.h>
++#include <linux/squashfs_fs_i.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include <linux/init.h>
++#include <linux/dcache.h>
++#include <linux/wait.h>
++#include <linux/zlib.h>
++#include <linux/blkdev.h>
++#include <linux/vmalloc.h>
++#include <asm/uaccess.h>
++#include <asm/semaphore.h>
++
++#include "squashfs.h"
++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
++static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *,
++                              struct nameidata *);
++
++static struct file_operations squashfs_dir_ops_2 = {
++      .read = generic_read_dir,
++      .readdir = squashfs_readdir_2
++};
++
++static struct inode_operations squashfs_dir_inode_ops_2 = {
++      .lookup = squashfs_lookup_2
++};
++
++static unsigned char squashfs_filetype_table[] = {
++      DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
++};
++
++static int read_fragment_index_table_2(struct super_block *s)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++
++      if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
++                                      (sblk->fragments), GFP_KERNEL))) {
++              ERROR("Failed to allocate uid/gid table\n");
++              return 0;
++      }
++
++      if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
++                                      !squashfs_read_data(s, (char *)
++                                      msblk->fragment_index_2,
++                                      sblk->fragment_table_start,
++                                      SQUASHFS_FRAGMENT_INDEX_BYTES_2
++                                      (sblk->fragments) |
++                                      SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
++              ERROR("unable to read fragment index table\n");
++              return 0;
++      }
++
++      if (msblk->swap) {
++              int i;
++              unsigned int fragment;
++
++              for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
++                                                                      i++) {
++                      SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
++                                              &msblk->fragment_index_2[i], 1);
++                      msblk->fragment_index_2[i] = fragment;
++              }
++      }
++
++      return 1;
++}
++
++
++static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
++                              long long *fragment_start_block,
++                              unsigned int *fragment_size)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      long long start_block =
++              msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
++      int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
++      struct squashfs_fragment_entry_2 fragment_entry;
++
++      if (msblk->swap) {
++              struct squashfs_fragment_entry_2 sfragment_entry;
++
++              if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
++                                      start_block, offset,
++                                      sizeof(sfragment_entry), &start_block,
++                                      &offset))
++                      goto out;
++              SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
++      } else
++              if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
++                                      start_block, offset,
++                                      sizeof(fragment_entry), &start_block,
++                                      &offset))
++                      goto out;
++
++      *fragment_start_block = fragment_entry.start_block;
++      *fragment_size = fragment_entry.size;
++
++      return 1;
++
++out:
++      return 0;
++}
++
++
++static struct inode *squashfs_new_inode(struct super_block *s,
++              struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      struct inode *i = new_inode(s);
++
++      if (i) {
++              i->i_ino = ino;
++              i->i_mtime.tv_sec = sblk->mkfs_time;
++              i->i_atime.tv_sec = sblk->mkfs_time;
++              i->i_ctime.tv_sec = sblk->mkfs_time;
++              i->i_uid = msblk->uid[inodeb->uid];
++              i->i_mode = inodeb->mode;
++              i->i_nlink = 1;
++              i->i_size = 0;
++              if (inodeb->guid == SQUASHFS_GUIDS)
++                      i->i_gid = i->i_uid;
++              else
++                      i->i_gid = msblk->guid[inodeb->guid];
++      }
++
++      return i;
++}
++
++
++static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode)
++{
++      struct inode *i;
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      unsigned int block = SQUASHFS_INODE_BLK(inode) +
++              sblk->inode_table_start;
++      unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
++      unsigned int ino = SQUASHFS_MK_VFS_INODE(block
++              - sblk->inode_table_start, offset);
++      long long next_block;
++      unsigned int next_offset;
++      union squashfs_inode_header_2 id, sid;
++      struct squashfs_base_inode_header_2 *inodeb = &id.base,
++                                        *sinodeb = &sid.base;
++
++      TRACE("Entered squashfs_iget\n");
++
++      if (msblk->swap) {
++              if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
++                                      offset, sizeof(*sinodeb), &next_block,
++                                      &next_offset))
++                      goto failed_read;
++              SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
++                                      sizeof(*sinodeb));
++      } else
++              if (!squashfs_get_cached_block(s, (char *) inodeb, block,
++                                      offset, sizeof(*inodeb), &next_block,
++                                      &next_offset))
++                      goto failed_read;
++
++      switch(inodeb->inode_type) {
++              case SQUASHFS_FILE_TYPE: {
++                      struct squashfs_reg_inode_header_2 *inodep = &id.reg;
++                      struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
++                      long long frag_blk;
++                      unsigned int frag_size;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      frag_blk = SQUASHFS_INVALID_BLK;
++                      if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++                                      !get_fragment_location_2(s,
++                                      inodep->fragment, &frag_blk, &frag_size))
++                              goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
++                              goto failed_read1;
++
++                      i->i_size = inodep->file_size;
++                      i->i_fop = &generic_ro_fops;
++                      i->i_mode |= S_IFREG;
++                      i->i_mtime.tv_sec = inodep->mtime;
++                      i->i_atime.tv_sec = inodep->mtime;
++                      i->i_ctime.tv_sec = inodep->mtime;
++                      i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++                      i->i_blksize = PAGE_CACHE_SIZE;
++                      SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++                      SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++                      SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++                      SQUASHFS_I(i)->start_block = inodep->start_block;
++                      SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++                      SQUASHFS_I(i)->offset = next_offset;
++                      if (sblk->block_size > 4096)
++                              i->i_data.a_ops = &squashfs_aops;
++                      else
++                              i->i_data.a_ops = &squashfs_aops_4K;
++
++                      TRACE("File inode %x:%x, start_block %x, "
++                                      "block_list_start %llx, offset %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      inodep->start_block, next_block,
++                                      next_offset);
++                      break;
++              }
++              case SQUASHFS_DIR_TYPE: {
++                      struct squashfs_dir_inode_header_2 *inodep = &id.dir;
++                      struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
++                              goto failed_read1;
++
++                      i->i_size = inodep->file_size;
++                      i->i_op = &squashfs_dir_inode_ops_2;
++                      i->i_fop = &squashfs_dir_ops_2;
++                      i->i_mode |= S_IFDIR;
++                      i->i_mtime.tv_sec = inodep->mtime;
++                      i->i_atime.tv_sec = inodep->mtime;
++                      i->i_ctime.tv_sec = inodep->mtime;
++                      SQUASHFS_I(i)->start_block = inodep->start_block;
++                      SQUASHFS_I(i)->offset = inodep->offset;
++                      SQUASHFS_I(i)->u.s2.directory_index_count = 0;
++                      SQUASHFS_I(i)->u.s2.parent_inode = 0;
++
++                      TRACE("Directory inode %x:%x, start_block %x, offset "
++                                      "%x\n", SQUASHFS_INODE_BLK(inode),
++                                      offset, inodep->start_block,
++                                      inodep->offset);
++                      break;
++              }
++              case SQUASHFS_LDIR_TYPE: {
++                      struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
++                      struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
++                                              sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
++                              goto failed_read1;
++
++                      i->i_size = inodep->file_size;
++                      i->i_op = &squashfs_dir_inode_ops_2;
++                      i->i_fop = &squashfs_dir_ops_2;
++                      i->i_mode |= S_IFDIR;
++                      i->i_mtime.tv_sec = inodep->mtime;
++                      i->i_atime.tv_sec = inodep->mtime;
++                      i->i_ctime.tv_sec = inodep->mtime;
++                      SQUASHFS_I(i)->start_block = inodep->start_block;
++                      SQUASHFS_I(i)->offset = inodep->offset;
++                      SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
++                      SQUASHFS_I(i)->u.s2.directory_index_offset =
++                                                              next_offset;
++                      SQUASHFS_I(i)->u.s2.directory_index_count =
++                                                              inodep->i_count;
++                      SQUASHFS_I(i)->u.s2.parent_inode = 0;
++
++                      TRACE("Long directory inode %x:%x, start_block %x, "
++                                      "offset %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      inodep->start_block, inodep->offset);
++                      break;
++              }
++              case SQUASHFS_SYMLINK_TYPE: {
++                      struct squashfs_symlink_inode_header_2 *inodep =
++                                                              &id.symlink;
++                      struct squashfs_symlink_inode_header_2 *sinodep =
++                                                              &sid.symlink;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
++                                                              sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
++                              goto failed_read1;
++
++                      i->i_size = inodep->symlink_size;
++                      i->i_op = &page_symlink_inode_operations;
++                      i->i_data.a_ops = &squashfs_symlink_aops;
++                      i->i_mode |= S_IFLNK;
++                      SQUASHFS_I(i)->start_block = next_block;
++                      SQUASHFS_I(i)->offset = next_offset;
++
++                      TRACE("Symbolic link inode %x:%x, start_block %llx, "
++                                      "offset %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      next_block, next_offset);
++                      break;
++               }
++               case SQUASHFS_BLKDEV_TYPE:
++               case SQUASHFS_CHRDEV_TYPE: {
++                      struct squashfs_dev_inode_header_2 *inodep = &id.dev;
++                      struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
++
++                      if (msblk->swap) {
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              sinodep, block, offset,
++                                              sizeof(*sinodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++                              SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
++                      } else
++                              if (!squashfs_get_cached_block(s, (char *)
++                                              inodep, block, offset,
++                                              sizeof(*inodep), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                      if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
++                              goto failed_read1;
++
++                      i->i_mode |= (inodeb->inode_type ==
++                                      SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
++                                      S_IFBLK;
++                      init_special_inode(i, i->i_mode,
++                                      old_decode_dev(inodep->rdev));
++
++                      TRACE("Device inode %x:%x, rdev %x\n",
++                                      SQUASHFS_INODE_BLK(inode), offset,
++                                      inodep->rdev);
++                      break;
++               }
++               case SQUASHFS_FIFO_TYPE:
++               case SQUASHFS_SOCKET_TYPE: {
++                      if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
++                              goto failed_read1;
++
++                      i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
++                                                      ? S_IFIFO : S_IFSOCK;
++                      init_special_inode(i, i->i_mode, 0);
++                      break;
++               }
++               default:
++                      ERROR("Unknown inode type %d in squashfs_iget!\n",
++                                      inodeb->inode_type);
++                      goto failed_read1;
++      }
++
++      insert_inode_hash(i);
++      return i;
++
++failed_read:
++      ERROR("Unable to read inode [%x:%x]\n", block, offset);
++
++failed_read1:
++      return NULL;
++}
++
++
++static int get_dir_index_using_offset(struct super_block *s, long long
++                              *next_block, unsigned int *next_offset,
++                              long long index_start,
++                              unsigned int index_offset, int i_count,
++                              long long f_pos)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      int i, length = 0;
++      struct squashfs_dir_index_2 index;
++
++      TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
++                                      i_count, (unsigned int) f_pos);
++
++      if (f_pos == 0)
++              goto finish;
++
++      for (i = 0; i < i_count; i++) {
++              if (msblk->swap) {
++                      struct squashfs_dir_index_2 sindex;
++                      squashfs_get_cached_block(s, (char *) &sindex,
++                                      index_start, index_offset,
++                                      sizeof(sindex), &index_start,
++                                      &index_offset);
++                      SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
++              } else
++                      squashfs_get_cached_block(s, (char *) &index,
++                                      index_start, index_offset,
++                                      sizeof(index), &index_start,
++                                      &index_offset);
++
++              if (index.index > f_pos)
++                      break;
++
++              squashfs_get_cached_block(s, NULL, index_start, index_offset,
++                                      index.size + 1, &index_start,
++                                      &index_offset);
++
++              length = index.index;
++              *next_block = index.start_block + sblk->directory_table_start;
++      }
++
++      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++
++finish:
++      return length;
++}
++
++
++static int get_dir_index_using_name(struct super_block *s, long long
++                              *next_block, unsigned int *next_offset,
++                              long long index_start,
++                              unsigned int index_offset, int i_count,
++                              const char *name, int size)
++{
++      struct squashfs_sb_info *msblk = s->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      int i, length = 0;
++      char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1];
++      struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer;
++      char str[SQUASHFS_NAME_LEN + 1];
++
++      TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
++
++      strncpy(str, name, size);
++      str[size] = '\0';
++
++      for (i = 0; i < i_count; i++) {
++              if (msblk->swap) {
++                      struct squashfs_dir_index_2 sindex;
++                      squashfs_get_cached_block(s, (char *) &sindex,
++                                      index_start, index_offset,
++                                      sizeof(sindex), &index_start,
++                                      &index_offset);
++                      SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
++              } else
++                      squashfs_get_cached_block(s, (char *) index,
++                                      index_start, index_offset,
++                                      sizeof(struct squashfs_dir_index_2),
++                                      &index_start, &index_offset);
++
++              squashfs_get_cached_block(s, index->name, index_start,
++                                      index_offset, index->size + 1,
++                                      &index_start, &index_offset);
++
++              index->name[index->size + 1] = '\0';
++
++              if (strcmp(index->name, str) > 0)
++                      break;
++
++              length = index->index;
++              *next_block = index->start_block + sblk->directory_table_start;
++      }
++
++      *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++      return length;
++}
++
++
++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
++{
++      struct inode *i = file->f_dentry->d_inode;
++      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      long long next_block = SQUASHFS_I(i)->start_block +
++              sblk->directory_table_start;
++      int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0,
++              dir_count;
++      struct squashfs_dir_header_2 dirh;
++      char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1];
++      struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
++
++      TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
++
++      length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
++                              SQUASHFS_I(i)->u.s2.directory_index_start,
++                              SQUASHFS_I(i)->u.s2.directory_index_offset,
++                              SQUASHFS_I(i)->u.s2.directory_index_count,
++                              file->f_pos);
++
++      while (length < i_size_read(i)) {
++              /* read directory header */
++              if (msblk->swap) {
++                      struct squashfs_dir_header_2 sdirh;
++
++                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++                                      next_block, next_offset, sizeof(sdirh),
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += sizeof(sdirh);
++                      SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
++              } else {
++                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++                                      next_block, next_offset, sizeof(dirh),
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += sizeof(dirh);
++              }
++
++              dir_count = dirh.count + 1;
++              while (dir_count--) {
++                      if (msblk->swap) {
++                              struct squashfs_dir_entry_2 sdire;
++                              if (!squashfs_get_cached_block(i->i_sb, (char *)
++                                              &sdire, next_block, next_offset,
++                                              sizeof(sdire), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                              length += sizeof(sdire);
++                              SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
++                      } else {
++                              if (!squashfs_get_cached_block(i->i_sb, (char *)
++                                              dire, next_block, next_offset,
++                                              sizeof(*dire), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                              length += sizeof(*dire);
++                      }
++
++                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
++                                              next_block, next_offset,
++                                              dire->size + 1, &next_block,
++                                              &next_offset))
++                              goto failed_read;
++
++                      length += dire->size + 1;
++
++                      if (file->f_pos >= length)
++                              continue;
++
++                      dire->name[dire->size + 1] = '\0';
++
++                      TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
++                                      (unsigned int) dirent, dire->name,
++                                      dire->size + 1, (int) file->f_pos,
++                                      dirh.start_block, dire->offset,
++                                      squashfs_filetype_table[dire->type]);
++
++                      if (filldir(dirent, dire->name, dire->size + 1,
++                                      file->f_pos, SQUASHFS_MK_VFS_INODE(
++                                      dirh.start_block, dire->offset),
++                                      squashfs_filetype_table[dire->type])
++                                      < 0) {
++                              TRACE("Filldir returned less than 0\n");
++                              goto finish;
++                      }
++                      file->f_pos = length;
++                      dirs_read++;
++              }
++      }
++
++finish:
++      return dirs_read;
++
++failed_read:
++      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++              next_offset);
++      return 0;
++}
++
++
++static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry,
++                              struct nameidata *nd)
++{
++      const unsigned char *name = dentry->d_name.name;
++      int len = dentry->d_name.len;
++      struct inode *inode = NULL;
++      struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++      struct squashfs_super_block *sblk = &msblk->sblk;
++      long long next_block = SQUASHFS_I(i)->start_block +
++                              sblk->directory_table_start;
++      int next_offset = SQUASHFS_I(i)->offset, length = 0,
++                              dir_count;
++      struct squashfs_dir_header_2 dirh;
++      char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN];
++      struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
++      int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
++
++      TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
++
++      if (len > SQUASHFS_NAME_LEN)
++              goto exit_loop;
++
++      length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
++                              SQUASHFS_I(i)->u.s2.directory_index_start,
++                              SQUASHFS_I(i)->u.s2.directory_index_offset,
++                              SQUASHFS_I(i)->u.s2.directory_index_count, name,
++                              len);
++
++      while (length < i_size_read(i)) {
++              /* read directory header */
++              if (msblk->swap) {
++                      struct squashfs_dir_header_2 sdirh;
++                      if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++                                      next_block, next_offset, sizeof(sdirh),
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += sizeof(sdirh);
++                      SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
++              } else {
++                      if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++                                      next_block, next_offset, sizeof(dirh),
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += sizeof(dirh);
++              }
++
++              dir_count = dirh.count + 1;
++              while (dir_count--) {
++                      if (msblk->swap) {
++                              struct squashfs_dir_entry_2 sdire;
++                              if (!squashfs_get_cached_block(i->i_sb, (char *)
++                                              &sdire, next_block,next_offset,
++                                              sizeof(sdire), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                              length += sizeof(sdire);
++                              SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
++                      } else {
++                              if (!squashfs_get_cached_block(i->i_sb, (char *)
++                                              dire, next_block,next_offset,
++                                              sizeof(*dire), &next_block,
++                                              &next_offset))
++                                      goto failed_read;
++
++                              length += sizeof(*dire);
++                      }
++
++                      if (!squashfs_get_cached_block(i->i_sb, dire->name,
++                                      next_block, next_offset, dire->size + 1,
++                                      &next_block, &next_offset))
++                              goto failed_read;
++
++                      length += dire->size + 1;
++
++                      if (sorted && name[0] < dire->name[0])
++                              goto exit_loop;
++
++                      if ((len == dire->size + 1) && !strncmp(name,
++                                              dire->name, len)) {
++                              squashfs_inode_t ino =
++                                      SQUASHFS_MKINODE(dirh.start_block,
++                                      dire->offset);
++
++                              TRACE("calling squashfs_iget for directory "
++                                      "entry %s, inode %x:%x, %lld\n", name,
++                                      dirh.start_block, dire->offset, ino);
++
++                              inode = (msblk->iget)(i->i_sb, ino);
++
++                              goto exit_loop;
++                      }
++              }
++      }
++
++exit_loop:
++      d_add(dentry, inode);
++      return ERR_PTR(0);
++
++failed_read:
++      ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++              next_offset);
++      goto exit_loop;
++}
++
++
++int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
++{
++      struct squashfs_super_block *sblk = &msblk->sblk;
++
++      msblk->iget = squashfs_iget_2;
++      msblk->read_fragment_index_table = read_fragment_index_table_2;
++
++      sblk->bytes_used = sblk->bytes_used_2;
++      sblk->uid_start = sblk->uid_start_2;
++      sblk->guid_start = sblk->guid_start_2;
++      sblk->inode_table_start = sblk->inode_table_start_2;
++      sblk->directory_table_start = sblk->directory_table_start_2;
++      sblk->fragment_table_start = sblk->fragment_table_start_2;
++
++      return 1;
++}
+Index: linux-2.6.21.7/fs/squashfs/squashfs.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/fs/squashfs/squashfs.h
+@@ -0,0 +1,86 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs.h
++ */
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++#endif
++
++#ifdef SQUASHFS_TRACE
++#define TRACE(s, args...)     printk(KERN_NOTICE "SQUASHFS: "s, ## args)
++#else
++#define TRACE(s, args...)     {}
++#endif
++
++#define ERROR(s, args...)     printk(KERN_ERR "SQUASHFS error: "s, ## args)
++
++#define SERROR(s, args...)    do { \
++                              if (!silent) \
++                              printk(KERN_ERR "SQUASHFS error: "s, ## args);\
++                              } while(0)
++
++#define WARNING(s, args...)   printk(KERN_WARNING "SQUASHFS: "s, ## args)
++
++static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode)
++{
++      return list_entry(inode, struct squashfs_inode_info, vfs_inode);
++}
++
++#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
++#define SQSH_EXTERN
++extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
++                              long long index, unsigned int length,
++                              long long *next_index);
++extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
++                              long long block, unsigned int offset,
++                              int length, long long *next_block,
++                              unsigned int *next_offset);
++extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
++                                      squashfs_fragment_cache *fragment);
++extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
++                                      *s, long long start_block,
++                                      int length);
++extern struct address_space_operations squashfs_symlink_aops;
++extern struct address_space_operations squashfs_aops;
++extern struct address_space_operations squashfs_aops_4K;
++extern struct inode_operations squashfs_dir_inode_ops;
++#else
++#define SQSH_EXTERN static
++#endif
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
++#else
++static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
++{
++      return 0;
++}
++#endif
++
++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
++#else
++static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
++{
++      return 0;
++}
++#endif
+Index: linux-2.6.21.7/include/linux/squashfs_fs.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/include/linux/squashfs_fs.h
+@@ -0,0 +1,911 @@
++#ifndef SQUASHFS_FS
++#define SQUASHFS_FS
++
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs.h
++ */
++
++#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
++#endif
++
++#ifdef        CONFIG_SQUASHFS_VMALLOC
++#define SQUASHFS_ALLOC(a)             vmalloc(a)
++#define SQUASHFS_FREE(a)              vfree(a)
++#else
++#define SQUASHFS_ALLOC(a)             kmalloc(a, GFP_KERNEL)
++#define SQUASHFS_FREE(a)              kfree(a)
++#endif
++#define SQUASHFS_CACHED_FRAGMENTS     CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
++#define SQUASHFS_MAJOR                        3
++#define SQUASHFS_MINOR                        0
++#define SQUASHFS_MAGIC                        0x73717368
++#define SQUASHFS_MAGIC_SWAP           0x68737173
++#define SQUASHFS_START                        0
++
++/* size of metadata (inode and directory) blocks */
++#define SQUASHFS_METADATA_SIZE                8192
++#define SQUASHFS_METADATA_LOG         13
++
++/* default size of data blocks */
++#define SQUASHFS_FILE_SIZE            65536
++#define SQUASHFS_FILE_LOG             16
++
++#define SQUASHFS_FILE_MAX_SIZE                65536
++
++/* Max number of uids and gids */
++#define SQUASHFS_UIDS                 256
++#define SQUASHFS_GUIDS                        255
++
++/* Max length of filename (not 255) */
++#define SQUASHFS_NAME_LEN             256
++
++#define SQUASHFS_INVALID              ((long long) 0xffffffffffff)
++#define SQUASHFS_INVALID_FRAG         ((unsigned int) 0xffffffff)
++#define SQUASHFS_INVALID_BLK          ((long long) -1)
++#define SQUASHFS_USED_BLK             ((long long) -2)
++
++/* Filesystem flags */
++#define SQUASHFS_NOI                  0
++#define SQUASHFS_NOD                  1
++#define SQUASHFS_CHECK                        2
++#define SQUASHFS_NOF                  3
++#define SQUASHFS_NO_FRAG              4
++#define SQUASHFS_ALWAYS_FRAG          5
++#define SQUASHFS_DUPLICATE            6
++
++#define SQUASHFS_BIT(flag, bit)               ((flag >> bit) & 1)
++
++#define SQUASHFS_UNCOMPRESSED_INODES(flags)   SQUASHFS_BIT(flags, \
++                                              SQUASHFS_NOI)
++
++#define SQUASHFS_UNCOMPRESSED_DATA(flags)     SQUASHFS_BIT(flags, \
++                                              SQUASHFS_NOD)
++
++#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags)        SQUASHFS_BIT(flags, \
++                                              SQUASHFS_NOF)
++
++#define SQUASHFS_NO_FRAGMENTS(flags)          SQUASHFS_BIT(flags, \
++                                              SQUASHFS_NO_FRAG)
++
++#define SQUASHFS_ALWAYS_FRAGMENTS(flags)      SQUASHFS_BIT(flags, \
++                                              SQUASHFS_ALWAYS_FRAG)
++
++#define SQUASHFS_DUPLICATES(flags)            SQUASHFS_BIT(flags, \
++                                              SQUASHFS_DUPLICATE)
++
++#define SQUASHFS_CHECK_DATA(flags)            SQUASHFS_BIT(flags, \
++                                              SQUASHFS_CHECK)
++
++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
++              duplicate_checking)     (noi | (nod << 1) | (check_data << 2) \
++              | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
++              (duplicate_checking << 6))
++
++/* Max number of types and file types */
++#define SQUASHFS_DIR_TYPE             1
++#define SQUASHFS_FILE_TYPE            2
++#define SQUASHFS_SYMLINK_TYPE         3
++#define SQUASHFS_BLKDEV_TYPE          4
++#define SQUASHFS_CHRDEV_TYPE          5
++#define SQUASHFS_FIFO_TYPE            6
++#define SQUASHFS_SOCKET_TYPE          7
++#define SQUASHFS_LDIR_TYPE            8
++#define SQUASHFS_LREG_TYPE            9
++
++/* 1.0 filesystem type definitions */
++#define SQUASHFS_TYPES                        5
++#define SQUASHFS_IPC_TYPE             0
++
++/* Flag whether block is compressed or uncompressed, bit is set if block is
++ * uncompressed */
++#define SQUASHFS_COMPRESSED_BIT               (1 << 15)
++
++#define SQUASHFS_COMPRESSED_SIZE(B)   (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
++              (B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
++
++#define SQUASHFS_COMPRESSED(B)                (!((B) & SQUASHFS_COMPRESSED_BIT))
++
++#define SQUASHFS_COMPRESSED_BIT_BLOCK         (1 << 24)
++
++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)     (((B) & \
++      ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
++      ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
++
++#define SQUASHFS_COMPRESSED_BLOCK(B)  (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
++
++/*
++ * Inode number ops.  Inodes consist of a compressed block number, and an
++ * uncompressed  offset within that block
++ */
++#define SQUASHFS_INODE_BLK(a)         ((unsigned int) ((a) >> 16))
++
++#define SQUASHFS_INODE_OFFSET(a)      ((unsigned int) ((a) & 0xffff))
++
++#define SQUASHFS_MKINODE(A, B)                ((squashfs_inode_t)(((squashfs_inode_t) (A)\
++                                      << 16) + (B)))
++
++/* Compute 32 bit VFS inode number from squashfs inode number */
++#define SQUASHFS_MK_VFS_INODE(a, b)   ((unsigned int) (((a) << 8) + \
++                                      ((b) >> 2) + 1))
++/* XXX */
++
++/* Translate between VFS mode and squashfs mode */
++#define SQUASHFS_MODE(a)              ((a) & 0xfff)
++
++/* fragment and fragment table defines */
++#define SQUASHFS_FRAGMENT_BYTES(A)    (A * sizeof(struct squashfs_fragment_entry))
++
++#define SQUASHFS_FRAGMENT_INDEX(A)    (SQUASHFS_FRAGMENT_BYTES(A) / \
++                                      SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)     (SQUASHFS_FRAGMENT_BYTES(A) % \
++                                              SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEXES(A)  ((SQUASHFS_FRAGMENT_BYTES(A) + \
++                                      SQUASHFS_METADATA_SIZE - 1) / \
++                                      SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)      (SQUASHFS_FRAGMENT_INDEXES(A) *\
++                                              sizeof(long long))
++
++/* cached data constants for filesystem */
++#define SQUASHFS_CACHED_BLKS          8
++
++#define SQUASHFS_MAX_FILE_SIZE_LOG    64
++
++#define SQUASHFS_MAX_FILE_SIZE                ((long long) 1 << \
++                                      (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
++
++#define SQUASHFS_MARKER_BYTE          0xff
++
++/* meta index cache */
++#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
++#define SQUASHFS_META_ENTRIES 31
++#define SQUASHFS_META_NUMBER  8
++#define SQUASHFS_SLOTS                4
++
++struct meta_entry {
++      long long               data_block;
++      unsigned int            index_block;
++      unsigned short          offset;
++      unsigned short          pad;
++};
++
++struct meta_index {
++      unsigned int            inode_number;
++      unsigned int            offset;
++      unsigned short          entries;
++      unsigned short          skip;
++      unsigned short          locked;
++      unsigned short          pad;
++      struct meta_entry       meta_entry[SQUASHFS_META_ENTRIES];
++};
++
++
++/*
++ * definitions for structures on disk
++ */
++
++typedef long long             squashfs_block_t;
++typedef long long             squashfs_inode_t;
++
++struct squashfs_super_block {
++      unsigned int            s_magic;
++      unsigned int            inodes;
++      unsigned int            bytes_used_2;
++      unsigned int            uid_start_2;
++      unsigned int            guid_start_2;
++      unsigned int            inode_table_start_2;
++      unsigned int            directory_table_start_2;
++      unsigned int            s_major:16;
++      unsigned int            s_minor:16;
++      unsigned int            block_size_1:16;
++      unsigned int            block_log:16;
++      unsigned int            flags:8;
++      unsigned int            no_uids:8;
++      unsigned int            no_guids:8;
++      unsigned int            mkfs_time /* time of filesystem creation */;
++      squashfs_inode_t        root_inode;
++      unsigned int            block_size;
++      unsigned int            fragments;
++      unsigned int            fragment_table_start_2;
++      long long               bytes_used;
++      long long               uid_start;
++      long long               guid_start;
++      long long               inode_table_start;
++      long long               directory_table_start;
++      long long               fragment_table_start;
++      long long               unused;
++} __attribute__ ((packed));
++
++struct squashfs_dir_index {
++      unsigned int            index;
++      unsigned int            start_block;
++      unsigned char           size;
++      unsigned char           name[0];
++} __attribute__ ((packed));
++
++#define SQUASHFS_BASE_INODE_HEADER            \
++      unsigned int            inode_type:4;   \
++      unsigned int            mode:12;        \
++      unsigned int            uid:8;          \
++      unsigned int            guid:8;         \
++      unsigned int            mtime;          \
++      unsigned int            inode_number;
++
++struct squashfs_base_inode_header {
++      SQUASHFS_BASE_INODE_HEADER;
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header {
++      SQUASHFS_BASE_INODE_HEADER;
++      unsigned int            nlink;
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header {
++      SQUASHFS_BASE_INODE_HEADER;
++      unsigned int            nlink;
++      unsigned short          rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header {
++      SQUASHFS_BASE_INODE_HEADER;
++      unsigned int            nlink;
++      unsigned short          symlink_size;
++      char                    symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header {
++      SQUASHFS_BASE_INODE_HEADER;
++      squashfs_block_t        start_block;
++      unsigned int            fragment;
++      unsigned int            offset;
++      unsigned int            file_size;
++      unsigned short          block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_lreg_inode_header {
++      SQUASHFS_BASE_INODE_HEADER;
++      unsigned int            nlink;
++      squashfs_block_t        start_block;
++      unsigned int            fragment;
++      unsigned int            offset;
++      long long               file_size;
++      unsigned short          block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header {
++      SQUASHFS_BASE_INODE_HEADER;
++      unsigned int            nlink;
++      unsigned int            file_size:19;
++      unsigned int            offset:13;
++      unsigned int            start_block;
++      unsigned int            parent_inode;
++} __attribute__  ((packed));
++
++struct squashfs_ldir_inode_header {
++      SQUASHFS_BASE_INODE_HEADER;
++      unsigned int            nlink;
++      unsigned int            file_size:27;
++      unsigned int            offset:13;
++      unsigned int            start_block;
++      unsigned int            i_count:16;
++      unsigned int            parent_inode;
++      struct squashfs_dir_index       index[0];
++} __attribute__  ((packed));
++
++union squashfs_inode_header {
++      struct squashfs_base_inode_header       base;
++      struct squashfs_dev_inode_header        dev;
++      struct squashfs_symlink_inode_header    symlink;
++      struct squashfs_reg_inode_header        reg;
++      struct squashfs_lreg_inode_header       lreg;
++      struct squashfs_dir_inode_header        dir;
++      struct squashfs_ldir_inode_header       ldir;
++      struct squashfs_ipc_inode_header        ipc;
++};
++
++struct squashfs_dir_entry {
++      unsigned int            offset:13;
++      unsigned int            type:3;
++      unsigned int            size:8;
++      int                     inode_number:16;
++      char                    name[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_header {
++      unsigned int            count:8;
++      unsigned int            start_block;
++      unsigned int            inode_number;
++} __attribute__ ((packed));
++
++struct squashfs_fragment_entry {
++      long long               start_block;
++      unsigned int            size;
++      unsigned int            unused;
++} __attribute__ ((packed));
++
++extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
++extern int squashfs_uncompress_init(void);
++extern int squashfs_uncompress_exit(void);
++
++/*
++ * macros to convert each packed bitfield structure from little endian to big
++ * endian and vice versa.  These are needed when creating or using a filesystem
++ * on a machine with different byte ordering to the target architecture.
++ *
++ */
++
++#define SQUASHFS_SWAP_START \
++      int bits;\
++      int b_pos;\
++      unsigned long long val;\
++      unsigned char *s;\
++      unsigned char *d;
++
++#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
++      SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
++      SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
++      SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
++      SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
++      SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
++      SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
++      SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
++      SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
++      SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
++      SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
++      SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
++      SQUASHFS_SWAP((s)->flags, d, 288, 8);\
++      SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
++      SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
++      SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
++      SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
++      SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
++      SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
++      SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
++      SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
++      SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
++      SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
++      SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
++      SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
++      SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
++      SQUASHFS_SWAP((s)->unused, d, 888, 64);\
++}
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
++      SQUASHFS_MEMSET(s, d, n);\
++      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++      SQUASHFS_SWAP((s)->uid, d, 16, 8);\
++      SQUASHFS_SWAP((s)->guid, d, 24, 8);\
++      SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
++      SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++                      sizeof(struct squashfs_ipc_inode_header))\
++      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++}
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++                      sizeof(struct squashfs_dev_inode_header)); \
++      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++      SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++                      sizeof(struct squashfs_symlink_inode_header));\
++      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++      SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++                      sizeof(struct squashfs_reg_inode_header));\
++      SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
++      SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
++      SQUASHFS_SWAP((s)->offset, d, 192, 32);\
++      SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
++}
++
++#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++                      sizeof(struct squashfs_lreg_inode_header));\
++      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++      SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
++      SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
++      SQUASHFS_SWAP((s)->offset, d, 224, 32);\
++      SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++                      sizeof(struct squashfs_dir_inode_header));\
++      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++      SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
++      SQUASHFS_SWAP((s)->offset, d, 147, 13);\
++      SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
++      SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
++}
++
++#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++                      sizeof(struct squashfs_ldir_inode_header));\
++      SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++      SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
++      SQUASHFS_SWAP((s)->offset, d, 155, 13);\
++      SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
++      SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
++      SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
++      SQUASHFS_SWAP((s)->index, d, 0, 32);\
++      SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
++      SQUASHFS_SWAP((s)->size, d, 64, 8);\
++}
++
++#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
++      SQUASHFS_SWAP((s)->count, d, 0, 8);\
++      SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
++      SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
++      SQUASHFS_SWAP((s)->offset, d, 0, 13);\
++      SQUASHFS_SWAP((s)->type, d, 13, 3);\
++      SQUASHFS_SWAP((s)->size, d, 16, 8);\
++      SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
++      SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
++      SQUASHFS_SWAP((s)->size, d, 64, 32);\
++}
++
++#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
++      int entry;\
++      int bit_position;\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, n * 2);\
++      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++                      16)\
++              SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
++}
++
++#define SQUASHFS_SWAP_INTS(s, d, n) {\
++      int entry;\
++      int bit_position;\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, n * 4);\
++      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++                      32)\
++              SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
++}
++
++#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
++      int entry;\
++      int bit_position;\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, n * 8);\
++      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++                      64)\
++              SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
++}
++
++#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
++      int entry;\
++      int bit_position;\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, n * bits / 8);\
++      for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++                      bits)\
++              SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++
++struct squashfs_base_inode_header_1 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:4; /* index into uid table */
++      unsigned int            guid:4; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header_1 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:4; /* index into uid table */
++      unsigned int            guid:4; /* index into guid table */
++      unsigned int            type:4;
++      unsigned int            offset:4;
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header_1 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:4; /* index into uid table */
++      unsigned int            guid:4; /* index into guid table */
++      unsigned short          rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header_1 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:4; /* index into uid table */
++      unsigned int            guid:4; /* index into guid table */
++      unsigned short          symlink_size;
++      char                    symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header_1 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:4; /* index into uid table */
++      unsigned int            guid:4; /* index into guid table */
++      unsigned int            mtime;
++      unsigned int            start_block;
++      unsigned int            file_size:32;
++      unsigned short          block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header_1 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:4; /* index into uid table */
++      unsigned int            guid:4; /* index into guid table */
++      unsigned int            file_size:19;
++      unsigned int            offset:13;
++      unsigned int            mtime;
++      unsigned int            start_block:24;
++} __attribute__  ((packed));
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
++      SQUASHFS_MEMSET(s, d, n);\
++      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++      SQUASHFS_SWAP((s)->uid, d, 16, 4);\
++      SQUASHFS_SWAP((s)->guid, d, 20, 4);
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++                      sizeof(struct squashfs_ipc_inode_header_1));\
++      SQUASHFS_SWAP((s)->type, d, 24, 4);\
++      SQUASHFS_SWAP((s)->offset, d, 28, 4);\
++}
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++                      sizeof(struct squashfs_dev_inode_header_1));\
++      SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++                      sizeof(struct squashfs_symlink_inode_header_1));\
++      SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++                      sizeof(struct squashfs_reg_inode_header_1));\
++      SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
++      SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
++      SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++                      sizeof(struct squashfs_dir_inode_header_1));\
++      SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
++      SQUASHFS_SWAP((s)->offset, d, 43, 13);\
++      SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
++      SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
++}
++
++#endif
++
++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++
++struct squashfs_dir_index_2 {
++      unsigned int            index:27;
++      unsigned int            start_block:29;
++      unsigned char           size;
++      unsigned char           name[0];
++} __attribute__ ((packed));
++
++struct squashfs_base_inode_header_2 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:8; /* index into uid table */
++      unsigned int            guid:8; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header_2 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:8; /* index into uid table */
++      unsigned int            guid:8; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header_2 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:8; /* index into uid table */
++      unsigned int            guid:8; /* index into guid table */
++      unsigned short          rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header_2 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:8; /* index into uid table */
++      unsigned int            guid:8; /* index into guid table */
++      unsigned short          symlink_size;
++      char                    symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header_2 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:8; /* index into uid table */
++      unsigned int            guid:8; /* index into guid table */
++      unsigned int            mtime;
++      unsigned int            start_block;
++      unsigned int            fragment;
++      unsigned int            offset;
++      unsigned int            file_size:32;
++      unsigned short          block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header_2 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:8; /* index into uid table */
++      unsigned int            guid:8; /* index into guid table */
++      unsigned int            file_size:19;
++      unsigned int            offset:13;
++      unsigned int            mtime;
++      unsigned int            start_block:24;
++} __attribute__  ((packed));
++
++struct squashfs_ldir_inode_header_2 {
++      unsigned int            inode_type:4;
++      unsigned int            mode:12; /* protection */
++      unsigned int            uid:8; /* index into uid table */
++      unsigned int            guid:8; /* index into guid table */
++      unsigned int            file_size:27;
++      unsigned int            offset:13;
++      unsigned int            mtime;
++      unsigned int            start_block:24;
++      unsigned int            i_count:16;
++      struct squashfs_dir_index_2     index[0];
++} __attribute__  ((packed));
++
++union squashfs_inode_header_2 {
++      struct squashfs_base_inode_header_2     base;
++      struct squashfs_dev_inode_header_2      dev;
++      struct squashfs_symlink_inode_header_2  symlink;
++      struct squashfs_reg_inode_header_2      reg;
++      struct squashfs_dir_inode_header_2      dir;
++      struct squashfs_ldir_inode_header_2     ldir;
++      struct squashfs_ipc_inode_header_2      ipc;
++};
++
++struct squashfs_dir_header_2 {
++      unsigned int            count:8;
++      unsigned int            start_block:24;
++} __attribute__ ((packed));
++
++struct squashfs_dir_entry_2 {
++      unsigned int            offset:13;
++      unsigned int            type:3;
++      unsigned int            size:8;
++      char                    name[0];
++} __attribute__ ((packed));
++
++struct squashfs_fragment_entry_2 {
++      unsigned int            start_block;
++      unsigned int            size;
++} __attribute__ ((packed));
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
++      SQUASHFS_MEMSET(s, d, n);\
++      SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++      SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++      SQUASHFS_SWAP((s)->uid, d, 16, 8);\
++      SQUASHFS_SWAP((s)->guid, d, 24, 8);\
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
++      SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++                      sizeof(struct squashfs_dev_inode_header_2)); \
++      SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++                      sizeof(struct squashfs_symlink_inode_header_2));\
++      SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++                      sizeof(struct squashfs_reg_inode_header_2));\
++      SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
++      SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
++      SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
++      SQUASHFS_SWAP((s)->offset, d, 128, 32);\
++      SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++                      sizeof(struct squashfs_dir_inode_header_2));\
++      SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
++      SQUASHFS_SWAP((s)->offset, d, 51, 13);\
++      SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
++      SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
++}
++
++#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++                      sizeof(struct squashfs_ldir_inode_header_2));\
++      SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
++      SQUASHFS_SWAP((s)->offset, d, 59, 13);\
++      SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
++      SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
++      SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
++      SQUASHFS_SWAP((s)->index, d, 0, 27);\
++      SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
++      SQUASHFS_SWAP((s)->size, d, 56, 8);\
++}
++#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
++      SQUASHFS_SWAP((s)->count, d, 0, 8);\
++      SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
++}
++
++#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
++      SQUASHFS_SWAP((s)->offset, d, 0, 13);\
++      SQUASHFS_SWAP((s)->type, d, 13, 3);\
++      SQUASHFS_SWAP((s)->size, d, 16, 8);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
++      SQUASHFS_SWAP_START\
++      SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
++      SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
++      SQUASHFS_SWAP((s)->size, d, 32, 32);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
++
++/* fragment and fragment table defines */
++#define SQUASHFS_FRAGMENT_BYTES_2(A)  (A * sizeof(struct squashfs_fragment_entry_2))
++
++#define SQUASHFS_FRAGMENT_INDEX_2(A)  (SQUASHFS_FRAGMENT_BYTES_2(A) / \
++                                      SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A)   (SQUASHFS_FRAGMENT_BYTES_2(A) % \
++                                              SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEXES_2(A)        ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
++                                      SQUASHFS_METADATA_SIZE - 1) / \
++                                      SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A)    (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
++                                              sizeof(int))
++
++#endif
++
++#ifdef __KERNEL__
++
++/*
++ * macros used to swap each structure entry, taking into account
++ * bitfields and different bitfield placing conventions on differing
++ * architectures
++ */
++
++#include <asm/byteorder.h>
++
++#ifdef __BIG_ENDIAN
++      /* convert from little endian to big endian */
++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
++              tbits, b_pos)
++#else
++      /* convert from big endian to little endian */
++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
++              tbits, 64 - tbits - b_pos)
++#endif
++
++#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
++      b_pos = pos % 8;\
++      val = 0;\
++      s = (unsigned char *)p + (pos / 8);\
++      d = ((unsigned char *) &val) + 7;\
++      for(bits = 0; bits < (tbits + b_pos); bits += 8) \
++              *d-- = *s++;\
++      value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
++}
++
++#define SQUASHFS_MEMSET(s, d, n)      memset(s, 0, n);
++
++#endif
++#endif
+Index: linux-2.6.21.7/include/linux/squashfs_fs_i.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/include/linux/squashfs_fs_i.h
+@@ -0,0 +1,45 @@
++#ifndef SQUASHFS_FS_I
++#define SQUASHFS_FS_I
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs_i.h
++ */
++
++struct squashfs_inode_info {
++      long long       start_block;
++      unsigned int    offset;
++      union {
++              struct {
++                      long long       fragment_start_block;
++                      unsigned int    fragment_size;
++                      unsigned int    fragment_offset;
++                      long long       block_list_start;
++              } s1;
++              struct {
++                      long long       directory_index_start;
++                      unsigned int    directory_index_offset;
++                      unsigned int    directory_index_count;
++                      unsigned int    parent_inode;
++              } s2;
++      } u;
++      struct inode    vfs_inode;
++};
++#endif
+Index: linux-2.6.21.7/include/linux/squashfs_fs_sb.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/include/linux/squashfs_fs_sb.h
+@@ -0,0 +1,74 @@
++#ifndef SQUASHFS_FS_SB
++#define SQUASHFS_FS_SB
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs_sb.h
++ */
++
++#include <linux/squashfs_fs.h>
++
++struct squashfs_cache {
++      long long       block;
++      int             length;
++      long long       next_index;
++      char            *data;
++};
++
++struct squashfs_fragment_cache {
++      long long       block;
++      int             length;
++      unsigned int    locked;
++      char            *data;
++};
++
++struct squashfs_sb_info {
++      struct squashfs_super_block     sblk;
++      int                     devblksize;
++      int                     devblksize_log2;
++      int                     swap;
++      struct squashfs_cache   *block_cache;
++      struct squashfs_fragment_cache  *fragment;
++      int                     next_cache;
++      int                     next_fragment;
++      int                     next_meta_index;
++      unsigned int            *uid;
++      unsigned int            *guid;
++      long long               *fragment_index;
++      unsigned int            *fragment_index_2;
++      unsigned int            read_size;
++      char                    *read_data;
++      char                    *read_page;
++      struct semaphore        read_data_mutex;
++      struct semaphore        read_page_mutex;
++      struct semaphore        block_cache_mutex;
++      struct semaphore        fragment_mutex;
++      struct semaphore        meta_index_mutex;
++      wait_queue_head_t       waitq;
++      wait_queue_head_t       fragment_wait_queue;
++      struct meta_index       *meta_index;
++      struct inode            *(*iget)(struct super_block *s,  squashfs_inode_t \
++                              inode);
++      long long               (*read_blocklist)(struct inode *inode, int \
++                              index, int readahead_blks, char *block_list, \
++                              unsigned short **block_p, unsigned int *bsize);
++      int                     (*read_fragment_index_table)(struct super_block *s);
++};
++#endif
+Index: linux-2.6.21.7/init/do_mounts_rd.c
+===================================================================
+--- linux-2.6.21.7.orig/init/do_mounts_rd.c
++++ linux-2.6.21.7/init/do_mounts_rd.c
+@@ -5,6 +5,7 @@
+ #include <linux/ext2_fs.h>
+ #include <linux/romfs_fs.h>
+ #include <linux/cramfs_fs.h>
++#include <linux/squashfs_fs.h>
+ #include <linux/initrd.h>
+ #include <linux/string.h>
+@@ -39,6 +40,7 @@ static int __init crd_load(int in_fd, in
+  * numbers could not be found.
+  *
+  * We currently check for the following magic numbers:
++ *      squashfs
+  *    minix
+  *    ext2
+  *    romfs
+@@ -53,6 +55,7 @@ identify_ramdisk_image(int fd, int start
+       struct ext2_super_block *ext2sb;
+       struct romfs_super_block *romfsb;
+       struct cramfs_super *cramfsb;
++      struct squashfs_super_block *squashfsb;
+       int nblocks = -1;
+       unsigned char *buf;
+@@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start
+       ext2sb = (struct ext2_super_block *) buf;
+       romfsb = (struct romfs_super_block *) buf;
+       cramfsb = (struct cramfs_super *) buf;
++      squashfsb = (struct squashfs_super_block *) buf;
+       memset(buf, 0xe5, size);
+       /*
+@@ -101,6 +105,15 @@ identify_ramdisk_image(int fd, int start
+               goto done;
+       }
++      /* squashfs is at block zero too */
++      if (squashfsb->s_magic == SQUASHFS_MAGIC) {
++              printk(KERN_NOTICE
++                     "RAMDISK: squashfs filesystem found at block %d\n",
++                     start_block);
++              nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
++              goto done;
++      }
++
+       /*
+        * Read block 1 to test for minix and ext2 superblock
+        */
diff --git a/target/linux/generic-2.6/patches-2.6.21/002-lzma_decompress.patch b/target/linux/generic-2.6/patches-2.6.21/002-lzma_decompress.patch
new file mode 100644 (file)
index 0000000..e4a0069
--- /dev/null
@@ -0,0 +1,794 @@
+Index: linux-2.6.21.7/include/linux/LzmaDecode.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/include/linux/LzmaDecode.h
+@@ -0,0 +1,100 @@
++/*
++  LzmaDecode.h
++  LZMA Decoder interface
++
++  LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25)
++  http://www.7-zip.org/
++
++  LZMA SDK is licensed under two licenses:
++  1) GNU Lesser General Public License (GNU LGPL)
++  2) Common Public License (CPL)
++  It means that you can select one of these two licenses and
++  follow rules of that license.
++
++  SPECIAL EXCEPTION:
++  Igor Pavlov, as the author of this code, expressly permits you to
++  statically or dynamically link your code (or bind by name) to the
++  interfaces of this file without subjecting your linked code to the
++  terms of the CPL or GNU LGPL. Any modifications or additions
++  to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#ifndef __LZMADECODE_H
++#define __LZMADECODE_H
++
++/* #define _LZMA_IN_CB */
++/* Use callback for input data */
++
++/* #define _LZMA_OUT_READ */
++/* Use read function for output data */
++
++/* #define _LZMA_PROB32 */
++/* It can increase speed on some 32-bit CPUs,
++   but memory usage will be doubled in that case */
++
++/* #define _LZMA_LOC_OPT */
++/* Enable local speed optimizations inside code */
++
++#ifndef UInt32
++#ifdef _LZMA_UINT32_IS_ULONG
++#define UInt32 unsigned long
++#else
++#define UInt32 unsigned int
++#endif
++#endif
++
++#ifdef _LZMA_PROB32
++#define CProb UInt32
++#else
++#define CProb unsigned short
++#endif
++
++#define LZMA_RESULT_OK 0
++#define LZMA_RESULT_DATA_ERROR 1
++#define LZMA_RESULT_NOT_ENOUGH_MEM 2
++
++#ifdef _LZMA_IN_CB
++typedef struct _ILzmaInCallback
++{
++  int (*Read)(void *object, unsigned char **buffer, UInt32 *bufferSize);
++} ILzmaInCallback;
++#endif
++
++#define LZMA_BASE_SIZE 1846
++#define LZMA_LIT_SIZE 768
++
++/*
++bufferSize = (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb)
++bufferSize += 100 in case of _LZMA_OUT_READ
++by default CProb is unsigned short,
++but if specify _LZMA_PROB_32, CProb will be UInt32(unsigned int)
++*/
++
++#ifdef _LZMA_OUT_READ
++int LzmaDecoderInit(
++    unsigned char *buffer, UInt32 bufferSize,
++    int lc, int lp, int pb,
++    unsigned char *dictionary, UInt32 dictionarySize,
++  #ifdef _LZMA_IN_CB
++    ILzmaInCallback *inCallback
++  #else
++    unsigned char *inStream, UInt32 inSize
++  #endif
++);
++#endif
++
++int LzmaDecode(
++    unsigned char *buffer,
++  #ifndef _LZMA_OUT_READ
++    UInt32 bufferSize,
++    int lc, int lp, int pb,
++  #ifdef _LZMA_IN_CB
++    ILzmaInCallback *inCallback,
++  #else
++    unsigned char *inStream, UInt32 inSize,
++  #endif
++  #endif
++    unsigned char *outStream, UInt32 outSize,
++    UInt32 *outSizeProcessed);
++
++#endif
+Index: linux-2.6.21.7/lib/LzmaDecode.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/lib/LzmaDecode.c
+@@ -0,0 +1,663 @@
++/*
++  LzmaDecode.c
++  LZMA Decoder
++
++  LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25)
++  http://www.7-zip.org/
++
++  LZMA SDK is licensed under two licenses:
++  1) GNU Lesser General Public License (GNU LGPL)
++  2) Common Public License (CPL)
++  It means that you can select one of these two licenses and
++  follow rules of that license.
++
++  SPECIAL EXCEPTION:
++  Igor Pavlov, as the author of this code, expressly permits you to
++  statically or dynamically link your code (or bind by name) to the
++  interfaces of this file without subjecting your linked code to the
++  terms of the CPL or GNU LGPL. Any modifications or additions
++  to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#include <linux/LzmaDecode.h>
++
++#ifndef Byte
++#define Byte unsigned char
++#endif
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++
++typedef struct _CRangeDecoder
++{
++  Byte *Buffer;
++  Byte *BufferLim;
++  UInt32 Range;
++  UInt32 Code;
++  #ifdef _LZMA_IN_CB
++  ILzmaInCallback *InCallback;
++  int Result;
++  #endif
++  int ExtraBytes;
++} CRangeDecoder;
++
++Byte RangeDecoderReadByte(CRangeDecoder *rd)
++{
++  if (rd->Buffer == rd->BufferLim)
++  {
++    #ifdef _LZMA_IN_CB
++    UInt32 size;
++    rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size);
++    rd->BufferLim = rd->Buffer + size;
++    if (size == 0)
++    #endif
++    {
++      rd->ExtraBytes = 1;
++      return 0xFF;
++    }
++  }
++  return (*rd->Buffer++);
++}
++
++/* #define ReadByte (*rd->Buffer++) */
++#define ReadByte (RangeDecoderReadByte(rd))
++
++void RangeDecoderInit(CRangeDecoder *rd,
++  #ifdef _LZMA_IN_CB
++    ILzmaInCallback *inCallback
++  #else
++    Byte *stream, UInt32 bufferSize
++  #endif
++    )
++{
++  int i;
++  #ifdef _LZMA_IN_CB
++  rd->InCallback = inCallback;
++  rd->Buffer = rd->BufferLim = 0;
++  #else
++  rd->Buffer = stream;
++  rd->BufferLim = stream + bufferSize;
++  #endif
++  rd->ExtraBytes = 0;
++  rd->Code = 0;
++  rd->Range = (0xFFFFFFFF);
++  for(i = 0; i < 5; i++)
++    rd->Code = (rd->Code << 8) | ReadByte;
++}
++
++#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code;
++#define RC_FLUSH_VAR rd->Range = range; rd->Code = code;
++#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; }
++
++UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits)
++{
++  RC_INIT_VAR
++  UInt32 result = 0;
++  int i;
++  for (i = numTotalBits; i > 0; i--)
++  {
++    /* UInt32 t; */
++    range >>= 1;
++
++    result <<= 1;
++    if (code >= range)
++    {
++      code -= range;
++      result |= 1;
++    }
++    /*
++    t = (code - range) >> 31;
++    t &= 1;
++    code -= range & (t - 1);
++    result = (result + result) | (1 - t);
++    */
++    RC_NORMALIZE
++  }
++  RC_FLUSH_VAR
++  return result;
++}
++
++int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd)
++{
++  UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob;
++  if (rd->Code < bound)
++  {
++    rd->Range = bound;
++    *prob += (kBitModelTotal - *prob) >> kNumMoveBits;
++    if (rd->Range < kTopValue)
++    {
++      rd->Code = (rd->Code << 8) | ReadByte;
++      rd->Range <<= 8;
++    }
++    return 0;
++  }
++  else
++  {
++    rd->Range -= bound;
++    rd->Code -= bound;
++    *prob -= (*prob) >> kNumMoveBits;
++    if (rd->Range < kTopValue)
++    {
++      rd->Code = (rd->Code << 8) | ReadByte;
++      rd->Range <<= 8;
++    }
++    return 1;
++  }
++}
++
++#define RC_GET_BIT2(prob, mi, A0, A1) \
++  UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \
++  if (code < bound) \
++    { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \
++  else \
++    { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \
++  RC_NORMALIZE
++
++#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;)
++
++int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd)
++{
++  int mi = 1;
++  int i;
++  #ifdef _LZMA_LOC_OPT
++  RC_INIT_VAR
++  #endif
++  for(i = numLevels; i > 0; i--)
++  {
++    #ifdef _LZMA_LOC_OPT
++    CProb *prob = probs + mi;
++    RC_GET_BIT(prob, mi)
++    #else
++    mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd);
++    #endif
++  }
++  #ifdef _LZMA_LOC_OPT
++  RC_FLUSH_VAR
++  #endif
++  return mi - (1 << numLevels);
++}
++
++int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd)
++{
++  int mi = 1;
++  int i;
++  int symbol = 0;
++  #ifdef _LZMA_LOC_OPT
++  RC_INIT_VAR
++  #endif
++  for(i = 0; i < numLevels; i++)
++  {
++    #ifdef _LZMA_LOC_OPT
++    CProb *prob = probs + mi;
++    RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i))
++    #else
++    int bit = RangeDecoderBitDecode(probs + mi, rd);
++    mi = mi + mi + bit;
++    symbol |= (bit << i);
++    #endif
++  }
++  #ifdef _LZMA_LOC_OPT
++  RC_FLUSH_VAR
++  #endif
++  return symbol;
++}
++
++Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd)
++{
++  int symbol = 1;
++  #ifdef _LZMA_LOC_OPT
++  RC_INIT_VAR
++  #endif
++  do
++  {
++    #ifdef _LZMA_LOC_OPT
++    CProb *prob = probs + symbol;
++    RC_GET_BIT(prob, symbol)
++    #else
++    symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd);
++    #endif
++  }
++  while (symbol < 0x100);
++  #ifdef _LZMA_LOC_OPT
++  RC_FLUSH_VAR
++  #endif
++  return symbol;
++}
++
++Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte)
++{
++  int symbol = 1;
++  #ifdef _LZMA_LOC_OPT
++  RC_INIT_VAR
++  #endif
++  do
++  {
++    int bit;
++    int matchBit = (matchByte >> 7) & 1;
++    matchByte <<= 1;
++    #ifdef _LZMA_LOC_OPT
++    {
++      CProb *prob = probs + ((1 + matchBit) << 8) + symbol;
++      RC_GET_BIT2(prob, symbol, bit = 0, bit = 1)
++    }
++    #else
++    bit = RangeDecoderBitDecode(probs + ((1 + matchBit) << 8) + symbol, rd);
++    symbol = (symbol << 1) | bit;
++    #endif
++    if (matchBit != bit)
++    {
++      while (symbol < 0x100)
++      {
++        #ifdef _LZMA_LOC_OPT
++        CProb *prob = probs + symbol;
++        RC_GET_BIT(prob, symbol)
++        #else
++        symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd);
++        #endif
++      }
++      break;
++    }
++  }
++  while (symbol < 0x100);
++  #ifdef _LZMA_LOC_OPT
++  RC_FLUSH_VAR
++  #endif
++  return symbol;
++}
++
++#define kNumPosBitsMax 4
++#define kNumPosStatesMax (1 << kNumPosBitsMax)
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define LenChoice 0
++#define LenChoice2 (LenChoice + 1)
++#define LenLow (LenChoice2 + 1)
++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
++#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
++
++int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState)
++{
++  if(RangeDecoderBitDecode(p + LenChoice, rd) == 0)
++    return RangeDecoderBitTreeDecode(p + LenLow +
++        (posState << kLenNumLowBits), kLenNumLowBits, rd);
++  if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0)
++    return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid +
++        (posState << kLenNumMidBits), kLenNumMidBits, rd);
++  return kLenNumLowSymbols + kLenNumMidSymbols +
++      RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd);
++}
++
++#define kNumStates 12
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#define kNumPosSlotBits 6
++#define kNumLenToPosStates 4
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++
++#define kMatchMinLen 2
++
++#define IsMatch 0
++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
++#define IsRepG0 (IsRep + kNumStates)
++#define IsRepG1 (IsRepG0 + kNumStates)
++#define IsRepG2 (IsRepG1 + kNumStates)
++#define IsRep0Long (IsRepG2 + kNumStates)
++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
++#define LenCoder (Align + kAlignTableSize)
++#define RepLenCoder (LenCoder + kNumLenProbs)
++#define Literal (RepLenCoder + kNumLenProbs)
++
++#if Literal != LZMA_BASE_SIZE
++StopCompilingDueBUG
++#endif
++
++#ifdef _LZMA_OUT_READ
++
++typedef struct _LzmaVarState
++{
++  CRangeDecoder RangeDecoder;
++  Byte *Dictionary;
++  UInt32 DictionarySize;
++  UInt32 DictionaryPos;
++  UInt32 GlobalPos;
++  UInt32 Reps[4];
++  int lc;
++  int lp;
++  int pb;
++  int State;
++  int PreviousIsMatch;
++  int RemainLen;
++} LzmaVarState;
++
++int LzmaDecoderInit(
++    unsigned char *buffer, UInt32 bufferSize,
++    int lc, int lp, int pb,
++    unsigned char *dictionary, UInt32 dictionarySize,
++    #ifdef _LZMA_IN_CB
++    ILzmaInCallback *inCallback
++    #else
++    unsigned char *inStream, UInt32 inSize
++    #endif
++    )
++{
++  LzmaVarState *vs = (LzmaVarState *)buffer;
++  CProb *p = (CProb *)(buffer + sizeof(LzmaVarState));
++  UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp));
++  UInt32 i;
++  if (bufferSize < numProbs * sizeof(CProb) + sizeof(LzmaVarState))
++    return LZMA_RESULT_NOT_ENOUGH_MEM;
++  vs->Dictionary = dictionary;
++  vs->DictionarySize = dictionarySize;
++  vs->DictionaryPos = 0;
++  vs->GlobalPos = 0;
++  vs->Reps[0] = vs->Reps[1] = vs->Reps[2] = vs->Reps[3] = 1;
++  vs->lc = lc;
++  vs->lp = lp;
++  vs->pb = pb;
++  vs->State = 0;
++  vs->PreviousIsMatch = 0;
++  vs->RemainLen = 0;
++  dictionary[dictionarySize - 1] = 0;
++  for (i = 0; i < numProbs; i++)
++    p[i] = kBitModelTotal >> 1;
++  RangeDecoderInit(&vs->RangeDecoder,
++      #ifdef _LZMA_IN_CB
++      inCallback
++      #else
++      inStream, inSize
++      #endif
++  );
++  return LZMA_RESULT_OK;
++}
++
++int LzmaDecode(unsigned char *buffer,
++    unsigned char *outStream, UInt32 outSize,
++    UInt32 *outSizeProcessed)
++{
++  LzmaVarState *vs = (LzmaVarState *)buffer;
++  CProb *p = (CProb *)(buffer + sizeof(LzmaVarState));
++  CRangeDecoder rd = vs->RangeDecoder;
++  int state = vs->State;
++  int previousIsMatch = vs->PreviousIsMatch;
++  Byte previousByte;
++  UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
++  UInt32 nowPos = 0;
++  UInt32 posStateMask = (1 << (vs->pb)) - 1;
++  UInt32 literalPosMask = (1 << (vs->lp)) - 1;
++  int lc = vs->lc;
++  int len = vs->RemainLen;
++  UInt32 globalPos = vs->GlobalPos;
++
++  Byte *dictionary = vs->Dictionary;
++  UInt32 dictionarySize = vs->DictionarySize;
++  UInt32 dictionaryPos = vs->DictionaryPos;
++
++  if (len == -1)
++  {
++    *outSizeProcessed = 0;
++    return LZMA_RESULT_OK;
++  }
++
++  while(len > 0 && nowPos < outSize)
++  {
++    UInt32 pos = dictionaryPos - rep0;
++    if (pos >= dictionarySize)
++      pos += dictionarySize;
++    outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
++    if (++dictionaryPos == dictionarySize)
++      dictionaryPos = 0;
++    len--;
++  }
++  if (dictionaryPos == 0)
++    previousByte = dictionary[dictionarySize - 1];
++  else
++    previousByte = dictionary[dictionaryPos - 1];
++#else
++
++int LzmaDecode(
++    Byte *buffer, UInt32 bufferSize,
++    int lc, int lp, int pb,
++    #ifdef _LZMA_IN_CB
++    ILzmaInCallback *inCallback,
++    #else
++    unsigned char *inStream, UInt32 inSize,
++    #endif
++    unsigned char *outStream, UInt32 outSize,
++    UInt32 *outSizeProcessed)
++{
++  UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp));
++  CProb *p = (CProb *)buffer;
++  CRangeDecoder rd;
++  UInt32 i;
++  int state = 0;
++  int previousIsMatch = 0;
++  Byte previousByte = 0;
++  UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
++  UInt32 nowPos = 0;
++  UInt32 posStateMask = (1 << pb) - 1;
++  UInt32 literalPosMask = (1 << lp) - 1;
++  int len = 0;
++  if (bufferSize < numProbs * sizeof(CProb))
++    return LZMA_RESULT_NOT_ENOUGH_MEM;
++  for (i = 0; i < numProbs; i++)
++    p[i] = kBitModelTotal >> 1;
++  RangeDecoderInit(&rd,
++      #ifdef _LZMA_IN_CB
++      inCallback
++      #else
++      inStream, inSize
++      #endif
++      );
++#endif
++
++  *outSizeProcessed = 0;
++  while(nowPos < outSize)
++  {
++    int posState = (int)(
++        (nowPos
++        #ifdef _LZMA_OUT_READ
++        + globalPos
++        #endif
++        )
++        & posStateMask);
++    #ifdef _LZMA_IN_CB
++    if (rd.Result != LZMA_RESULT_OK)
++      return rd.Result;
++    #endif
++    if (rd.ExtraBytes != 0)
++      return LZMA_RESULT_DATA_ERROR;
++    if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0)
++    {
++      CProb *probs = p + Literal + (LZMA_LIT_SIZE *
++        (((
++        (nowPos
++        #ifdef _LZMA_OUT_READ
++        + globalPos
++        #endif
++        )
++        & literalPosMask) << lc) + (previousByte >> (8 - lc))));
++
++      if (state < 4) state = 0;
++      else if (state < 10) state -= 3;
++      else state -= 6;
++      if (previousIsMatch)
++      {
++        Byte matchByte;
++        #ifdef _LZMA_OUT_READ
++        UInt32 pos = dictionaryPos - rep0;
++        if (pos >= dictionarySize)
++          pos += dictionarySize;
++        matchByte = dictionary[pos];
++        #else
++        matchByte = outStream[nowPos - rep0];
++        #endif
++        previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte);
++        previousIsMatch = 0;
++      }
++      else
++        previousByte = LzmaLiteralDecode(probs, &rd);
++      outStream[nowPos++] = previousByte;
++      #ifdef _LZMA_OUT_READ
++      dictionary[dictionaryPos] = previousByte;
++      if (++dictionaryPos == dictionarySize)
++        dictionaryPos = 0;
++      #endif
++    }
++    else
++    {
++      previousIsMatch = 1;
++      if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1)
++      {
++        if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0)
++        {
++          if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0)
++          {
++            #ifdef _LZMA_OUT_READ
++            UInt32 pos;
++            #endif
++            if (
++               (nowPos
++                #ifdef _LZMA_OUT_READ
++                + globalPos
++                #endif
++               )
++               == 0)
++              return LZMA_RESULT_DATA_ERROR;
++            state = state < 7 ? 9 : 11;
++            #ifdef _LZMA_OUT_READ
++            pos = dictionaryPos - rep0;
++            if (pos >= dictionarySize)
++              pos += dictionarySize;
++            previousByte = dictionary[pos];
++            dictionary[dictionaryPos] = previousByte;
++            if (++dictionaryPos == dictionarySize)
++              dictionaryPos = 0;
++            #else
++            previousByte = outStream[nowPos - rep0];
++            #endif
++            outStream[nowPos++] = previousByte;
++            continue;
++          }
++        }
++        else
++        {
++          UInt32 distance;
++          if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0)
++            distance = rep1;
++          else
++          {
++            if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0)
++              distance = rep2;
++            else
++            {
++              distance = rep3;
++              rep3 = rep2;
++            }
++            rep2 = rep1;
++          }
++          rep1 = rep0;
++          rep0 = distance;
++        }
++        len = LzmaLenDecode(p + RepLenCoder, &rd, posState);
++        state = state < 7 ? 8 : 11;
++      }
++      else
++      {
++        int posSlot;
++        rep3 = rep2;
++        rep2 = rep1;
++        rep1 = rep0;
++        state = state < 7 ? 7 : 10;
++        len = LzmaLenDecode(p + LenCoder, &rd, posState);
++        posSlot = RangeDecoderBitTreeDecode(p + PosSlot +
++            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
++            kNumPosSlotBits), kNumPosSlotBits, &rd);
++        if (posSlot >= kStartPosModelIndex)
++        {
++          int numDirectBits = ((posSlot >> 1) - 1);
++          rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits);
++          if (posSlot < kEndPosModelIndex)
++          {
++            rep0 += RangeDecoderReverseBitTreeDecode(
++                p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd);
++          }
++          else
++          {
++            rep0 += RangeDecoderDecodeDirectBits(&rd,
++                numDirectBits - kNumAlignBits) << kNumAlignBits;
++            rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd);
++          }
++        }
++        else
++          rep0 = posSlot;
++        rep0++;
++      }
++      if (rep0 == (UInt32)(0))
++      {
++        /* it's for stream version */
++        len = -1;
++        break;
++      }
++      if (rep0 > nowPos
++        #ifdef _LZMA_OUT_READ
++        + globalPos
++        #endif
++        )
++      {
++        return LZMA_RESULT_DATA_ERROR;
++      }
++      len += kMatchMinLen;
++      do
++      {
++        #ifdef _LZMA_OUT_READ
++        UInt32 pos = dictionaryPos - rep0;
++        if (pos >= dictionarySize)
++          pos += dictionarySize;
++        previousByte = dictionary[pos];
++        dictionary[dictionaryPos] = previousByte;
++        if (++dictionaryPos == dictionarySize)
++          dictionaryPos = 0;
++        #else
++        previousByte = outStream[nowPos - rep0];
++        #endif
++        outStream[nowPos++] = previousByte;
++        len--;
++      }
++      while(len > 0 && nowPos < outSize);
++    }
++  }
++
++  #ifdef _LZMA_OUT_READ
++  vs->RangeDecoder = rd;
++  vs->DictionaryPos = dictionaryPos;
++  vs->GlobalPos = globalPos + nowPos;
++  vs->Reps[0] = rep0;
++  vs->Reps[1] = rep1;
++  vs->Reps[2] = rep2;
++  vs->Reps[3] = rep3;
++  vs->State = state;
++  vs->PreviousIsMatch = previousIsMatch;
++  vs->RemainLen = len;
++  #endif
++
++  *outSizeProcessed = nowPos;
++  return LZMA_RESULT_OK;
++}
+Index: linux-2.6.21.7/lib/Makefile
+===================================================================
+--- linux-2.6.21.7.orig/lib/Makefile
++++ linux-2.6.21.7/lib/Makefile
+@@ -12,7 +12,7 @@ lib-$(CONFIG_SMP) += cpumask.o
+ lib-y += kobject.o kref.o kobject_uevent.o klist.o
+-obj-y += sort.o parser.o halfmd4.o debug_locks.o random32.o bust_spinlocks.o
++obj-y += sort.o parser.o halfmd4.o debug_locks.o random32.o bust_spinlocks.o LzmaDecode.o
+ ifeq ($(CONFIG_DEBUG_KOBJECT),y)
+ CFLAGS_kobject.o += -DDEBUG
+@@ -56,6 +56,7 @@ obj-$(CONFIG_SMP) += percpu_counter.o
+ obj-$(CONFIG_AUDIT_GENERIC) += audit.o
+ obj-$(CONFIG_SWIOTLB) += swiotlb.o
++
+ obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o
+ lib-$(CONFIG_GENERIC_BUG) += bug.o
diff --git a/target/linux/generic-2.6/patches-2.6.21/003-squashfs_lzma.patch b/target/linux/generic-2.6/patches-2.6.21/003-squashfs_lzma.patch
new file mode 100644 (file)
index 0000000..e476c76
--- /dev/null
@@ -0,0 +1,109 @@
+Index: linux-2.6.21.7/fs/squashfs/inode.c
+===================================================================
+--- linux-2.6.21.7.orig/fs/squashfs/inode.c
++++ linux-2.6.21.7/fs/squashfs/inode.c
+@@ -4,6 +4,9 @@
+  * Copyright (c) 2002, 2003, 2004, 2005, 2006
+  * Phillip Lougher <phillip@lougher.org.uk>
+  *
++ * LZMA decompressor support added by Oleg I. Vdovikin
++ * Copyright (c) 2005 Oleg I.Vdovikin <oleg@cs.msu.su>
++ *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2,
+@@ -21,6 +24,7 @@
+  * inode.c
+  */
++#define SQUASHFS_LZMA
+ #include <linux/types.h>
+ #include <linux/squashfs_fs.h>
+ #include <linux/module.h>
+@@ -44,6 +48,19 @@
+ #include "squashfs.h"
++#ifdef SQUASHFS_LZMA
++#include <linux/LzmaDecode.h>
++
++/* default LZMA settings, should be in sync with mksquashfs */
++#define LZMA_LC 3
++#define LZMA_LP 0
++#define LZMA_PB 2
++
++#define LZMA_WORKSPACE_SIZE ((LZMA_BASE_SIZE + \
++      (LZMA_LIT_SIZE << (LZMA_LC + LZMA_LP))) * sizeof(CProb))
++
++#endif
++
+ static void squashfs_put_super(struct super_block *);
+ static int squashfs_statfs(struct dentry *, struct kstatfs *);
+ static int squashfs_symlink_readpage(struct file *file, struct page *page);
+@@ -64,7 +81,11 @@ static int squashfs_get_sb(struct file_s
+                       const char *, void *, struct vfsmount *);
++#ifdef SQUASHFS_LZMA
++static unsigned char lzma_workspace[LZMA_WORKSPACE_SIZE];
++#else
+ static z_stream stream;
++#endif
+ static struct file_system_type squashfs_fs_type = {
+       .owner = THIS_MODULE,
+@@ -249,6 +270,15 @@ SQSH_EXTERN unsigned int squashfs_read_d
+       if (compressed) {
+               int zlib_err;
++#ifdef SQUASHFS_LZMA
++              if ((zlib_err = LzmaDecode(lzma_workspace,
++                      LZMA_WORKSPACE_SIZE, LZMA_LC, LZMA_LP, LZMA_PB,
++                      c_buffer, c_byte, buffer, msblk->read_size, &bytes)) != LZMA_RESULT_OK)
++              {
++                      ERROR("lzma returned unexpected result 0x%x\n", zlib_err);
++                      bytes = 0;
++              }
++#else
+               stream.next_in = c_buffer;
+               stream.avail_in = c_byte;
+               stream.next_out = buffer;
+@@ -263,7 +293,7 @@ SQSH_EXTERN unsigned int squashfs_read_d
+                       bytes = 0;
+               } else
+                       bytes = stream.total_out;
+-
++#endif
+               up(&msblk->read_data_mutex);
+       }
+@@ -2045,15 +2075,19 @@ static int __init init_squashfs_fs(void)
+       printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) "
+               "Phillip Lougher\n");
++#ifndef SQUASHFS_LZMA
+       if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
+               ERROR("Failed to allocate zlib workspace\n");
+               destroy_inodecache();
+               err = -ENOMEM;
+               goto out;
+       }
++#endif
+       if ((err = register_filesystem(&squashfs_fs_type))) {
++#ifndef SQUASHFS_LZMA
+               vfree(stream.workspace);
++#endif
+               destroy_inodecache();
+       }
+@@ -2064,7 +2098,9 @@ out:
+ static void __exit exit_squashfs_fs(void)
+ {
++#ifndef SQUASHFS_LZMA
+       vfree(stream.workspace);
++#endif
+       unregister_filesystem(&squashfs_fs_type);
+       destroy_inodecache();
+ }
diff --git a/target/linux/generic-2.6/patches-2.6.21/004-extra_optimization.patch b/target/linux/generic-2.6/patches-2.6.21/004-extra_optimization.patch
new file mode 100644 (file)
index 0000000..3a08e13
--- /dev/null
@@ -0,0 +1,14 @@
+Index: linux-2.6.21.7/Makefile
+===================================================================
+--- linux-2.6.21.7.orig/Makefile
++++ linux-2.6.21.7/Makefile
+@@ -507,6 +507,9 @@ CFLAGS          += $(call cc-option, -fn
+ NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
+ CHECKFLAGS     += $(NOSTDINC_FLAGS)
++# improve gcc optimization
++CFLAGS += $(call cc-option,-funit-at-a-time,)
++
+ # warn about C99 declaration after statement
+ CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
diff --git a/target/linux/generic-2.6/patches-2.6.21/006-gcc4_inline_fix.patch b/target/linux/generic-2.6/patches-2.6.21/006-gcc4_inline_fix.patch
new file mode 100644 (file)
index 0000000..2c147b5
--- /dev/null
@@ -0,0 +1,13 @@
+Index: linux-2.6.21.7/include/asm-mips/system.h
+===================================================================
+--- linux-2.6.21.7.orig/include/asm-mips/system.h
++++ linux-2.6.21.7/include/asm-mips/system.h
+@@ -188,7 +188,7 @@ extern __u64 __xchg_u64_unsupported_on_3
+    if something tries to do an invalid xchg().  */
+ extern void __xchg_called_with_bad_pointer(void);
+-static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
++static __always_inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
+ {
+       switch (size) {
+       case 4:
diff --git a/target/linux/generic-2.6/patches-2.6.21/007-samsung_flash.patch b/target/linux/generic-2.6/patches-2.6.21/007-samsung_flash.patch
new file mode 100644 (file)
index 0000000..6878633
--- /dev/null
@@ -0,0 +1,38 @@
+Index: linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0002.c
+===================================================================
+--- linux-2.6.21.7.orig/drivers/mtd/chips/cfi_cmdset_0002.c
++++ linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0002.c
+@@ -51,6 +51,7 @@
+ #define SST49LF040B           0x0050
+ #define SST49LF008A           0x005a
+ #define AT49BV6416            0x00d6
++#define MANUFACTURER_SAMSUNG  0x00ec
+ static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+ static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
+@@ -294,12 +295,19 @@ struct mtd_info *cfi_cmdset_0002(struct 
+               if (extp->MajorVersion != '1' ||
+                   (extp->MinorVersion < '0' || extp->MinorVersion > '4')) {
+-                      printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
+-                             "version %c.%c.\n",  extp->MajorVersion,
+-                             extp->MinorVersion);
+-                      kfree(extp);
+-                      kfree(mtd);
+-                      return NULL;
++                      if (cfi->mfr == MANUFACTURER_SAMSUNG &&
++                          (extp->MajorVersion == '3' && extp->MinorVersion == '3')) {
++                          printk(KERN_NOTICE "  Newer Samsung flash detected, "
++                                 "should be compatibile with Amd/Fujitsu.\n");
++                      }
++                      else {
++                          printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
++                                 "version %c.%c.\n",  extp->MajorVersion,
++                                 extp->MinorVersion);
++                          kfree(extp);
++                          kfree(mtd);
++                          return NULL;
++                      }
+               }
+               /* Install our own private info structure */
diff --git a/target/linux/generic-2.6/patches-2.6.21/009-revert_intel_flash_breakage.patch b/target/linux/generic-2.6/patches-2.6.21/009-revert_intel_flash_breakage.patch
new file mode 100644 (file)
index 0000000..8c7684a
--- /dev/null
@@ -0,0 +1,171 @@
+Index: linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0001.c
+===================================================================
+--- linux-2.6.21.7.orig/drivers/mtd/chips/cfi_cmdset_0001.c
++++ linux-2.6.21.7/drivers/mtd/chips/cfi_cmdset_0001.c
+@@ -919,7 +919,7 @@ static void __xipram xip_enable(struct m
+ static int __xipram xip_wait_for_operation(
+               struct map_info *map, struct flchip *chip,
+-              unsigned long adr, unsigned int chip_op_time )
++              unsigned long adr, int *chip_op_time )
+ {
+       struct cfi_private *cfi = map->fldrv_priv;
+       struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
+@@ -928,7 +928,7 @@ static int __xipram xip_wait_for_operati
+       flstate_t oldstate, newstate;
+               start = xip_currtime();
+-      usec = chip_op_time * 8;
++      usec = *chip_op_time * 8;
+       if (usec == 0)
+               usec = 500000;
+       done = 0;
+@@ -1038,8 +1038,8 @@ static int __xipram xip_wait_for_operati
+ #define XIP_INVAL_CACHED_RANGE(map, from, size)  \
+       INVALIDATE_CACHED_RANGE(map, from, size)
+-#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, usec) \
+-      xip_wait_for_operation(map, chip, cmd_adr, usec)
++#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, p_usec) \
++      xip_wait_for_operation(map, chip, cmd_adr, p_usec)
+ #else
+@@ -1051,65 +1051,65 @@ static int __xipram xip_wait_for_operati
+ static int inval_cache_and_wait_for_operation(
+               struct map_info *map, struct flchip *chip,
+               unsigned long cmd_adr, unsigned long inval_adr, int inval_len,
+-              unsigned int chip_op_time)
++              int *chip_op_time )
+ {
+       struct cfi_private *cfi = map->fldrv_priv;
+       map_word status, status_OK = CMD(0x80);
+-      int chip_state = chip->state;
+-      unsigned int timeo, sleep_time;
++      int z, chip_state = chip->state;
++      unsigned long timeo;
+       spin_unlock(chip->mutex);
+       if (inval_len)
+               INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len);
++      if (*chip_op_time)
++              cfi_udelay(*chip_op_time);
+       spin_lock(chip->mutex);
+-      /* set our timeout to 8 times the expected delay */
+-      timeo = chip_op_time * 8;
+-      if (!timeo)
+-              timeo = 500000;
+-      sleep_time = chip_op_time / 2;
++      timeo = *chip_op_time * 8 * HZ / 1000000;
++      if (timeo < HZ/2)
++              timeo = HZ/2;
++      timeo += jiffies;
++      z = 0;
+       for (;;) {
++              if (chip->state != chip_state) {
++                      /* Someone's suspended the operation: sleep */
++                      DECLARE_WAITQUEUE(wait, current);
++
++                      set_current_state(TASK_UNINTERRUPTIBLE);
++                      add_wait_queue(&chip->wq, &wait);
++                      spin_unlock(chip->mutex);
++                      schedule();
++                      remove_wait_queue(&chip->wq, &wait);
++                      timeo = jiffies + (HZ / 2); /* FIXME */
++                      spin_lock(chip->mutex);
++                      continue;
++              }
++
+               status = map_read(map, cmd_adr);
+               if (map_word_andequal(map, status, status_OK, status_OK))
+                       break;
+-              if (!timeo) {
++              /* OK Still waiting */
++              if (time_after(jiffies, timeo)) {
+                       map_write(map, CMD(0x70), cmd_adr);
+                       chip->state = FL_STATUS;
+                       return -ETIME;
+               }
+-              /* OK Still waiting. Drop the lock, wait a while and retry. */
++              /* Latency issues. Drop the lock, wait a while and retry */
++              z++;
+               spin_unlock(chip->mutex);
+-              if (sleep_time >= 1000000/HZ) {
+-                      /*
+-                       * Half of the normal delay still remaining
+-                       * can be performed with a sleeping delay instead
+-                       * of busy waiting.
+-                       */
+-                      msleep(sleep_time/1000);
+-                      timeo -= sleep_time;
+-                      sleep_time = 1000000/HZ;
+-              } else {
+-                      udelay(1);
+-                      cond_resched();
+-                      timeo--;
+-              }
++              cfi_udelay(1);
+               spin_lock(chip->mutex);
+-
+-              while (chip->state != chip_state) {
+-                      /* Someone's suspended the operation: sleep */
+-                      DECLARE_WAITQUEUE(wait, current);
+-                      set_current_state(TASK_UNINTERRUPTIBLE);
+-                      add_wait_queue(&chip->wq, &wait);
+-                      spin_unlock(chip->mutex);
+-                      schedule();
+-                      remove_wait_queue(&chip->wq, &wait);
+-                      spin_lock(chip->mutex);
+-              }
+       }
++      if (!z) {
++              if (!--(*chip_op_time))
++                      *chip_op_time = 1;
++      } else if (z > 1)
++              ++(*chip_op_time);
++
+       /* Done and happy. */
+       chip->state = FL_STATUS;
+       return 0;
+@@ -1118,7 +1118,8 @@ static int inval_cache_and_wait_for_oper
+ #endif
+ #define WAIT_TIMEOUT(map, chip, adr, udelay) \
+-      INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay);
++      ({ int __udelay = (udelay); \
++         INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, &__udelay); })
+ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
+@@ -1342,7 +1343,7 @@ static int __xipram do_write_oneword(str
+       ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
+                                  adr, map_bankwidth(map),
+-                                 chip->word_write_time);
++                                 &chip->word_write_time);
+       if (ret) {
+               xip_enable(map, chip, adr);
+               printk(KERN_ERR "%s: word write error (status timeout)\n", map->name);
+@@ -1579,7 +1580,7 @@ static int __xipram do_write_buffer(stru
+       ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr,
+                                  adr, len,
+-                                 chip->buffer_write_time);
++                                 &chip->buffer_write_time);
+       if (ret) {
+               map_write(map, CMD(0x70), cmd_adr);
+               chip->state = FL_STATUS;
+@@ -1714,7 +1715,7 @@ static int __xipram do_erase_oneblock(st
+       ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
+                                  adr, len,
+-                                 chip->erase_time);
++                                 &chip->erase_time);
+       if (ret) {
+               map_write(map, CMD(0x70), adr);
+               chip->state = FL_STATUS;
diff --git a/target/linux/generic-2.6/patches-2.6.21/010-disable_old_squashfs_compatibility.patch b/target/linux/generic-2.6/patches-2.6.21/010-disable_old_squashfs_compatibility.patch
new file mode 100644 (file)
index 0000000..5549861
--- /dev/null
@@ -0,0 +1,23 @@
+Index: linux-2.6.21.7/fs/squashfs/Makefile
+===================================================================
+--- linux-2.6.21.7.orig/fs/squashfs/Makefile
++++ linux-2.6.21.7/fs/squashfs/Makefile
+@@ -4,4 +4,3 @@
+ obj-$(CONFIG_SQUASHFS) += squashfs.o
+ squashfs-y += inode.o
+-squashfs-y += squashfs2_0.o
+Index: linux-2.6.21.7/fs/squashfs/squashfs.h
+===================================================================
+--- linux-2.6.21.7.orig/fs/squashfs/squashfs.h
++++ linux-2.6.21.7/fs/squashfs/squashfs.h
+@@ -24,6 +24,9 @@
+ #ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+ #undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+ #endif
++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++#undef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++#endif
+ #ifdef SQUASHFS_TRACE
+ #define TRACE(s, args...)     printk(KERN_NOTICE "SQUASHFS: "s, ## args)
diff --git a/target/linux/generic-2.6/patches-2.6.21/011-mips_boot.patch b/target/linux/generic-2.6/patches-2.6.21/011-mips_boot.patch
new file mode 100644 (file)
index 0000000..866b3f0
--- /dev/null
@@ -0,0 +1,21 @@
+Index: linux-2.6.21.7/arch/mips/kernel/head.S
+===================================================================
+--- linux-2.6.21.7.orig/arch/mips/kernel/head.S
++++ linux-2.6.21.7/arch/mips/kernel/head.S
+@@ -129,11 +129,15 @@
+ #endif
+       .endm
++
++      j kernel_entry
++      nop
++
+       /*
+        * Reserved space for exception handlers.
+        * Necessary for machines which link their kernels at KSEG0.
+        */
+-      .fill   0x400
++      .align 10
+ EXPORT(stext)                                 # used for profiling
+ EXPORT(_stext)
diff --git a/target/linux/generic-2.6/patches-2.6.21/012-mips_cpu_tlb.patch b/target/linux/generic-2.6/patches-2.6.21/012-mips_cpu_tlb.patch
new file mode 100644 (file)
index 0000000..305cef0
--- /dev/null
@@ -0,0 +1,20 @@
+Index: linux-2.6.21.7/arch/mips/mm/tlbex.c
+===================================================================
+--- linux-2.6.21.7.orig/arch/mips/mm/tlbex.c
++++ linux-2.6.21.7/arch/mips/mm/tlbex.c
+@@ -887,7 +887,6 @@ static __init void build_tlb_write_entry
+       case CPU_R10000:
+       case CPU_R12000:
+       case CPU_R14000:
+-      case CPU_4KC:
+       case CPU_SB1:
+       case CPU_SB1A:
+       case CPU_4KSC:
+@@ -915,6 +914,7 @@ static __init void build_tlb_write_entry
+               tlbw(p);
+               break;
++      case CPU_4KC:
+       case CPU_4KEC:
+       case CPU_24K:
+       case CPU_34K:
diff --git a/target/linux/generic-2.6/patches-2.6.21/013-mips_generic_gpio_support.patch b/target/linux/generic-2.6/patches-2.6.21/013-mips_generic_gpio_support.patch
new file mode 100644 (file)
index 0000000..c23519e
--- /dev/null
@@ -0,0 +1,38 @@
+Index: linux-2.6.21.7/arch/mips/defconfig
+===================================================================
+--- linux-2.6.21.7.orig/arch/mips/defconfig
++++ linux-2.6.21.7/arch/mips/defconfig
+@@ -69,6 +69,7 @@ CONFIG_GENERIC_FIND_NEXT_BIT=y
+ CONFIG_GENERIC_HWEIGHT=y
+ CONFIG_GENERIC_CALIBRATE_DELAY=y
+ CONFIG_GENERIC_TIME=y
++CONFIG_GENERIC_GPIO=n
+ CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
+ # CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ is not set
+ CONFIG_ARC=y
+Index: linux-2.6.21.7/arch/mips/Kconfig
+===================================================================
+--- linux-2.6.21.7.orig/arch/mips/Kconfig
++++ linux-2.6.21.7/arch/mips/Kconfig
+@@ -869,6 +869,10 @@ config GENERIC_TIME
+       bool
+       default y
++config GENERIC_GPIO
++      bool
++      default n
++
+ config SCHED_NO_NO_OMIT_FRAME_POINTER
+       bool
+       default y
+Index: linux-2.6.21.7/include/asm-mips/gpio.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/include/asm-mips/gpio.h
+@@ -0,0 +1,6 @@
++#ifndef _ASM_MIPS_GPIO_H
++#define _ASM_MIPS_GPIO_H
++
++#include <gpio.h>
++
++#endif /* _ASM_MIPS_GPIO_H */
diff --git a/target/linux/generic-2.6/patches-2.6.21/060-rootfs_split.patch b/target/linux/generic-2.6/patches-2.6.21/060-rootfs_split.patch
new file mode 100644 (file)
index 0000000..2103606
--- /dev/null
@@ -0,0 +1,492 @@
+Index: linux-2.6.21.7/drivers/mtd/Kconfig
+===================================================================
+--- linux-2.6.21.7.orig/drivers/mtd/Kconfig
++++ linux-2.6.21.7/drivers/mtd/Kconfig
+@@ -49,6 +49,16 @@ config MTD_PARTITIONS
+         devices. Partitioning on NFTL 'devices' is a different - that's the
+         'normal' form of partitioning used on a block device.
++config MTD_ROOTFS_ROOT_DEV
++      bool "Automatically set 'rootfs' partition to be root filesystem"
++      depends on MTD_PARTITIONS
++      default y
++
++config MTD_ROOTFS_SPLIT
++      bool "Automatically split 'rootfs' partition for squashfs"
++      depends on MTD_PARTITIONS
++      default y
++
+ config MTD_REDBOOT_PARTS
+       tristate "RedBoot partition table parsing"
+       depends on MTD_PARTITIONS
+Index: linux-2.6.21.7/drivers/mtd/mtdpart.c
+===================================================================
+--- linux-2.6.21.7.orig/drivers/mtd/mtdpart.c
++++ linux-2.6.21.7/drivers/mtd/mtdpart.c
+@@ -20,6 +20,8 @@
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/partitions.h>
+ #include <linux/mtd/compatmac.h>
++#include <linux/squashfs_fs.h>
++#include <linux/root_dev.h>
+ /* Our partition linked list */
+ static LIST_HEAD(mtd_partitions);
+@@ -308,6 +310,266 @@ int del_mtd_partitions(struct mtd_info *
+       return 0;
+ }
++static u_int32_t cur_offset = 0;
++static int add_one_partition(struct mtd_info *master, const struct mtd_partition *part,
++              int i, struct mtd_part **slp)
++{
++      struct mtd_part *slave;
++
++      /* allocate the partition structure */
++      slave = kzalloc (sizeof(*slave), GFP_KERNEL);
++      if (!slave) {
++              printk ("memory allocation error while creating partitions for \"%s\"\n",
++                      master->name);
++              del_mtd_partitions(master);
++              return -ENOMEM;
++      }
++      list_add(&slave->list, &mtd_partitions);
++
++      /* set up the MTD object for this partition */
++      slave->mtd.type = master->type;
++      slave->mtd.flags = master->flags & ~part->mask_flags;
++      slave->mtd.size = part->size;
++      slave->mtd.writesize = master->writesize;
++      slave->mtd.oobsize = master->oobsize;
++      slave->mtd.oobavail = master->oobavail;
++      slave->mtd.subpage_sft = master->subpage_sft;
++
++      slave->mtd.name = part->name;
++      slave->mtd.bank_size = master->bank_size;
++      slave->mtd.owner = master->owner;
++
++      slave->mtd.read = part_read;
++      slave->mtd.write = part_write;
++
++      if(master->point && master->unpoint){
++              slave->mtd.point = part_point;
++              slave->mtd.unpoint = part_unpoint;
++      }
++
++      if (master->read_oob)
++              slave->mtd.read_oob = part_read_oob;
++      if (master->write_oob)
++              slave->mtd.write_oob = part_write_oob;
++      if(master->read_user_prot_reg)
++              slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
++      if(master->read_fact_prot_reg)
++              slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
++      if(master->write_user_prot_reg)
++              slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
++      if(master->lock_user_prot_reg)
++              slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
++      if(master->get_user_prot_info)
++              slave->mtd.get_user_prot_info = part_get_user_prot_info;
++      if(master->get_fact_prot_info)
++              slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
++      if (master->sync)
++              slave->mtd.sync = part_sync;
++      if (!i && master->suspend && master->resume) {
++                      slave->mtd.suspend = part_suspend;
++                      slave->mtd.resume = part_resume;
++      }
++      if (master->writev)
++              slave->mtd.writev = part_writev;
++      if (master->lock)
++              slave->mtd.lock = part_lock;
++      if (master->unlock)
++              slave->mtd.unlock = part_unlock;
++      if (master->block_isbad)
++              slave->mtd.block_isbad = part_block_isbad;
++      if (master->block_markbad)
++              slave->mtd.block_markbad = part_block_markbad;
++      slave->mtd.erase = part_erase;
++      slave->master = master;
++      slave->offset = part->offset;
++      slave->index = i;
++
++      if (slave->offset == MTDPART_OFS_APPEND)
++              slave->offset = cur_offset;
++      if (slave->offset == MTDPART_OFS_NXTBLK) {
++              slave->offset = cur_offset;
++              if ((cur_offset % master->erasesize) != 0) {
++                      /* Round up to next erasesize */
++                      slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize;
++                      printk(KERN_NOTICE "Moving partition %d: "
++                             "0x%08x -> 0x%08x\n", i,
++                             cur_offset, slave->offset);
++              }
++      }
++      if (slave->mtd.size == MTDPART_SIZ_FULL)
++              slave->mtd.size = master->size - slave->offset;
++      cur_offset = slave->offset + slave->mtd.size;
++
++      printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,
++              slave->offset + slave->mtd.size, slave->mtd.name);
++
++      /* let's do some sanity checks */
++      if (slave->offset >= master->size) {
++                      /* let's register it anyway to preserve ordering */
++              slave->offset = 0;
++              slave->mtd.size = 0;
++              printk ("mtd: partition \"%s\" is out of reach -- disabled\n",
++                      part->name);
++      }
++      if (slave->offset + slave->mtd.size > master->size) {
++              slave->mtd.size = master->size - slave->offset;
++              printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",
++                      part->name, master->name, slave->mtd.size);
++      }
++      if (master->numeraseregions>1) {
++              /* Deal with variable erase size stuff */
++              int i;
++              struct mtd_erase_region_info *regions = master->eraseregions;
++
++              /* Find the first erase regions which is part of this partition. */
++              for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)
++                      ;
++
++              for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) {
++                      if (slave->mtd.erasesize < regions[i].erasesize) {
++                              slave->mtd.erasesize = regions[i].erasesize;
++                      }
++              }
++      } else {
++              /* Single erase size */
++              slave->mtd.erasesize = master->erasesize;
++      }
++
++      if ((slave->mtd.flags & MTD_WRITEABLE) &&
++          (slave->offset % slave->mtd.erasesize)) {
++              /* Doesn't start on a boundary of major erase size */
++              /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */
++              slave->mtd.flags &= ~MTD_WRITEABLE;
++              printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
++                      part->name);
++      }
++      if ((slave->mtd.flags & MTD_WRITEABLE) &&
++          (slave->mtd.size % slave->mtd.erasesize)) {
++              slave->mtd.flags &= ~MTD_WRITEABLE;
++              printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
++                      part->name);
++      }
++
++      slave->mtd.ecclayout = master->ecclayout;
++      if (master->block_isbad) {
++              uint32_t offs = 0;
++
++              while(offs < slave->mtd.size) {
++                      if (master->block_isbad(master,
++                                              offs + slave->offset))
++                              slave->mtd.ecc_stats.badblocks++;
++                      offs += slave->mtd.erasesize;
++              }
++      }
++
++      if(part->mtdp)
++      {       /* store the object pointer (caller may or may not register it */
++              *part->mtdp = &slave->mtd;
++              slave->registered = 0;
++      }
++      else
++      {
++              /* register our partition */
++              add_mtd_device(&slave->mtd);
++              slave->registered = 1;
++      }
++
++      if (slp)
++              *slp = slave;
++
++      return 0;
++}
++
++#ifdef CONFIG_MTD_ROOTFS_SPLIT
++#define ROOTFS_SPLIT_NAME "rootfs_data"
++static int split_squashfs(struct mtd_info *master, struct mtd_partition *old,
++              struct mtd_partition **new)
++{
++      struct mtd_partition *part = NULL;
++      int len;
++      char buf[512];
++      struct squashfs_super_block *sb = (struct squashfs_super_block *) buf;
++      int ret;
++
++      ret = master->read(master, old->offset, sizeof(*sb), &len, buf);
++      if (ret) {
++              printk(KERN_ALERT "split_squashfs: error occured while reading "
++                      "from \"%s\"\n", master->name);
++              goto out;
++      }
++
++      if (len != sizeof(*sb)) {
++              printk(KERN_ALERT "split_squashfs: unable to read superblock "
++                      "from \"%s\"\n", master->name);
++              ret=-1;
++              goto out;
++      }
++
++      if (*((u32 *) buf) != SQUASHFS_MAGIC) {
++              printk(KERN_ALERT "split_squasfs: no squashfs found in \"%s\"\n",
++                      master->name);
++              ret=0;
++              goto out;
++      }
++
++      if (sb->bytes_used <= 0) {
++              printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n",
++                      master->name);
++              ret=0;
++              goto out;
++      }
++
++      part = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL);
++      if (part == NULL) {
++              printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n",
++                      ROOTFS_SPLIT_NAME);
++              ret = -ENOMEM;
++              goto out;
++      }
++
++      memcpy(part, old, sizeof(*part));
++      part->name = (unsigned char *)&part[1];
++      strcpy(part->name, ROOTFS_SPLIT_NAME);
++
++      len = (u32) sb->bytes_used;
++      len += (part->offset & 0x000fffff);
++      len +=  (master->erasesize - 1);
++      len &= ~(master->erasesize - 1);
++      len -= (part->offset & 0x000fffff);
++      part->offset += len;
++      part->size -= len;
++
++      ret = 0;
++
++out:
++      *new = part;
++      return ret;
++}
++
++static int split_rootfs_data(struct mtd_info *master, struct mtd_partition *part,
++              int index)
++{
++      struct mtd_partition *dpart;
++      int ret;
++
++      ret = split_squashfs(master, part, &dpart);
++      if (ret)
++              return ret;
++
++      if (dpart == NULL)
++              return 1;
++
++      printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=%X, len=%X \n",
++              ROOTFS_SPLIT_NAME, dpart->offset, dpart->size);
++
++      ret = add_one_partition(master, dpart, index, NULL);
++      if (ret)
++              kfree(dpart);
++
++      return ret;
++}
++#endif /* CONFIG_MTD_ROOTFS_SPLIT */
++
+ /*
+  * This function, given a master MTD object and a partition table, creates
+  * and registers slave MTD objects which are bound to the master according to
+@@ -320,169 +582,31 @@ int add_mtd_partitions(struct mtd_info *
+                      int nbparts)
+ {
+       struct mtd_part *slave;
+-      u_int32_t cur_offset = 0;
+-      int i;
++      struct mtd_partition *part;
++      int i, j, ret = 0;
+       printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
+-      for (i = 0; i < nbparts; i++) {
+-
+-              /* allocate the partition structure */
+-              slave = kzalloc (sizeof(*slave), GFP_KERNEL);
+-              if (!slave) {
+-                      printk ("memory allocation error while creating partitions for \"%s\"\n",
+-                              master->name);
+-                      del_mtd_partitions(master);
+-                      return -ENOMEM;
+-              }
+-              list_add(&slave->list, &mtd_partitions);
+-
+-              /* set up the MTD object for this partition */
+-              slave->mtd.type = master->type;
+-              slave->mtd.flags = master->flags & ~parts[i].mask_flags;
+-              slave->mtd.size = parts[i].size;
+-              slave->mtd.writesize = master->writesize;
+-              slave->mtd.oobsize = master->oobsize;
+-              slave->mtd.oobavail = master->oobavail;
+-              slave->mtd.subpage_sft = master->subpage_sft;
+-
+-              slave->mtd.name = parts[i].name;
+-              slave->mtd.bank_size = master->bank_size;
+-              slave->mtd.owner = master->owner;
+-
+-              slave->mtd.read = part_read;
+-              slave->mtd.write = part_write;
+-
+-              if(master->point && master->unpoint){
+-                      slave->mtd.point = part_point;
+-                      slave->mtd.unpoint = part_unpoint;
+-              }
+-
+-              if (master->read_oob)
+-                      slave->mtd.read_oob = part_read_oob;
+-              if (master->write_oob)
+-                      slave->mtd.write_oob = part_write_oob;
+-              if(master->read_user_prot_reg)
+-                      slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
+-              if(master->read_fact_prot_reg)
+-                      slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
+-              if(master->write_user_prot_reg)
+-                      slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
+-              if(master->lock_user_prot_reg)
+-                      slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
+-              if(master->get_user_prot_info)
+-                      slave->mtd.get_user_prot_info = part_get_user_prot_info;
+-              if(master->get_fact_prot_info)
+-                      slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
+-              if (master->sync)
+-                      slave->mtd.sync = part_sync;
+-              if (!i && master->suspend && master->resume) {
+-                              slave->mtd.suspend = part_suspend;
+-                              slave->mtd.resume = part_resume;
+-              }
+-              if (master->writev)
+-                      slave->mtd.writev = part_writev;
+-              if (master->lock)
+-                      slave->mtd.lock = part_lock;
+-              if (master->unlock)
+-                      slave->mtd.unlock = part_unlock;
+-              if (master->block_isbad)
+-                      slave->mtd.block_isbad = part_block_isbad;
+-              if (master->block_markbad)
+-                      slave->mtd.block_markbad = part_block_markbad;
+-              slave->mtd.erase = part_erase;
+-              slave->master = master;
+-              slave->offset = parts[i].offset;
+-              slave->index = i;
+-
+-              if (slave->offset == MTDPART_OFS_APPEND)
+-                      slave->offset = cur_offset;
+-              if (slave->offset == MTDPART_OFS_NXTBLK) {
+-                      slave->offset = cur_offset;
+-                      if ((cur_offset % master->erasesize) != 0) {
+-                              /* Round up to next erasesize */
+-                              slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize;
+-                              printk(KERN_NOTICE "Moving partition %d: "
+-                                     "0x%08x -> 0x%08x\n", i,
+-                                     cur_offset, slave->offset);
+-                      }
+-              }
+-              if (slave->mtd.size == MTDPART_SIZ_FULL)
+-                      slave->mtd.size = master->size - slave->offset;
+-              cur_offset = slave->offset + slave->mtd.size;
+-
+-              printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset,
+-                      slave->offset + slave->mtd.size, slave->mtd.name);
+-
+-              /* let's do some sanity checks */
+-              if (slave->offset >= master->size) {
+-                              /* let's register it anyway to preserve ordering */
+-                      slave->offset = 0;
+-                      slave->mtd.size = 0;
+-                      printk ("mtd: partition \"%s\" is out of reach -- disabled\n",
+-                              parts[i].name);
+-              }
+-              if (slave->offset + slave->mtd.size > master->size) {
+-                      slave->mtd.size = master->size - slave->offset;
+-                      printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n",
+-                              parts[i].name, master->name, slave->mtd.size);
+-              }
+-              if (master->numeraseregions>1) {
+-                      /* Deal with variable erase size stuff */
+-                      int i;
+-                      struct mtd_erase_region_info *regions = master->eraseregions;
+-
+-                      /* Find the first erase regions which is part of this partition. */
+-                      for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++)
+-                              ;
+-
+-                      for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) {
+-                              if (slave->mtd.erasesize < regions[i].erasesize) {
+-                                      slave->mtd.erasesize = regions[i].erasesize;
+-                              }
+-                      }
+-              } else {
+-                      /* Single erase size */
+-                      slave->mtd.erasesize = master->erasesize;
+-              }
+-
+-              if ((slave->mtd.flags & MTD_WRITEABLE) &&
+-                  (slave->offset % slave->mtd.erasesize)) {
+-                      /* Doesn't start on a boundary of major erase size */
+-                      /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */
+-                      slave->mtd.flags &= ~MTD_WRITEABLE;
+-                      printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
+-                              parts[i].name);
+-              }
+-              if ((slave->mtd.flags & MTD_WRITEABLE) &&
+-                  (slave->mtd.size % slave->mtd.erasesize)) {
+-                      slave->mtd.flags &= ~MTD_WRITEABLE;
+-                      printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
+-                              parts[i].name);
+-              }
+-
+-              slave->mtd.ecclayout = master->ecclayout;
+-              if (master->block_isbad) {
+-                      uint32_t offs = 0;
+-
+-                      while(offs < slave->mtd.size) {
+-                              if (master->block_isbad(master,
+-                                                      offs + slave->offset))
+-                                      slave->mtd.ecc_stats.badblocks++;
+-                              offs += slave->mtd.erasesize;
++      for (i = 0, j = 0; i < nbparts; i++) {
++              part = (struct mtd_partition *) &parts[i];
++              ret = add_one_partition(master, part, j, &slave);
++              if (ret)
++                      return ret;
++              j++;
++
++              if (strcmp(part->name, "rootfs") == 0 && slave->registered) {
++#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
++                      if (ROOT_DEV == 0) {
++                              printk(KERN_NOTICE "mtd: partition \"rootfs\" "
++                                      "set to be root filesystem\n");
++                              ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index);
+                       }
+-              }
+-
+-              if(parts[i].mtdp)
+-              {       /* store the object pointer (caller may or may not register it */
+-                      *parts[i].mtdp = &slave->mtd;
+-                      slave->registered = 0;
+-              }
+-              else
+-              {
+-                      /* register our partition */
+-                      add_mtd_device(&slave->mtd);
+-                      slave->registered = 1;
++#endif
++#ifdef CONFIG_MTD_ROOTFS_SPLIT
++                      ret = split_rootfs_data(master, part, j);
++                      if (ret == 0)
++                              j++;
++#endif
+               }
+       }
diff --git a/target/linux/generic-2.6/patches-2.6.21/065-block2mtd_init.patch b/target/linux/generic-2.6/patches-2.6.21/065-block2mtd_init.patch
new file mode 100644 (file)
index 0000000..0e36f9a
--- /dev/null
@@ -0,0 +1,114 @@
+Index: linux-2.6.21.7/drivers/mtd/devices/block2mtd.c
+===================================================================
+--- linux-2.6.21.7.orig/drivers/mtd/devices/block2mtd.c
++++ linux-2.6.21.7/drivers/mtd/devices/block2mtd.c
+@@ -16,6 +16,7 @@
+ #include <linux/list.h>
+ #include <linux/init.h>
+ #include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
+ #include <linux/buffer_head.h>
+ #include <linux/mutex.h>
+ #include <linux/mount.h>
+@@ -288,10 +289,11 @@ static void block2mtd_free_device(struct
+ /* FIXME: ensure that mtd->size % erase_size == 0 */
+-static struct block2mtd_dev *add_device(char *devname, int erase_size)
++static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname)
+ {
+       struct block_device *bdev;
+       struct block2mtd_dev *dev;
++      struct mtd_partition *part;
+       if (!devname)
+               return NULL;
+@@ -330,14 +332,18 @@ static struct block2mtd_dev *add_device(
+       /* Setup the MTD structure */
+       /* make the name contain the block device in */
+-      dev->mtd.name = kmalloc(sizeof("block2mtd: ") + strlen(devname),
+-                      GFP_KERNEL);
++
++      if (!mtdname)
++              mtdname = devname;
++
++      dev->mtd.name = kmalloc(strlen(mtdname) + 1, GFP_KERNEL);
++
+       if (!dev->mtd.name)
+               goto devinit_err;
++      
++      strcpy(dev->mtd.name, mtdname);
+-      sprintf(dev->mtd.name, "block2mtd: %s", devname);
+-
+-      dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
++      dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK & ~(erase_size - 1);
+       dev->mtd.erasesize = erase_size;
+       dev->mtd.writesize = 1;
+       dev->mtd.type = MTD_RAM;
+@@ -349,15 +355,18 @@ static struct block2mtd_dev *add_device(
+       dev->mtd.read = block2mtd_read;
+       dev->mtd.priv = dev;
+       dev->mtd.owner = THIS_MODULE;
+-
+-      if (add_mtd_device(&dev->mtd)) {
++      
++      part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL);
++      part->name = dev->mtd.name;
++      part->offset = 0;
++      part->size = dev->mtd.size;
++      if (add_mtd_partitions(&dev->mtd, part, 1)) {
+               /* Device didnt get added, so free the entry */
+               goto devinit_err;
+       }
+       list_add(&dev->list, &blkmtd_device_list);
+       INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index,
+-                      dev->mtd.name + strlen("blkmtd: "),
+-                      dev->mtd.erasesize >> 10, dev->mtd.erasesize);
++                      mtdname, dev->mtd.erasesize >> 10, dev->mtd.erasesize);
+       return dev;
+ devinit_err:
+@@ -430,9 +439,9 @@ static __initdata char block2mtd_paramli
+ static int block2mtd_setup2(const char *val)
+ {
+-      char buf[80 + 12]; /* 80 for device, 12 for erase size */
++      char buf[80 + 12 + 80]; /* 80 for device, 12 for erase size, 80 for name */
+       char *str = buf;
+-      char *token[2];
++      char *token[3];
+       char *name;
+       size_t erase_size = PAGE_SIZE;
+       int i, ret;
+@@ -443,7 +452,7 @@ static int block2mtd_setup2(const char *
+       strcpy(str, val);
+       kill_final_newline(str);
+-      for (i = 0; i < 2; i++)
++      for (i = 0; i < 3; i++)
+               token[i] = strsep(&str, ",");
+       if (str)
+@@ -463,8 +472,10 @@ static int block2mtd_setup2(const char *
+                       parse_err("illegal erase size");
+               }
+       }
++      if (token[2] && (strlen(token[2]) + 1 > 80))
++              parse_err("mtd device name too long");
+-      add_device(name, erase_size);
++      add_device(name, erase_size, token[2]);
+       return 0;
+ }
+@@ -498,7 +509,7 @@ static int block2mtd_setup(const char *v
+ module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200);
+-MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\"");
++MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>[,<name>]]\"");
+ static int __init block2mtd_init(void)
+ {
diff --git a/target/linux/generic-2.6/patches-2.6.21/100-netfilter_layer7_2.17.patch b/target/linux/generic-2.6/patches-2.6.21/100-netfilter_layer7_2.17.patch
new file mode 100644 (file)
index 0000000..52908b3
--- /dev/null
@@ -0,0 +1,2123 @@
+Index: linux-2.6.21.7/net/netfilter/Kconfig
+===================================================================
+--- linux-2.6.21.7.orig/net/netfilter/Kconfig
++++ linux-2.6.21.7/net/netfilter/Kconfig
+@@ -640,6 +640,27 @@ config NETFILTER_XT_MATCH_STATE
+         To compile it as a module, choose M here.  If unsure, say N.
++config NETFILTER_XT_MATCH_LAYER7
++      tristate '"layer7" match support'
++      depends on NETFILTER_XTABLES
++      depends on EXPERIMENTAL && (IP_NF_CONNTRACK || NF_CONNTRACK)
++       depends on NF_CT_ACCT
++      help
++        Say Y if you want to be able to classify connections (and their
++        packets) based on regular expression matching of their application
++        layer data.   This is one way to classify applications such as
++        peer-to-peer filesharing systems that do not always use the same
++        port.
++
++        To compile it as a module, choose M here.  If unsure, say N.
++
++config NETFILTER_XT_MATCH_LAYER7_DEBUG
++        bool 'Layer 7 debugging output'
++        depends on NETFILTER_XT_MATCH_LAYER7
++        help
++          Say Y to get lots of debugging output.
++
++
+ config NETFILTER_XT_MATCH_STATISTIC
+       tristate '"statistic" match support'
+       depends on NETFILTER_XTABLES
+Index: linux-2.6.21.7/net/netfilter/Makefile
+===================================================================
+--- linux-2.6.21.7.orig/net/netfilter/Makefile
++++ linux-2.6.21.7/net/netfilter/Makefile
+@@ -68,6 +68,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) +
+ obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o
+ obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o
+ obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o
++obj-$(CONFIG_NETFILTER_XT_MATCH_LAYER7) += xt_layer7.o
+ obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o
+ obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
+ obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
+Index: linux-2.6.21.7/net/netfilter/xt_layer7.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/net/netfilter/xt_layer7.c
+@@ -0,0 +1,634 @@
++/*
++  Kernel module to match application layer (OSI layer 7) data in connections.
++
++  http://l7-filter.sf.net
++
++  (C) 2003, 2004, 2005, 2006, 2007 Matthew Strait and Ethan Sommer.
++
++  This program is free software; you can redistribute it and/or
++  modify it under the terms of the GNU General Public License
++  as published by the Free Software Foundation; either version
++  2 of the License, or (at your option) any later version.
++  http://www.gnu.org/licenses/gpl.txt
++
++  Based on ipt_string.c (C) 2000 Emmanuel Roger <winfield@freegates.be>,
++  xt_helper.c (C) 2002 Harald Welte and cls_layer7.c (C) 2003 Matthew Strait,
++  Ethan Sommer, Justin Levandoski.
++*/
++
++#include <linux/spinlock.h>
++#include <linux/version.h>
++#include <net/ip.h>
++#include <net/tcp.h>
++#include <linux/module.h>
++#include <linux/skbuff.h>
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_conntrack.h>
++#include <net/netfilter/nf_conntrack_core.h>
++#include <linux/netfilter/x_tables.h>
++#include <linux/netfilter/xt_layer7.h>
++#include <linux/ctype.h>
++#include <linux/proc_fs.h>
++
++#include "regexp/regexp.c"
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>");
++MODULE_DESCRIPTION("iptables application layer match module");
++MODULE_ALIAS("ipt_layer7");
++MODULE_VERSION("2.17");
++
++static int maxdatalen = 2048; // this is the default
++module_param(maxdatalen, int, 0444);
++MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter");
++#ifdef CONFIG_NETFILTER_XT_MATCH_LAYER7_DEBUG
++      #define DPRINTK(format,args...) printk(format,##args)
++#else
++      #define DPRINTK(format,args...)
++#endif
++
++#define TOTAL_PACKETS master_conntrack->counters[IP_CT_DIR_ORIGINAL].packets + \
++                    master_conntrack->counters[IP_CT_DIR_REPLY].packets
++
++/* Number of packets whose data we look at.
++This can be modified through /proc/net/layer7_numpackets */
++static int num_packets = 10;
++
++static struct pattern_cache {
++      char * regex_string;
++      regexp * pattern;
++      struct pattern_cache * next;
++} * first_pattern_cache = NULL;
++
++DEFINE_SPINLOCK(l7_lock);
++
++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
++/* Converts an unfriendly string into a friendly one by
++replacing unprintables with periods and all whitespace with " ". */
++static char * friendly_print(unsigned char * s)
++{
++      char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC);
++      int i;
++
++      if(!f) {
++              if (net_ratelimit())
++                      printk(KERN_ERR "layer7: out of memory in "
++                                      "friendly_print, bailing.\n");
++              return NULL;
++      }
++
++      for(i = 0; i < strlen(s); i++){
++              if(isprint(s[i]) && s[i] < 128) f[i] = s[i];
++              else if(isspace(s[i]))          f[i] = ' ';
++              else                            f[i] = '.';
++      }
++      f[i] = '\0';
++      return f;
++}
++
++static char dec2hex(int i)
++{
++      switch (i) {
++              case 0 ... 9:
++                      return (i + '0');
++                      break;
++              case 10 ... 15:
++                      return (i - 10 + 'a');
++                      break;
++              default:
++                      if (net_ratelimit())
++                              printk("layer7: Problem in dec2hex\n");
++                      return '\0';
++      }
++}
++
++static char * hex_print(unsigned char * s)
++{
++      char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC);
++      int i;
++
++      if(!g) {
++             if (net_ratelimit())
++                      printk(KERN_ERR "layer7: out of memory in hex_print, "
++                                      "bailing.\n");
++             return NULL;
++      }
++
++      for(i = 0; i < strlen(s); i++) {
++              g[i*3    ] = dec2hex(s[i]/16);
++              g[i*3 + 1] = dec2hex(s[i]%16);
++              g[i*3 + 2] = ' ';
++      }
++      g[i*3] = '\0';
++
++      return g;
++}
++#endif // DEBUG
++
++/* Use instead of regcomp.  As we expect to be seeing the same regexps over and
++over again, it make sense to cache the results. */
++static regexp * compile_and_cache(const char * regex_string, 
++                                  const char * protocol)
++{
++      struct pattern_cache * node               = first_pattern_cache;
++      struct pattern_cache * last_pattern_cache = first_pattern_cache;
++      struct pattern_cache * tmp;
++      unsigned int len;
++
++      while (node != NULL) {
++              if (!strcmp(node->regex_string, regex_string))
++              return node->pattern;
++
++              last_pattern_cache = node;/* points at the last non-NULL node */
++              node = node->next;
++      }
++
++      /* If we reach the end of the list, then we have not yet cached
++         the pattern for this regex. Let's do that now.
++         Be paranoid about running out of memory to avoid list corruption. */
++      tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC);
++
++      if(!tmp) {
++              if (net_ratelimit())
++                      printk(KERN_ERR "layer7: out of memory in "
++                                      "compile_and_cache, bailing.\n");
++              return NULL;
++      }
++
++      tmp->regex_string  = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC);
++      tmp->pattern       = kmalloc(sizeof(struct regexp),    GFP_ATOMIC);
++      tmp->next = NULL;
++
++      if(!tmp->regex_string || !tmp->pattern) {
++              if (net_ratelimit())
++                      printk(KERN_ERR "layer7: out of memory in "
++                                      "compile_and_cache, bailing.\n");
++              kfree(tmp->regex_string);
++              kfree(tmp->pattern);
++              kfree(tmp);
++              return NULL;
++      }
++
++      /* Ok.  The new node is all ready now. */
++      node = tmp;
++
++      if(first_pattern_cache == NULL) /* list is empty */
++              first_pattern_cache = node; /* make node the beginning */
++      else
++              last_pattern_cache->next = node; /* attach node to the end */
++
++      /* copy the string and compile the regex */
++      len = strlen(regex_string);
++      DPRINTK("About to compile this: \"%s\"\n", regex_string);
++      node->pattern = regcomp((char *)regex_string, &len);
++      if ( !node->pattern ) {
++              if (net_ratelimit())
++                      printk(KERN_ERR "layer7: Error compiling regexp "
++                                      "\"%s\" (%s)\n", 
++                                      regex_string, protocol);
++              /* pattern is now cached as NULL, so we won't try again. */
++      }
++
++      strcpy(node->regex_string, regex_string);
++      return node->pattern;
++}
++
++static int can_handle(const struct sk_buff *skb)
++{
++      if(!ip_hdr(skb)) /* not IP */
++              return 0;
++      if(ip_hdr(skb)->protocol != IPPROTO_TCP &&
++         ip_hdr(skb)->protocol != IPPROTO_UDP &&
++         ip_hdr(skb)->protocol != IPPROTO_ICMP)
++              return 0;
++      return 1;
++}
++
++/* Returns offset the into the skb->data that the application data starts */
++static int app_data_offset(const struct sk_buff *skb)
++{
++      /* In case we are ported somewhere (ebtables?) where ip_hdr(skb)
++      isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */
++      int ip_hl = 4*ip_hdr(skb)->ihl;
++
++      if( ip_hdr(skb)->protocol == IPPROTO_TCP ) {
++              /* 12 == offset into TCP header for the header length field.
++              Can't get this with skb->h.th->doff because the tcphdr
++              struct doesn't get set when routing (this is confirmed to be
++              true in Netfilter as well as QoS.) */
++              int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4);
++
++              return ip_hl + tcp_hl;
++      } else if( ip_hdr(skb)->protocol == IPPROTO_UDP  ) {
++              return ip_hl + 8; /* UDP header is always 8 bytes */
++      } else if( ip_hdr(skb)->protocol == IPPROTO_ICMP ) {
++              return ip_hl + 8; /* ICMP header is 8 bytes */
++      } else {
++              if (net_ratelimit())
++                      printk(KERN_ERR "layer7: tried to handle unknown "
++                                      "protocol!\n");
++              return ip_hl + 8; /* something reasonable */
++      }
++}
++
++/* handles whether there's a match when we aren't appending data anymore */
++static int match_no_append(struct nf_conn * conntrack, 
++                           struct nf_conn * master_conntrack, 
++                           enum ip_conntrack_info ctinfo,
++                           enum ip_conntrack_info master_ctinfo,
++                           const struct xt_layer7_info * info)
++{
++      /* If we're in here, throw the app data away */
++      if(master_conntrack->layer7.app_data != NULL) {
++
++      #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG
++              if(!master_conntrack->layer7.app_proto) {
++                      char * f = 
++                        friendly_print(master_conntrack->layer7.app_data);
++                      char * g = 
++                        hex_print(master_conntrack->layer7.app_data);
++                      DPRINTK("\nl7-filter gave up after %d bytes "
++                              "(%d packets):\n%s\n",
++                              strlen(f), TOTAL_PACKETS, f);
++                      kfree(f);
++                      DPRINTK("In hex: %s\n", g);
++                      kfree(g);
++              }
++      #endif
++
++              kfree(master_conntrack->layer7.app_data);
++              master_conntrack->layer7.app_data = NULL; /* don't free again */
++      }
++
++      if(master_conntrack->layer7.app_proto){
++              /* Here child connections set their .app_proto (for /proc) */
++              if(!conntrack->layer7.app_proto) {
++                      conntrack->layer7.app_proto = 
++                        kmalloc(strlen(master_conntrack->layer7.app_proto)+1, 
++                          GFP_ATOMIC);
++                      if(!conntrack->layer7.app_proto){
++                              if (net_ratelimit())
++                                      printk(KERN_ERR "layer7: out of memory "
++                                                      "in match_no_append, "
++                                                      "bailing.\n");
++                              return 1;
++                      }
++                      strcpy(conntrack->layer7.app_proto, 
++                              master_conntrack->layer7.app_proto);
++              }
++
++              return (!strcmp(master_conntrack->layer7.app_proto, 
++                              info->protocol));
++      }
++      else {
++              /* If not classified, set to "unknown" to distinguish from
++              connections that are still being tested. */
++              master_conntrack->layer7.app_proto = 
++                      kmalloc(strlen("unknown")+1, GFP_ATOMIC);
++              if(!master_conntrack->layer7.app_proto){
++                      if (net_ratelimit())
++                              printk(KERN_ERR "layer7: out of memory in "
++                                              "match_no_append, bailing.\n");
++                      return 1;
++              }
++              strcpy(master_conntrack->layer7.app_proto, "unknown");
++              return 0;
++      }
++}
++
++/* add the new app data to the conntrack.  Return number of bytes added. */
++static int add_data(struct nf_conn * master_conntrack,
++                    char * app_data, int appdatalen)
++{
++      int length = 0, i;
++      int oldlength = master_conntrack->layer7.app_data_len;
++
++      /* This is a fix for a race condition by Deti Fliegl. However, I'm not 
++         clear on whether the race condition exists or whether this really 
++         fixes it.  I might just be being dense... Anyway, if it's not really 
++         a fix, all it does is waste a very small amount of time. */
++      if(!master_conntrack->layer7.app_data) return 0;
++
++      /* Strip nulls. Make everything lower case (our regex lib doesn't
++      do case insensitivity).  Add it to the end of the current data. */
++      for(i = 0; i < maxdatalen-oldlength-1 &&
++                 i < appdatalen; i++) {
++              if(app_data[i] != '\0') {
++                      /* the kernel version of tolower mungs 'upper ascii' */
++                      master_conntrack->layer7.app_data[length+oldlength] =
++                              isascii(app_data[i])? 
++                                      tolower(app_data[i]) : app_data[i];
++                      length++;
++              }
++      }
++
++      master_conntrack->layer7.app_data[length+oldlength] = '\0';
++      master_conntrack->layer7.app_data_len = length + oldlength;
++
++      return length;
++}
++
++/* taken from drivers/video/modedb.c */
++static int my_atoi(const char *s)
++{
++      int val = 0;
++
++      for (;; s++) {
++              switch (*s) {
++                      case '0'...'9':
++                      val = 10*val+(*s-'0');
++                      break;
++              default:
++                      return val;
++              }
++      }
++}
++
++/* write out num_packets to userland. */
++static int layer7_read_proc(char* page, char ** start, off_t off, int count,
++                            int* eof, void * data)
++{
++      if(num_packets > 99 && net_ratelimit())
++              printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n");
++
++      page[0] = num_packets/10 + '0';
++      page[1] = num_packets%10 + '0';
++      page[2] = '\n';
++      page[3] = '\0';
++
++      *eof=1;
++
++      return 3;
++}
++
++/* Read in num_packets from userland */
++static int layer7_write_proc(struct file* file, const char* buffer,
++                             unsigned long count, void *data)
++{
++      char * foo = kmalloc(count, GFP_ATOMIC);
++
++      if(!foo){
++              if (net_ratelimit())
++                      printk(KERN_ERR "layer7: out of memory, bailing. "
++                                      "num_packets unchanged.\n");
++              return count;
++      }
++
++      if(copy_from_user(foo, buffer, count)) {
++              return -EFAULT;
++      }
++
++
++      num_packets = my_atoi(foo);
++      kfree (foo);
++
++      /* This has an arbitrary limit to make the math easier. I'm lazy.
++      But anyway, 99 is a LOT! If you want more, you're doing it wrong! */
++      if(num_packets > 99) {
++              printk(KERN_WARNING "layer7: num_packets can't be > 99.\n");
++              num_packets = 99;
++      } else if(num_packets < 1) {
++              printk(KERN_WARNING "layer7: num_packets can't be < 1.\n");
++              num_packets = 1;
++      }
++
++      return count;
++}
++
++static int
++match(const struct sk_buff *skbin,
++      const struct net_device *in,
++      const struct net_device *out,
++      const struct xt_match *match,
++      const void *matchinfo,
++      int offset,
++      unsigned int protoff,
++      int *hotdrop)
++{
++      /* sidestep const without getting a compiler warning... */
++      struct sk_buff * skb = (struct sk_buff *)skbin; 
++
++      const struct xt_layer7_info * info = matchinfo;
++      enum ip_conntrack_info master_ctinfo, ctinfo;
++      struct nf_conn *master_conntrack, *conntrack;
++      unsigned char * app_data;
++      unsigned int pattern_result, appdatalen;
++      regexp * comppattern;
++
++      /* Be paranoid/incompetent - lock the entire match function. */
++      spin_lock_bh(&l7_lock);
++
++      if(!can_handle(skb)){
++              DPRINTK("layer7: This is some protocol I can't handle.\n");
++              spin_unlock_bh(&l7_lock);
++              return info->invert;
++      }
++
++      /* Treat parent & all its children together as one connection, except
++      for the purpose of setting conntrack->layer7.app_proto in the actual
++      connection. This makes /proc/net/ip_conntrack more satisfying. */
++      if(!(conntrack = nf_ct_get(skb, &ctinfo)) ||
++         !(master_conntrack=nf_ct_get(skb,&master_ctinfo))){
++              DPRINTK("layer7: couldn't get conntrack.\n");
++              spin_unlock_bh(&l7_lock);
++              return info->invert;
++      }
++
++      /* Try to get a master conntrack (and its master etc) for FTP, etc. */
++      while (master_ct(master_conntrack) != NULL)
++              master_conntrack = master_ct(master_conntrack);
++
++      /* if we've classified it or seen too many packets */
++      if(TOTAL_PACKETS > num_packets ||
++         master_conntrack->layer7.app_proto) {
++
++              pattern_result = match_no_append(conntrack, master_conntrack, 
++                                               ctinfo, master_ctinfo, info);
++
++              /* skb->cb[0] == seen. Don't do things twice if there are 
++              multiple l7 rules. I'm not sure that using cb for this purpose 
++              is correct, even though it says "put your private variables 
++              there". But it doesn't look like it is being used for anything
++              else in the skbs that make it here. */
++              skb->cb[0] = 1; /* marking it seen here's probably irrelevant */
++
++              spin_unlock_bh(&l7_lock);
++              return (pattern_result ^ info->invert);
++      }
++
++      if(skb_is_nonlinear(skb)){
++              if(skb_linearize(skb) != 0){
++                      if (net_ratelimit())
++                              printk(KERN_ERR "layer7: failed to linearize "
++                                              "packet, bailing.\n");
++                      spin_unlock_bh(&l7_lock);
++                      return info->invert;
++              }
++      }
++
++      /* now that the skb is linearized, it's safe to set these. */
++      app_data = skb->data + app_data_offset(skb);
++      appdatalen = skb_tail_pointer(skb) - app_data;
++
++      /* the return value gets checked later, when we're ready to use it */
++      comppattern = compile_and_cache(info->pattern, info->protocol);
++
++      /* On the first packet of a connection, allocate space for app data */
++      if(TOTAL_PACKETS == 1 && !skb->cb[0] && 
++         !master_conntrack->layer7.app_data){
++              master_conntrack->layer7.app_data = 
++                      kmalloc(maxdatalen, GFP_ATOMIC);
++              if(!master_conntrack->layer7.app_data){
++                      if (net_ratelimit())
++                              printk(KERN_ERR "layer7: out of memory in "
++                                              "match, bailing.\n");
++                      spin_unlock_bh(&l7_lock);
++                      return info->invert;
++              }
++
++              master_conntrack->layer7.app_data[0] = '\0';
++      }
++
++      /* Can be here, but unallocated, if numpackets is increased near
++      the beginning of a connection */
++      if(master_conntrack->layer7.app_data == NULL){
++              spin_unlock_bh(&l7_lock);
++              return (info->invert); /* unmatched */
++      }
++
++      if(!skb->cb[0]){
++              int newbytes;
++              newbytes = add_data(master_conntrack, app_data, appdatalen);
++
++              if(newbytes == 0) { /* didn't add any data */
++                      skb->cb[0] = 1;
++                      /* Didn't match before, not going to match now */
++                      spin_unlock_bh(&l7_lock);
++                      return info->invert;
++              }
++      }
++
++      /* If looking for "unknown", then never match.  "Unknown" means that
++      we've given up; we're still trying with these packets. */
++      if(!strcmp(info->protocol, "unknown")) {
++              pattern_result = 0;
++      /* If looking for "unset", then always match. "Unset" means that we
++      haven't yet classified the connection. */
++      } else if(!strcmp(info->protocol, "unset")) {
++              pattern_result = 2;
++              DPRINTK("layer7: matched unset: not yet classified "
++                      "(%d/%d packets)\n", TOTAL_PACKETS, num_packets);
++      /* If the regexp failed to compile, don't bother running it */
++      } else if(comppattern && 
++                regexec(comppattern, master_conntrack->layer7.app_data)){
++              DPRINTK("layer7: matched %s\n", info->protocol);
++              pattern_result = 1;
++      } else pattern_result = 0;
++
++      if(pattern_result == 1) {
++              master_conntrack->layer7.app_proto = 
++                      kmalloc(strlen(info->protocol)+1, GFP_ATOMIC);
++              if(!master_conntrack->layer7.app_proto){
++                      if (net_ratelimit())
++                              printk(KERN_ERR "layer7: out of memory in "
++                                              "match, bailing.\n");
++                      spin_unlock_bh(&l7_lock);
++                      return (pattern_result ^ info->invert);
++              }
++              strcpy(master_conntrack->layer7.app_proto, info->protocol);
++      } else if(pattern_result > 1) { /* cleanup from "unset" */
++              pattern_result = 1;
++      }
++
++      /* mark the packet seen */
++      skb->cb[0] = 1;
++
++      spin_unlock_bh(&l7_lock);
++      return (pattern_result ^ info->invert);
++}
++
++static int check(const char *tablename,
++               const void *inf,
++               const struct xt_match *match,
++               void *matchinfo,
++               unsigned int hook_mask)
++
++{
++      // load nf_conntrack_ipv4
++        if (nf_ct_l3proto_try_module_get(match->family) < 0) {
++                printk(KERN_WARNING "can't load conntrack support for "
++                                    "proto=%d\n", match->family);
++                return false;
++        }
++      return true;
++}
++
++static void
++destroy(const struct xt_match *match, void *matchinfo)
++{
++      nf_ct_l3proto_module_put(match->family);
++}
++
++static struct xt_match xt_layer7_match[] = {
++{
++      .name           = "layer7",
++      .family         = AF_INET,
++      .checkentry     = check,
++      .match          = match,
++      .destroy        = destroy,
++      .matchsize      = sizeof(struct xt_layer7_info),
++      .me             = THIS_MODULE
++}
++};
++
++static void layer7_cleanup_proc(void)
++{
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
++      remove_proc_entry("layer7_numpackets", proc_net);
++#else
++      remove_proc_entry("layer7_numpackets", init_net.proc_net);
++#endif
++}
++
++/* register the proc file */
++static void layer7_init_proc(void)
++{
++      struct proc_dir_entry* entry;
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
++      entry = create_proc_entry("layer7_numpackets", 0644, proc_net);
++#else
++      entry = create_proc_entry("layer7_numpackets", 0644, init_net.proc_net);
++#endif
++      entry->read_proc = layer7_read_proc;
++      entry->write_proc = layer7_write_proc;
++}
++
++static int __init xt_layer7_init(void)
++{
++      need_conntrack();
++
++      layer7_init_proc();
++      if(maxdatalen < 1) {
++              printk(KERN_WARNING "layer7: maxdatalen can't be < 1, "
++                      "using 1\n");
++              maxdatalen = 1;
++      }
++      /* This is not a hard limit.  It's just here to prevent people from
++      bringing their slow machines to a grinding halt. */
++      else if(maxdatalen > 65536) {
++              printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, "
++                      "using 65536\n");
++              maxdatalen = 65536;
++      }
++      return xt_register_matches(xt_layer7_match,
++                                 ARRAY_SIZE(xt_layer7_match));
++}
++
++static void __exit xt_layer7_fini(void)
++{
++      layer7_cleanup_proc();
++      xt_unregister_matches(xt_layer7_match, ARRAY_SIZE(xt_layer7_match));
++}
++
++module_init(xt_layer7_init);
++module_exit(xt_layer7_fini);
+Index: linux-2.6.21.7/net/netfilter/regexp/regexp.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.7/net/netfilter/regexp/regexp.c
+@@ -0,0 +1,1197 @@
++/*
++ * regcomp and regexec -- regsub and regerror are elsewhere
++ * @(#)regexp.c       1.3 of 18 April 87
++ *
++ *    Copyright (c) 1986 by University of Toronto.
++ *    Written by Henry Spencer.  Not derived from licensed software.
++ *
++ *    Permission is granted to anyone to use this software for any
++ *    purpose on any computer system, and to redistribute it freely,
++ *    subject to the following restrictions:
++ *
++ *    1. The author is not responsible for the consequences of use of
++ *            this software, no matter how awful, even if they arise
++ *            from defects in it.
++ *
++ *    2. The origin of this software must not be misrepresented, either
++ *            by explicit claim or by omission.
++ *
++ *    3. Altered versions must be plainly marked as such, and must not
++ *            be misrepresented as being the original software.
++ *
++ * Beware that some of this code is subtly aware of the way operator
++ * precedence is structured in regular expressions.  Serious changes in
++ * regular-expression syntax might require a total rethink.
++ *
++ * This code was modified by Ethan Sommer to work within the kernel
++ * (it now uses kmalloc etc..)
++ *
++ * Modified slightly by Matthew Strait to use more modern C.
++ */
++
++#include "regexp.h"
++#include "regmagic.h"
++
++/* added by ethan and matt.  Lets it work in both kernel and user space.
++(So iptables can use it, for instance.)  Yea, it goes both ways... */
++#if __KERNEL__
++  #define malloc(foo) kmalloc(foo,GFP_ATOMIC)
++#else
++  #define printk(format,args...) printf(format,##args)
++#endif
++
++void regerror(char * s)
++{
++        printk("<3>Regexp: %s\n", s);
++        /* NOTREACHED */
++}
++
++/*
++ * The "internal use only" fields in regexp.h are present to pass info from
++ * compile to execute that permits the execute phase to run lots faster on
++ * simple cases.  They are:
++ *
++ * regstart   char that must begin a match; '\0' if none obvious
++ * reganch    is the match anchored (at beginning-of-line only)?
++ * regmust    string (pointer into program) that match must include, or NULL
++ * regmlen    length of regmust string
++ *
++ * Regstart and reganch permit very fast decisions on suitable starting points
++ * for a match, cutting down the work a lot.  Regmust permits fast rejection
++ * of lines that cannot possibly match.  The regmust tests are costly enough
++ * that regcomp() supplies a regmust only if the r.e. contains something
++ * potentially expensive (at present, the only such thing detected is * or +
++ * at the start of the r.e., which can involve a lot of backup).  Regmlen is
++ * supplied because the test in regexec() needs it and regcomp() is computing
++ * it anyway.
++ */
++
++/*
++ * Structure for regexp "program".  This is essentially a linear encoding
++ * of a nondeterministic finite-state machine (aka syntax charts or
++ * "railroad normal form" in parsing technology).  Each node is an opcode
++ * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
++ * all nodes except BRANCH implement concatenation; a "next" pointer with
++ * a BRANCH on both ends of it is connecting two alternatives.  (Here we
++ * have one of the subtle syntax dependencies:  an individual BRANCH (as
++ * opposed to a collection of them) is never concatenated with anything
++ * because of operator precedence.)  The operand of some types of node is
++ * a literal string; for others, it is a node leading into a sub-FSM.  In
++ * particular, the operand of a BRANCH node is the first node of the branch.
++ * (NB this is *not* a tree structure:  the tail of the branch connects
++ * to the thing following the set of BRANCHes.)  The opcodes are:
++ */
++
++/* definition number  opnd?   meaning */
++#define       END     0       /* no   End of program. */
++#define       BOL     1       /* no   Match "" at beginning of line. */
++#define       EOL     2       /* no   Match "" at end of line. */
++#define       ANY     3       /* no   Match any one character. */
++#define       ANYOF   4       /* str  Match any character in this string. */
++#define       ANYBUT  5       /* str  Match any character not in this string. */
++#define       BRANCH  6       /* node Match this alternative, or the next... */
++#define       BACK    7       /* no   Match "", "next" ptr points backward. */
++#define       EXACTLY 8       /* str  Match this string. */
++#define       NOTHING 9       /* no   Match empty string. */
++#define       STAR    10      /* node Match this (simple) thing 0 or more times. */
++#define       PLUS    11      /* node Match this (simple) thing 1 or more times. */
++#define       OPEN    20      /* no   Mark this point in input as start of #n. */
++                      /*      OPEN+1 is number 1, etc. */
++#define       CLOSE   30      /* no   Analogous to OPEN. */
++
++/*
++ * Opcode notes:
++ *
++ * BRANCH     The set of branches constituting a single choice are hooked
++ *            together with their "next" pointers, since precedence prevents
++ *            anything being concatenated to any individual branch.  The
++ *            "next" pointer of the last BRANCH in a choice points to the
++ *            thing following the whole choice.  This is also where the
++ *            final "next" pointer of each individual branch points; each
++ *            branch starts with the operand node of a BRANCH node.
++ *
++ * BACK               Normal "next" pointers all implicitly point forward; BACK
++ *            exists to make loop structures possible.
++ *
++ * STAR,PLUS  '?', and complex '*' and '+', are implemented as circular
++ *            BRANCH structures using BACK.  Simple cases (one character
++ *            per match) are implemented with STAR and PLUS for speed
++ *            and to minimize recursive plunges.
++ *
++ * OPEN,CLOSE ...are numbered at compile time.
++ */
++
++/*
++ * A node is one char of opcode followed by two chars of "next" pointer.
++ * "Next" pointers are stored as two 8-bit pieces, high order first.  The
++ * value is a positive offset from the opcode of the node containing it.
++ * An operand, if any, simply follows the node.  (Note that much of the
++ * code generation knows about this implicit relationship.)
++ *
++ * Using two bytes for the "next" pointer is vast overkill for most things,
++ * but allows patterns to get big without disasters.
++ */
++#define       OP(p)   (*(p))
++#define       NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
++#define       OPERAND(p)      ((p) + 3)
++
++/*
++ * See regmagic.h for one further detail of program structure.
++ */
++
++
++/*
++ * Utility definitions.
++ */
++#ifndef CHARBITS
++#define       UCHARAT(p)      ((int)*(unsigned char *)(p))
++#else
++#define       UCHARAT(p)      ((int)*(p)&CHARBITS)
++#endif
++
++#define       FAIL(m) { regerror(m); return(NULL); }
++#define       ISMULT(c)       ((c) == '*' || (c) == '+' || (c) == '?')
++#define       META    "^$.[()|?+*\\"
++
++/*
++ * Flags to be passed up and down.
++ */
++#define       HASWIDTH        01      /* Known never to match null string. */
++#define       SIMPLE          02      /* Simple enough to be STAR/PLUS operand. */
++#define       SPSTART         04      /* Starts with * or +. */
++#define       WORST           0       /* Worst case. */
++
++/*
++ * Global work variables for regcomp().
++ */
++struct match_globals {
++char *reginput;               /* String-input pointer. */
++char *regbol;         /* Beginning of input, for ^ check. */
++char **regstartp;     /* Pointer to startp array. */
++char **regendp;               /* Ditto for endp. */
++char *regparse;               /* Input-scan pointer. */
++int regnpar;          /* () count. */
++char regdummy;
++char *regcode;                /* Code-emit pointer; &regdummy = don't. */
++long regsize;         /* Code size. */
++};
++
++/*
++ * Forward declarations for regcomp()'s friends.
++ */
++#ifndef STATIC
++#define       STATIC  static
++#endif
++STATIC char *reg(struct match_globals *g, int paren,int *flagp);
++STATIC char *regbranch(struct match_globals *g, int *flagp);
++STATIC char *regpiece(struct match_globals *g, int *flagp);
++STATIC char *regatom(struct match_globals *g, int *flagp);
++STATIC char *regnode(struct match_globals *g, char op);
++STATIC char *regnext(struct match_globals *g, char *p);
++STATIC void regc(struct match_globals *g, char b);
++STATIC void reginsert(struct match_globals *g, char op, char *opnd);
++STATIC void regtail(struct match_globals *g, char *p, char *val);
++STATIC void regoptail(struct match_globals *g, char *p, char *val);
++
++
++__kernel_size_t my_strcspn(const char *s1,const char *s2)
++{
++        char *scan1;
++        char *scan2;
++        int count;
++
++        count = 0;
++        for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) {
++                for (scan2 = (char *)s2; *scan2 != '\0';)       /* ++ moved down. */
++                        if (*scan1 == *scan2++)
++                                return(count);
++                count++;
++        }
++        return(count);
++}
++
++/*
++ - regcomp - compile a regular expression into internal code
++ *
++ * We can't allocate space until we know how big the compiled form will be,
++ * but we can't compile it (and thus know how big it is) until we've got a
++ * place to put the code.  So we cheat:  we compile it twice, once with code
++ * generation turned off and size counting turned on, and once "for real".
++ * This also means that we don't allocate space until we are sure that the
++ * thing really will compile successfully, and we never have to move the
++ * code and thus invalidate pointers into it.  (Note that it has to be in
++ * one piece because free() must be able to free it all.)
++ *
++ * Beware that the optimization-preparation code in here knows about some
++ * of the structure of the compiled regexp.
++ */
++regexp *
++regcomp(char *exp,int *patternsize)
++{
++      register regexp *r;
++      register char *scan;
++      register char *longest;
++      register int len;
++      int flags;
++      struct match_globals g;
++      
++      /* commented out by ethan
++         extern char *malloc();
++      */
++
++      if (exp == NULL)
++              FAIL("NULL argument");
++
++      /* First pass: determine size, legality. */
++      g.regparse = exp;
++      g.regnpar = 1;
++      g.regsize = 0L;
++      g.regcode = &g.regdummy;
++      regc(&g, MAGIC);
++      if (reg(&g, 0, &flags) == NULL)
++              return(NULL);
++
++      /* Small enough for pointer-storage convention? */
++      if (g.regsize >= 32767L)                /* Probably could be 65535L. */
++              FAIL("regexp too big");
++
++      /* Allocate space. */
++      *patternsize=sizeof(regexp) + (unsigned)g.regsize;
++      r = (regexp *)malloc(sizeof(regexp) + (unsigned)g.regsize);
++      if (r == NULL)
++              FAIL("out of space");
++
++      /* Second pass: emit code. */
++      g.regparse = exp;
++      g.regnpar = 1;
++      g.regcode = r->program;
++      regc(&g, MAGIC);
++      if (reg(&g, 0, &flags) == NULL)
++              return(NULL);
++
++      /* Dig out information for optimizations. */
++      r->regstart = '\0';     /* Worst-case defaults. */
++      r->reganch = 0;
++      r->regmust = NULL;
++      r->regmlen = 0;
++      scan = r->program+1;                    /* First BRANCH. */
++      if (OP(regnext(&g, scan)) == END) {             /* Only one top-level choice. */
++              scan = OPERAND(scan);
++
++              /* Starting-point info. */
++              if (OP(scan) == EXACTLY)
++                      r->regstart = *OPERAND(scan);
++              else if (OP(scan) == BOL)
++                      r->reganch++;
++
++              /*
++               * If there's something expensive in the r.e., find the
++               * longest literal string that must appear and make it the
++               * regmust.  Resolve ties in favor of later strings, since
++               * the regstart check works with the beginning of the r.e.
++               * and avoiding duplication strengthens checking.  Not a
++               * strong reason, but sufficient in the absence of others.
++               */
++              if (flags&SPSTART) {
++                      longest = NULL;
++                      len = 0;
++                      for (; scan != NULL; scan = regnext(&g, scan))
++                              if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
++                                      longest = OPERAND(scan);
++                                      len = strlen(OPERAND(scan));
++                              }
++                      r->regmust = longest;
++                      r->regmlen = len;
++              }
++      }
++
++      return(r);
++}
++
++/*
++ - reg - regular expression, i.e. main body or parenthesized thing
++ *
++ * Caller must absorb opening parenthesis.
++ *
++ * Combining parenthesis handling with the base level of regular expression
++ * is a trifle forced, but the need to tie the tails of the branches to what
++ * follows makes it hard to avoid.
++ */
++static char *
++reg(struct match_globals *g, int paren, int *flagp /* Parenthesized? */ )
++{
++      register char *ret;
++      register char *br;
++      register char *ender;
++      register int parno = 0; /* 0 makes gcc happy */
++      int flags;
++
++      *flagp = HASWIDTH;      /* Tentatively. */
++
++      /* Make an OPEN node, if parenthesized. */
++      if (paren) {
++              if (g->regnpar >= NSUBEXP)
++                      FAIL("too many ()");
++              parno = g->regnpar;
++              g->regnpar++;
++              ret = regnode(g, OPEN+parno);
++      } else
++              ret = NULL;
++
++      /* Pick up the branches, linking them together. */
++      br = regbranch(g, &flags);
++      if (br == NULL)
++              return(NULL);
++      if (ret != NULL)
++              regtail(g, ret, br);    /* OPEN -> first. */
++      else
++              ret = br;
++      if (!(flags&HASWIDTH))
++              *flagp &= ~HASWIDTH;
++      *flagp |= flags&SPSTART;
++      while (*g->regparse == '|') {
++              g->regparse++;
++              br = regbranch(g, &flags);
++              if (br == NULL)
++                      return(NULL);
++              regtail(g, ret, br);    /* BRANCH -> BRANCH. */
++              if (!(flags&HASWIDTH))
++                      *flagp &= ~HASWIDTH;
++              *flagp |= flags&SPSTART;
++      }
++
++      /* Make a closing node, and hook it on the end. */
++      ender = regnode(g, (paren) ? CLOSE+parno : END);        
++      regtail(g, ret, ender);
++