--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/2.6.20/4300_squashfs-3.2.patch Wed Feb 07 23:57:30 2007 +0100
@@ -0,0 +1,4400 @@
+diff -Nurp -x .gitignore linux-2.6.20/fs/Kconfig linux-2.6.20-squashfs3.2/fs/Kconfig
+--- linux-2.6.20/fs/Kconfig 2006-12-25 01:13:12.000000000 +0000
++++ linux-2.6.20-squashfs3.2/fs/Kconfig 2006-12-25 10:56:26.000000000 +0000
+@@ -1404,6 +1404,71 @@ config CRAMFS
+
+ If unsure, say N.
+
++config SQUASHFS
++ tristate "SquashFS 3.2 - Squashed file system support"
++ select ZLIB_INFLATE
++ help
++ Saying Y here includes support for SquashFS 3.2 (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.1 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
+diff -Nurp -x .gitignore linux-2.6.20/fs/Makefile linux-2.6.20-squashfs3.2/fs/Makefile
+--- linux-2.6.20/fs/Makefile 2006-12-25 01:13:12.000000000 +0000
++++ linux-2.6.20-squashfs3.2/fs/Makefile 2006-12-25 10:55:37.000000000 +0000
+@@ -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/
+diff -Nurp -x .gitignore linux-2.6.20/fs/squashfs/inode.c linux-2.6.20-squashfs3.2/fs/squashfs/inode.c
+--- linux-2.6.20/fs/squashfs/inode.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.20-squashfs3.2/fs/squashfs/inode.c 2006-12-31 19:26:30.000000000 +0000
+@@ -0,0 +1,2328 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * 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/zlib.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/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 int squashfs_read_inode(struct inode *i, 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 void vfs_read_inode(struct inode *i);
++static struct dentry *squashfs_get_parent(struct dentry *child);
++
++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_super_ops = {
++ .alloc_inode = squashfs_alloc_inode,
++ .destroy_inode = squashfs_destroy_inode,
++ .statfs = squashfs_statfs,
++ .put_super = squashfs_put_super,
++};
++
++static struct super_operations squashfs_export_super_ops = {
++ .alloc_inode = squashfs_alloc_inode,
++ .destroy_inode = squashfs_destroy_inode,
++ .statfs = squashfs_statfs,
++ .put_super = squashfs_put_super,
++ .read_inode = vfs_read_inode
++};
++
++struct export_operations squashfs_export_ops = {
++ .get_parent = squashfs_get_parent
++};
++
++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, int srclength)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ 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 = 0;
++ unsigned int compressed;
++ unsigned int c_byte = length;
++
++ if (c_byte) {
++ bytes = msblk->devblksize - offset;
++ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
++ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
++
++ TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed
++ ? "" : "un", (unsigned int) c_byte, srclength);
++
++ if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used)
++ goto read_failure;
++
++ 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 (index < 0 || (index + 2) > sblk->bytes_used)
++ goto read_failure;
++
++ 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_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
++
++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
++ ? "" : "un", (unsigned int) c_byte);
++
++ if (c_byte > srclength || (index + c_byte) > sblk->bytes_used)
++ goto read_failure;
++
++ 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) {
++ int zlib_err = 0;
++
++ /*
++ * uncompress block
++ */
++
++ down(&msblk->read_data_mutex);
++
++ msblk->stream.next_out = buffer;
++ msblk->stream.avail_out = srclength;
++
++ for (bytes = 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 release_mutex;
++
++ msblk->stream.next_in = bh[k]->b_data + offset;
++ msblk->stream.avail_in = avail_bytes;
++
++ if (k == 0) {
++ zlib_err = zlib_inflateInit(&msblk->stream);
++ if (zlib_err != Z_OK) {
++ ERROR("zlib_inflateInit returned unexpected result 0x%x, srclength %d\n",
++ zlib_err, srclength);
++ goto release_mutex;
++ }
++
++ if (avail_bytes == 0) {
++ offset = 0;
++ brelse(bh[k]);
++ continue;
++ }
++ }
++
++ zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
++ if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) {
++ ERROR("zlib_inflate returned unexpected result 0x%x, srclength %d, avail_in %d, avail_out %d\n",
++ zlib_err, srclength, msblk->stream.avail_in, msblk->stream.avail_out);
++ goto release_mutex;
++ }
++
++ bytes += avail_bytes;
++ offset = 0;
++ brelse(bh[k]);
++ }
++
++ if (zlib_err != Z_STREAM_END)
++ goto release_mutex;
++
++ zlib_err = zlib_inflateEnd(&msblk->stream);
++ if (zlib_err != Z_OK) {
++ ERROR("zlib_inflateEnd returned unexpected result 0x%x, srclength %d\n",
++ zlib_err, srclength);
++ goto release_mutex;
++ }
++ bytes = msblk->stream.total_out;
++ up(&msblk->read_data_mutex);
++ } else {
++ int i;
++
++ for(i = 0; i < b; i++) {
++ wait_on_buffer(bh[i]);
++ if(!buffer_uptodate(bh[i]))
++ goto block_release;
++ }
++
++ for (bytes = 0; k < b; k++) {
++ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
++ msblk->devblksize - offset :
++ c_byte - bytes;
++ memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes);
++ bytes += avail_bytes;
++ offset = 0;
++ brelse(bh[k]);
++ }
++ }
++
++ if (next_index)
++ *next_index = index + c_byte + (length ? 0 :
++ (SQUASHFS_CHECK_DATA(msblk->sblk.flags)
++ ? 3 : 2));
++ return bytes;
++
++release_mutex:
++ up(&msblk->read_data_mutex);
++
++block_release:
++ for (; k < b; k++)
++ brelse(bh[k]);
++
++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);
++
++ msblk->block_cache[i].length = squashfs_read_data(s,
++ msblk->block_cache[i].data, block, 0, &next_index, SQUASHFS_METADATA_SIZE);
++ if (msblk->block_cache[i].length == 0) {
++ ERROR("Unable to read cache block [%llx:%x]\n",
++ block, offset);
++ down(&msblk->block_cache_mutex);
++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
++ kfree(msblk->block_cache[i].data);
++ wake_up(&msblk->waitq);
++ up(&msblk->block_cache_mutex);
++ 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;
++ }
++
++ bytes = msblk->block_cache[i].length - offset;
++
++ if (bytes < 1) {
++ up(&msblk->block_cache_mutex);
++ goto out;
++ } else if (bytes >= 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;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ 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, sblk->block_size))) {
++ ERROR("Unable to read fragment cache block "
++ "[%llx]\n", start_block);
++ msblk->fragment[i].locked = 0;
++ goto out;
++ }
++
++ down(&msblk->fragment_mutex);
++ 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);
++ up(&msblk->fragment_mutex);
++ 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 void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
++ struct squashfs_base_inode_header *inodeb)
++{
++ 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];
++}
++
++
++static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)];
++ int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1);
++ squashfs_inode_t inode;
++
++ TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino);
++
++ if (msblk->swap) {
++ squashfs_inode_t sinode;
++
++ if (!squashfs_get_cached_block(s, (char *) &sinode, start, offset,
++ sizeof(sinode), &start, &offset))
++ goto out;
++ SQUASHFS_SWAP_INODE_T((&inode), &sinode);
++ } else if (!squashfs_get_cached_block(s, (char *) &inode, start, offset,
++ sizeof(inode), &start, &offset))
++ goto out;
++
++ TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode);
++
++ return inode;
++
++out:
++ return SQUASHFS_INVALID_BLK;
++}
++
++
++static void vfs_read_inode(struct inode *i)
++{
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ squashfs_inode_t inode = squashfs_inode_lookup(i->i_sb, i->i_ino);
++
++ TRACE("Entered vfs_read_inode\n");
++
++ if(inode != SQUASHFS_INVALID_BLK)
++ (msblk->read_inode)(i, inode);
++}
++
++
++static struct dentry *squashfs_get_parent(struct dentry *child)
++{
++ struct inode *i = child->d_inode;
++ struct inode *parent = iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode);
++ struct dentry *rv;
++
++ TRACE("Entered squashfs_get_parent\n");
++
++ if(parent == NULL) {
++ rv = ERR_PTR(-EACCES);
++ goto out;
++ }
++
++ rv = d_alloc_anon(parent);
++ if(rv == NULL)
++ rv = ERR_PTR(-ENOMEM);
++
++out:
++ return rv;
++}
++
++
++SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct inode *i = iget_locked(s, inode_number);
++
++ TRACE("Entered squashfs_iget\n");
++
++ if(i && (i->i_state & I_NEW)) {
++ (msblk->read_inode)(i, inode);
++ unlock_new_inode(i);
++ }
++
++ return i;
++}
++
++
++static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode)
++{
++ struct super_block *s = i->i_sb;
++ 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_read_inode\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;
++
++ squashfs_new_inode(msblk, i, inodeb);
++
++ 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;
++
++ 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;
++
++ 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;
++
++ 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;
++
++ 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;
++
++ 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;
++
++ 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;
++
++ 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;
++ }
++
++ return 1;
++
++failed_read:
++ ERROR("Unable to read inode [%llx:%x]\n", block, offset);
++
++failed_read1:
++ make_bad_inode(i);
++ return 0;
++}
++
++
++static int read_inode_lookup_table(struct super_block *s)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes);
++
++ TRACE("In read_inode_lookup_table, length %d\n", length);
++
++ /* Allocate inode lookup table */
++ if (!(msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL))) {
++ ERROR("Failed to allocate inode lookup table\n");
++ return 0;
++ }
++
++ if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table,
++ sblk->lookup_table_start, length |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
++ ERROR("unable to read inode lookup table\n");
++ return 0;
++ }
++
++ if (msblk->swap) {
++ int i;
++ long long block;
++
++ for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) {
++ SQUASHFS_SWAP_LOOKUP_BLOCKS((&block),
++ &msblk->inode_lookup_table[i], 1);
++ msblk->inode_lookup_table[i] = block;
++ }
++ }
++
++ return 1;
++}
++
++
++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;
++ unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments);
++
++ if(length == 0)
++ return 1;
++
++ /* Allocate fragment index table */
++ if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) {
++ ERROR("Failed to allocate fragment index table\n");
++ return 0;
++ }
++
++ if (!squashfs_read_data(s, (char *) msblk->fragment_index,
++ sblk->fragment_table_start, length |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
++ 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->read_inode = squashfs_read_inode;
++ 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;
++ if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
++ ERROR("Failed to allocate zlib workspace\n");
++ goto failure;
++ }
++ 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);
++
++ sblk->bytes_used = sizeof(struct squashfs_super_block);
++ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
++ sizeof(struct squashfs_super_block) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) {
++ 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;
++
++ /* Check the filesystem does not extend beyond the end of the
++ block device */
++ if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode))
++ goto failed_mount;
++
++ /* Check the root inode for sanity */
++ if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE)
++ 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_super_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_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, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
++ 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, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
++ 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 and read fragment index table */
++ if (msblk->read_fragment_index_table(s) == 0)
++ goto failed_mount;
++
++ if(sblk->s_major < 3 || sblk->lookup_table_start == SQUASHFS_INVALID_BLK)
++ goto allocate_root;
++
++ /* Allocate and read inode lookup table */
++ if (read_inode_lookup_table(s) == 0)
++ goto failed_mount;
++
++ s->s_op = &squashfs_export_super_ops;
++ s->s_export_op = &squashfs_export_ops;
++
++allocate_root:
++ root = new_inode(s);
++ if ((msblk->read_inode)(root, sblk->root_inode) == 0)
++ goto failed_mount;
++ insert_inode_hash(root);
++
++ 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->inode_lookup_table);
++ kfree(msblk->fragment_index);
++ kfree(msblk->fragment);
++ kfree(msblk->uid);
++ kfree(msblk->read_page);
++ kfree(msblk->block_cache);
++ kfree(msblk->fragment_index_2);
++ vfree(msblk->stream.workspace);
++ 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_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);
++ flush_dcache_page(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 {
++ if(meta->entries == 0)
++ goto failed;
++ 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;
++ 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 (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
++ ERROR("Failed to allocate block_list\n");
++ goto skip_read;
++ }
++
++ 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, sblk->block_size))) {
++ 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 avail = (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, avail);
++
++ push_page = (i == page->index) ? page :
++ grab_cache_page_nowait(page->mapping, i);
++
++ if (!push_page)
++ continue;
++
++ if (PageUptodate(push_page))
++ goto skip_page;
++
++ pageaddr = kmap_atomic(push_page, KM_USER0);
++ memcpy(pageaddr, data_ptr + byte_offset, avail);
++ memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
++ kunmap_atomic(pageaddr, KM_USER0);
++ flush_dcache_page(push_page);
++ SetPageUptodate(push_page);
++skip_page:
++ unlock_page(push_page);
++ if(i != page->index)
++ 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);
++
++ kfree(block_list);
++ 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);
++
++ kfree(block_list);
++ 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;
++ 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)) {
++ block_list = NULL;
++ goto skip_read;
++ }
++
++ if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
++ ERROR("Failed to allocate block_list\n");
++ 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);
++ if(block == 0)
++ goto skip_read;
++
++ down(&msblk->read_page_mutex);
++ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
++ bsize, NULL, sblk->block_size);
++ if (bytes) {
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memcpy(pageaddr, msblk->read_page, bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ } 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);
++ if (fragment) {
++ bytes = i_size_read(inode) & (sblk->block_size - 1);
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
++ u.s1.fragment_offset, bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ 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:
++ 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);
++
++ kfree(block_list);
++ 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;
++ struct squashfs_dir_index *index;
++ char *str;
++
++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
++
++ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_index\n");
++ goto failure;
++ }
++
++ index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1);
++ 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;
++ kfree(str);
++failure:
++ 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,
++ dir_count;
++ struct squashfs_dir_header dirh;
++ struct squashfs_dir_entry *dire;
++
++ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto finish;
++ }
++
++ 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;
++ }
++
++ 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;
++ }
++ }
++
++finish:
++ kfree(dire);
++ return 0;
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ kfree(dire);
++ 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;
++ struct squashfs_dir_entry *dire;
++
++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto exit_lookup;
++ }
++
++ if (len > SQUASHFS_NAME_LEN)
++ goto exit_lookup;
++
++ 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_lookup;
++
++ 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 = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number);
++
++ goto exit_lookup;
++ }
++ }
++ }
++
++exit_lookup:
++ kfree(dire);
++ if (inode)
++ return d_splice_alias(inode, dentry);
++ 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_lookup;
++}
++
++
++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_page);
++ kfree(sbi->uid);
++ kfree(sbi->fragment_index);
++ kfree(sbi->fragment_index_2);
++ kfree(sbi->meta_index);
++ vfree(sbi->stream.workspace);
++ 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.2 (2007/01/02) "
++ "Phillip Lougher\n");
++
++ if ((err = register_filesystem(&squashfs_fs_type)))
++ destroy_inodecache();
++
++out:
++ return err;
++}
++
++
++static void __exit exit_squashfs_fs(void)
++{
++ 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 3.2, a compressed read-only filesystem");
++MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>");
++MODULE_LICENSE("GPL");
+diff -Nurp -x .gitignore linux-2.6.20/fs/squashfs/Makefile linux-2.6.20-squashfs3.2/fs/squashfs/Makefile
+--- linux-2.6.20/fs/squashfs/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.20-squashfs3.2/fs/squashfs/Makefile 2006-12-25 10:55:38.000000000 +0000
+@@ -0,0 +1,7 @@
++#
++# Makefile for the linux squashfs routines.
++#
++
++obj-$(CONFIG_SQUASHFS) += squashfs.o
++squashfs-y += inode.o
++squashfs-y += squashfs2_0.o
+diff -Nurp -x .gitignore linux-2.6.20/fs/squashfs/squashfs2_0.c linux-2.6.20-squashfs3.2/fs/squashfs/squashfs2_0.c
+--- linux-2.6.20/fs/squashfs/squashfs2_0.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.20-squashfs3.2/fs/squashfs/squashfs2_0.c 2006-12-31 19:26:39.000000000 +0000
+@@ -0,0 +1,757 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * 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/zlib.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, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
++ 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 void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
++ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
++{
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ 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];
++}
++
++
++static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
++{
++ struct super_block *s = i->i_sb;
++ 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 = i->i_ino;
++ 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;
++
++ squashfs_new_inode(msblk, i, inodeb, ino);
++
++ 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 = 0;
++
++ 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;
++
++ 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;
++ 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;
++
++ 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;
++
++ 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;
++
++ 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;
++
++ 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: {
++
++ 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;
++ }
++
++ return 1;
++
++failed_read:
++ ERROR("Unable to read inode [%x:%x]\n", block, offset);
++
++failed_read1:
++ 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_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;
++ struct squashfs_dir_index_2 *index;
++ char *str;
++
++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
++
++ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_index\n");
++ goto failure;
++ }
++
++ index = (struct squashfs_dir_index_2 *) (str + SQUASHFS_NAME_LEN + 1);
++ 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;
++ kfree(str);
++failure:
++ 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,
++ dir_count;
++ struct squashfs_dir_header_2 dirh;
++ struct squashfs_dir_entry_2 *dire;
++
++ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto finish;
++ }
++
++ 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;
++ }
++ }
++
++finish:
++ kfree(dire);
++ return 0;
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ kfree(dire);
++ 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;
++ struct squashfs_dir_entry_2 *dire;
++ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
++
++ TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto exit_loop;
++ }
++
++ 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);
++ unsigned int inode_number = SQUASHFS_MK_VFS_INODE(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 = squashfs_iget(i->i_sb, ino, inode_number);
++
++ goto exit_loop;
++ }
++ }
++ }
++
++exit_loop:
++ kfree(dire);
++ 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->read_inode = squashfs_read_inode_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;
++}
+diff -Nurp -x .gitignore linux-2.6.20/fs/squashfs/squashfs.h linux-2.6.20-squashfs3.2/fs/squashfs/squashfs.h
+--- linux-2.6.20/fs/squashfs/squashfs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.20-squashfs3.2/fs/squashfs/squashfs.h 2006-12-31 19:26:47.000000000 +0000
+@@ -0,0 +1,87 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * 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, int srclength);
++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 inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number);
++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
+diff -Nurp -x .gitignore linux-2.6.20/include/linux/squashfs_fs.h linux-2.6.20-squashfs3.2/include/linux/squashfs_fs.h
+--- linux-2.6.20/include/linux/squashfs_fs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.20-squashfs3.2/include/linux/squashfs_fs.h 2007-01-01 08:25:53.000000000 +0000
+@@ -0,0 +1,934 @@
++#ifndef SQUASHFS_FS
++#define SQUASHFS_FS
++
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * 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_EXPORT 7
++
++#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_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_EXPORT)
++
++#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_CHECK)
++
++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
++ duplicate_checking, exortable) (noi | (nod << 1) | (check_data << 2) \
++ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
++ (duplicate_checking << 6) | (exportable << 7))
++
++/* 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))
++
++/* inode lookup table defines */
++#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(squashfs_inode_t))
++
++#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
++ SQUASHFS_METADATA_SIZE - 1) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(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 lookup_table_start;
++} __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 pending;
++} __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)->lookup_table_start, 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_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
++
++#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)
++#define SQUASHFS_SWAP_LOOKUP_BLOCKS(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
+diff -Nurp -x .gitignore linux-2.6.20/include/linux/squashfs_fs_i.h linux-2.6.20-squashfs3.2/include/linux/squashfs_fs_i.h
+--- linux-2.6.20/include/linux/squashfs_fs_i.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.20-squashfs3.2/include/linux/squashfs_fs_i.h 2006-12-31 19:27:17.000000000 +0000
+@@ -0,0 +1,45 @@
++#ifndef SQUASHFS_FS_I
++#define SQUASHFS_FS_I
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * 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
+diff -Nurp -x .gitignore linux-2.6.20/include/linux/squashfs_fs_sb.h linux-2.6.20-squashfs3.2/include/linux/squashfs_fs_sb.h
+--- linux-2.6.20/include/linux/squashfs_fs_sb.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.20-squashfs3.2/include/linux/squashfs_fs_sb.h 2006-12-31 19:27:28.000000000 +0000
+@@ -0,0 +1,74 @@
++#ifndef SQUASHFS_FS_SB
++#define SQUASHFS_FS_SB
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * 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;
++ 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;
++ z_stream stream;
++ long long *inode_lookup_table;
++ int (*read_inode)(struct inode *i, 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
+diff -Nurp -x .gitignore linux-2.6.20/init/do_mounts_rd.c linux-2.6.20-squashfs3.2/init/do_mounts_rd.c
+--- linux-2.6.20/init/do_mounts_rd.c 2006-11-29 21:57:37.000000000 +0000
++++ linux-2.6.20-squashfs3.2/init/do_mounts_rd.c 2006-12-25 10:55:38.000000000 +0000
+@@ -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,18 @@ 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);
++ if (squashfsb->s_major < 3)
++ nblocks = (squashfsb->bytes_used_2+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
++ else
++ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
++ goto done;
++ }
++
+ /*
+ * Read block 1 to test for minix and ext2 superblock
+ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/2.6.20/4400_speakup-20070204.patch Wed Feb 07 23:57:30 2007 +0100
@@ -0,0 +1,11767 @@
+speakup CVS for Linux 2.6.20
+Generated by dsd on Sun Feb 4 16:09:21 EST 2007
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/arch/arm/Kconfig linux-2.6.20-spk/arch/arm/Kconfig
+--- linux-2.6.20/arch/arm/Kconfig 2007-02-04 16:08:00.000000000 -0500
++++ linux-2.6.20-spk/arch/arm/Kconfig 2007-02-04 16:09:21.000000000 -0500
+@@ -951,6 +951,7 @@ source "drivers/leds/Kconfig"
+ source "drivers/media/Kconfig"
+
+ source "drivers/video/Kconfig"
++source "drivers/char/speakup/Kconfig"
+
+ source "sound/Kconfig"
+
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/Documentation/speakup/DefaultKeyAssignments linux-2.6.20-spk/Documentation/speakup/DefaultKeyAssignments
+--- linux-2.6.20/Documentation/speakup/DefaultKeyAssignments 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20-spk/Documentation/speakup/DefaultKeyAssignments 2007-02-04 16:09:21.000000000 -0500
+@@ -0,0 +1,46 @@
++This file is intended to give you an overview of the default keys used
++by speakup for it's review functions. You may change them to be
++anything you want but that will take some familiarity with key
++mapping.
++
++We have remapped the insert or zero key on the keypad to act as a
++shift key. Well, actually as an altgr key. So in the following list
++InsKeyPad-period means hold down the insert key like a shift key and
++hit the keypad period.
++
++KeyPad-8 Say current Line
++InsKeyPad-8 say from top of screen to reading cursor.
++KeyPad-7 Say Previous Line (UP one line)
++KeyPad-9 Say Next Line (down one line)
++KeyPad-5 Say Current Word
++InsKeyPad-5 Spell Current Word
++KeyPad-4 Say Previous Word (left one word)
++InsKeyPad-4 say from left edge of line to reading cursor.
++KeyPad-6 Say Next Word (right one word)
++InsKeyPad-6 Say from reading cursor to right edge of line.
++KeyPad-2 Say Current Letter
++InsKeyPad-2 say current letter phonetically
++KeyPad-1 Say Previous Character (left one letter)
++KeyPad-3 Say Next Character (right one letter)
++KeyPad-plus Say Entire Screen
++InsKeyPad-plus Say from reading cursor line to bottom of screen.
++KeyPad-Minus Park reading cursor (toggle)
++InsKeyPad-minus Say character hex and decimal value.
++KeyPad-period Say Position (current line, position and console)
++InsKeyPad-period say colour attributes of current position.
++InsKeyPad-9 Move reading cursor to top of screen (insert pgup)
++InsKeyPad-3 Move reading cursor to bottom of screen (insert pgdn)
++InsKeyPad-7 Move reading cursor to left edge of screen (insert home)
++InsKeyPad-1 Move reading cursor to right edge of screen (insert end)
++ControlKeyPad-1 Move reading cursor to last character on current line.
++KeyPad-Enter Shut Up (until another key is hit) and sync reading cursor
++InsKeyPad-Enter Shut Up (until toggled back on).
++InsKeyPad-star n<x|y> go to line (y) or column (x). Where 'n' is any
++ allowed value for the row or column for your current screen.
++KeyPad-/ Mark and Cut screen region.
++InsKeyPad-/ Paste screen region into any console.
++
++Hitting any key while speakup is outputting speech will quiet the
++synth until it has caught up with what is being printed on the
++console.
++
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/Documentation/speakup/INSTALLATION linux-2.6.20-spk/Documentation/speakup/INSTALLATION
+--- linux-2.6.20/Documentation/speakup/INSTALLATION 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20-spk/Documentation/speakup/INSTALLATION 2007-02-04 16:09:21.000000000 -0500
+@@ -0,0 +1,108 @@
++This document assumes you have had some experience with kernel
++compilation and installation. If you have not, I recommend you get
++the kernel source and read the README and various documents in the
++linux/Documentation directory. In particular the Changes file to make
++sure you have the appropriate utilities needed for installing a 2.2.xx
++or 2.4xx kernel. It isn't as difficult as you might think. The
++kernel README is intimidating the first time but once you get the
++steps down, it's really pretty easy. Getting through the "make
++config" is the tedious bit.
++
++The first thing to do is to place a copy of the tarball in the /usr/src
++directory which is the directory the linux tree is located in as well.
++Next untar speakup by typing:
++
++tar zxf speakup-1.00.tar.gz
++cd speakup-1.00
++./install
++
++Note the dot-slash before the install. This will copy the speakup
++directory to the kernel tree and apply the various patches and
++components to the appropriate kernel files. Depending on how
++experienced you are with kernel compiling and hacking will determine
++whether you should bother looking at any failed patches. If this
++happens, you should probably write to the speakup mailing list for
++help or myself.
++
++If all of the patch hunks apply successfully then just continue with
++the standard steps to compile the kernel with:
++
++make mrproper
++make config
++
++When you get to the section console speech output, answer 'y' to the
++CONFIG_SPEAKUP prompt. You will be given a submenu with the list of
++synthesizers which are currently supported. You can include as many
++synths in the kernel as you wish but remember each one takes up kernel
++space. You can only choose one of the synths as the default or none,
++so just type dtlk or whatever is the correct string for the
++synthesizer you have. You will also be asked if you wish to build-in
++a speakup key map. If you do not say 'y' to this option you will need
++to load a speakup map at boot time with whichever mechanism your
++distribution uses for loading key maps.
++
++We have placed the speakup configuration options in make config just
++after the vga console choice. For the DoubleTalk PC driver included
++by Jim Van Zandt. I recommend you say no to that option. I have not
++tried configuring them both in, but I wouldn't be at all surprised if
++it didn't work.
++
++If all goes well up to this point you can continue with the compiling
++process by doing:
++
++make dep >dep.file 2>&1 &
++make bzImage >cc.file 2>&1 &
++make modules >mod.file 2>&1 &
++
++I always redirect output to the files dep.file and cc.file so I can
++look over the compilation record to make sure there are no errors and
++warnings.
++
++Okay, you are ready to install the newly compiled kernel. Make sure
++you make an linux.old entry in your lilo.conf file so you can recover
++if it blows up. next as root run "make modules_install" to install
++whatever modules you compiled and move the bzImage from
++/usr/src/linux/arch/i386/boot to wherever your kernel lives. Also
++move the System.map from /usr/src/linux to where your System.map
++lives. On our systems we use debian so we create an vmlinuz-speakup
++and System.map-speakup in our /boot directory and set the symbolic
++links vmlinuz and System.map in the root (/) directory to point to the
++images. Now type lilo to tell lilo to build the new booter file and
++install it.
++
++As of version 0.07, the keymap for speakup is automatically built in
++at compile time. If you have other keymaps installed at boot time,
++you might want to consider removing them before you reboot the system.
++
++If everything has gone OK up until now, cross your fingers and type:
++
++shutdown -r now
++
++Your system should start talking to you as soon as it starts booting.
++It will talk and talk and ... well, you might want to hit the
++keypad-enter key to tell it to shut up. You should also read the
++DefaultKeyAssignments file to learn the various review functions
++available.
++
++As of v-0.10 the speakup configuration options are in the
++/proc/speakup subtree. The individual options should be fairly
++obvious by their names such as rate, volume, punc_level and so forth.
++You can manipulate them by cat'ing or echoing new values to them such
++as:
++
++echo 9 >/proc/speakup/rate
++
++You can see what the current values are by cat'ing those files to the console:
++
++cat /proc/speakup/rate
++
++I have probably managed to overlook a whole whack of things because
++this is the, enter version number here, draft. Don't worry we'll get
++it right eventually. If you like the package you really should get on
++the mailing list and start participating in it's development.
++
++ Kirk
++
++email: kirk@braille.uwo.ca
++phone: (519) 679-6845 (home)
++
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/Documentation/speakup/keymap-tutorial linux-2.6.20-spk/Documentation/speakup/keymap-tutorial
+--- linux-2.6.20/Documentation/speakup/keymap-tutorial 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20-spk/Documentation/speakup/keymap-tutorial 2007-02-04 16:09:21.000000000 -0500
+@@ -0,0 +1,140 @@
++ Speakup Keymap Tutorial
++
++This is meant to be a basic tutorial on how to change the Linux keymap
++file to assign speakup review functions to desired keys. It is not
++intended to be a replacement for the loadkeys(8) or keymap(5) man
++pages.
++
++The basic lay-out of the keymap file is a series of lines with the
++following fields. The keyword keycode indicates this is the start of
++a new key assignment. It is then followed by a number which
++represents the actual key on the keyboard. That number is followed by
++the equals '=' operator and finally a list of keywords representing
++key names such as keypad5. Each line can have quite a few key
++functions on it. They are interpreted by loadkeys in order and
++assigned to key shift states depending on the order they are
++encountered. So for example, the first value after the equals is the
++keys unshifted state, while the second is the keys shifted state. If
++you wish to learn the order they are interpreted in read the
++loadkeys(8) and keymap(5) man pages.
++
++You can have subsequent lines which are indented and start with
++another keyword for the various shifted states. This way you can
++assign some of the states without having to specify them all in order
++up until you get to the one you want to assign.
++
++In speakup, we have assigned the insert key on the number pad to the
++altgr keyword. This is not required; you could choose any other
++shifted state keyword. We used altgr because it typically represents
++the right hand alt key. In Linux each shift key is separate and
++independent, so the left shift and the right shift keys are not
++necessarily the same. The altgr key is not really used for anything
++important, so we steel it.
++
++Here are the default key assignments for the number eight on the
++keypad:
++
++keycode 72 = KP_8
++ alt keycode 72 = Ascii_8
++
++As you can see, the first line starts with keycode followed by 72
++which is the actual number assigned to the key when the keyboard port
++is read. The KP_8 after the equal sign, is the symbolic representation
++of the function called when that key is hit.
++
++The second line is the same format except it starts with the keyword
++alt which is indented. That means that the function at the end of
++that line Ascii_8 is applied to the alt-shifted eight key.
++
++Now here are the speakup assignments for that key:
++
++keycode 72 = 0x0d0a
++ altgr keycode 72 = 0x0d20
++#keycode 72 = KP_8
++ alt keycode 72 = Ascii_8
++
++Notice that the only thing which has changed on the first line is the
++function called when the key is struck. It is a hexadecimal number
++identifying the function called in a look up table. It is not a
++symbolic representation yet because that means we need to change the
++loadkeys program to understand our symbolic names. We will do this in
++the future but for now it is more expedient to just use the table
++indices. You will find a table at the bottom of this document
++listing the review functions and their corresponding hex lookups.
++
++The 0x0d0a in the first line above is speakup's say line function.
++The second line ends with 0x0d20 which is speakup's read from top of
++screen to reading cursor line.
++
++The third line is the original key assignment commented out with a
++number-sign '#' at the beginning. I do that so I can easily find the
++keys I want to affect by symbolic name. Otherwise I would need to
++keep a look up table for all the keycodes. I recommend you do this as
++well or you'll be very sorry at some point in the future.
++
++The forth line is just the standard key assignment for the left hand
++alt key.
++
++Now let's say we want to design a different keyboard layout. I'll use
++an example for the JAWS style keypad because I've specifically been
++asked to help with that. JAWS uses the eight on the keypad to move up
++a line or the speakup function to read previous line. JAWS also uses
++the keypad_8 key in a shifted mode to read the current line. I
++apologize if these are not quite right. It has been a long time since
++I used JAWS. So we would have the following two lines:
++
++keycode 72 = 0x0d0b
++ altgr keycode 72 = 0x0d0a
++
++The hex value 0x0d0b in the first line is speakup's SAY_PREVIOUS_LINE
++function. The 0x0d0a in the second line is the same say_line function
++as we had earlier. So when the number eight is hit on the keypad
++speakup will read the previous line and when the number eight is
++shifted with the insert key on the keypad it will read the current
++line.
++
++As you can tell, it is not really very difficult to reassign the keys
++to different review functions.
++
++Once you have carefully edited the keymap file, called default.map in
++the speakup distribution, you copy it into the /etc/kbd directory.
++Make sure you back up the original default.map from that directory
++first, if there is one. Then you run loadkeys to load the new map
++into the kernel:
++
++loadkeys /etc/kbd/default.map
++
++If you wish to build your new keyboard lay-out into the kernel, after
++testing it, copy the default.map file into the drivers/char directory,
++with the name defkeymap.map, of your Linux source tree. Then rm the
++defkeymap.c file and recompile the kernel. Because there is no
++defkeymap.c `make' will rebuild it on the next compile.
++
++Here is a list of the available speakup review functions at this point
++in time.
++
++SAY_CHAR 0x0d04 /* say this character */
++SAY_PREV_CHAR 0x0d05 /* say character left of this char */
++SAY_NEXT_CHAR 0x0d06 /* say char right of this char */
++SAY_WORD 0x0d07 /* say this word under reading cursor */
++SAY_PREV_WORD 0x0d08
++SAY_NEXT_WORD 0x0d09
++SAY_LINE 0x0d0a /* say this line */
++SAY_PREV_LINE 0x0d0b /* say line above this line */
++SAY_NEXT_LINE 0x0d0c
++TOP_EDGE 0x0d0d /* move to top edge of screen */
++BOTTOM_EDGE 0x0d0e
++LEFT_EDGE 0x0d0f
++RIGHT_EDGE 0x0d10
++SAY_PHONETIC_CHAR 0x0d11 /* say this character phonetically */
++SPELL_WORD 0x0d12 /* spell this word letter by letter */
++SAY_SCREEN 0x0d14
++SAY_POSITION 0x0d1b
++SPEECH_OFF 0x0d1c
++SAY_ATTRIBUTES 0x0d1d
++SPEAKUP_PARKED 0x0d1e
++SAY_FROM_TOP 0x0d20
++SAY_TO_BOTTOM 0x0d21
++SAY_FROM_LEFT 0x0d22
++SAY_TO_RIGHT 0x0d23
++
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/Documentation/speakup/README linux-2.6.20-spk/Documentation/speakup/README
+--- linux-2.6.20/Documentation/speakup/README 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20-spk/Documentation/speakup/README 2007-02-04 16:09:21.000000000 -0500
+@@ -0,0 +1,98 @@
++Welcome to the speakup project for the Speakup speech package for Linux.
++
++Speakup is written by Kirk Reiser and Andy Berdan. It is licensed
++under the GPL. If you don't already know, the GPL stands for the GNU
++General Public License. Which basically states that this code is free to
++copy, modify and distribute to anyone interested in playing with it.
++The one thing you may not do is turn any part of it into proprietary
++or commercial code without the permission of the author. That's me.
++
++If you are interested in being involved with the development of speech
++output for Linux you can subscribe to the Speakup mailing list by
++sending a message to speakup-request@braille.uwo.ca with the line: subscribe. You can also subscribe by going to the speakup web page and following the links at http://www.linux-speakup.org.
++
++We are at a very early stage in the development of this package.
++Hopefully changes will happen often and many. The current files in
++this directory are:
++
++DefaultKeyAssignments # speakup's default review keys
++INSTALLATION # for installing speakup from the tar ball.
++README # this file
++keymap-tutorial # a tutorial on how to layout the keyboard
++
++Read the INSTALLATION file to learn how to apply the patches and the
++default.map for the keyboard. You should also read the Changes file.
++It really has any new things I've added since last time.
++
++There is no documentation in any of these files to instruct you what
++to do if something goes wrong with the patching or compilation. If
++you would like that information you will need to subscribe to the
++mailing list and ask for help, or write me kirk@braille.uwo.ca for
++help. I suggest the mailing list because I will probably tire quickly
++of answering the same questions over and over. You could always
++decide to dig-in and take on the task, and write documentation to help
++others.
++
++There also is a speakup reflector for the Speak Freely package, which
++many of us hang out on and discuss all sorts of topics from speakup
++problems to ALSA driver installation and just about anything else
++you'd like to talk about. The reflector is at lwl.braille.uwo.ca:4074
++with it's lwl page at lwl.braille.uwo.ca/speakup.html. Come and join
++us, it's fun!
++
++Acknowledgements:
++
++I am really very new at kernel hacking and screen review package
++writing, so I have depended heavily on other folks kindness to help me
++a long. No doubt I will continue to abuse them freely and others
++before this is a really good speech solution for Linux. (Oh Well!,
++somebody's got to do it.)
++
++Theodore Ts'o. He gave me a good discussion of unicode and UTF and
++the like. He doesn't even remember writing me about it.
++
++Alan Cox. He has answered many questions about scheduling and wait
++queues and timers along with code fragments and so on. I just wish I
++understood it all totally. He has also helped immensely in moving
++this package toward inclusion in the standard kernel tree. (Maybe next
++release!)
++
++Martin Mares. He pointed me in the right direction to figuring out
++the colour attributes and other useful tidbits.
++
++Paul McDermott. He really is the catalyst for me to actually get
++this all working. Besides I like seeing him bounce around and get all
++excited every time I have something new working.
++
++John Covici, He was the first person to actually attempt writing
++another synthesizer driver for speakup. It was the Speakout driver so
++it was also the first serial driver.
++
++Brian Borowski, he was the first person to actually write a speakup
++function other than Andy and I.
++
++Jim Danley, he has more or less become my main man in helping test
++code, add new features, bounce ideas off and generally become a good
++friend.
++
++Matt Campbell, he basically rewrote the drivers to be able to include
++all synths in the kernel at the same time. The distribution
++maintainers appreciate him a lot as well.
++
++Gene Collins, he was very helpful debugging the current release prior
++to its public showing. He has also worked hard educating others on
++the list and writing the ALSA mini howto.
++
++I would also like to really thank the folks that handle the
++distribution packages. I and many other people would not find access
++to speakup nearly so convenient without their efforts. They include
++Bill Acker, Tom Moore, Matt Campbell, Joe Norton and Joshua Lambert.
++
++There are probably many more I am forgetting right now. I guess I'll
++just have to add you all later.
++
++
++Happy Hacking!
++
++ Kirk
++
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/Documentation/speakup/spkguide.txt linux-2.6.20-spk/Documentation/speakup/spkguide.txt
+--- linux-2.6.20/Documentation/speakup/spkguide.txt 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20-spk/Documentation/speakup/spkguide.txt 2007-02-04 16:09:21.000000000 -0500
+@@ -0,0 +1,1279 @@
++
++The Speakup User's Guide
++For Speakup 2.0 and Later
++By Gene Collins
++Last modified on Tue Mar 29 10:54:19 2005
++Document version 1.0
++
++Copyright (c) 2005 Gene Collins
++
++Permission is granted to copy, distribute and/or modify this document
++under the terms of the GNU Free Documentation License, Version 1.2 or
++any later version published by the Free Software Foundation; with no
++Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
++copy of the license is included in the section entitled "GNU Free
++Documentation License".
++
++Preface
++
++The purpose of this document is to familiarize users with the user
++interface to Speakup, a Linux Screen Reader. If you need instructions
++for installing or obtaining Speakup, visit the web site at
++http://linux-speakup.org/. Speakup is a set of patches to the standard
++Linux kernel source tree. It can be built as a series of modules, or as
++a part of a monolithic kernel. These details are beyond the scope of
++this manual, but the user may need to be aware of the module
++capabilities, depending on how your system administrator has installed
++Speakup. If Speakup is built as a part of a monolithic kernel, and the
++user is using a hardware synthesizer, then Speakup will be able to
++provide speech access from the time the kernel is loaded, until the time
++the system is shutdown. This means that if you have obtained Linux
++installation media for a distribution which includes Speakup as a part
++of its kernel, you will be able, as a blind person, to install Linux
++with speech access unaided by a sighted person. Again, these details
++are beyond the scope of this manual, but the user should be aware of
++them. See the web site mentioned above for further details.
++
++1. Starting Speakup
++
++If your system administrator has installed Speakup to work with your
++specific synthesizer by default, then all you need to do to use Speakup
++is to boot your system, and Speakup should come up talking. This
++assumes of course that your synthesizer is a supported hardware
++synthesizer, and that it is either installed in or connected to your
++system, and is if necessary powered on.
++
++It is possible, however, that Speakup may have been compiled into the
++kernel with no default synthesizer. It is even possible that your
++kernel has been compiled with support for some of the supported
++synthesizers and not others. If you find that this is the case, and
++your synthesizer is supported but not available, complain to the person
++who compiled and installed your kernel. Or better yet, go to the web
++site, and learn how to patch Speakup into your own kernel source, and
++build and install your own kernel.
++
++If your kernel has been compiled with Speakup, and has no default
++synthesizer set, or you would like to use a different synthesizer than
++the default one, then you may issue the following command at the boot
++prompt of your boot loader.
++
++linux speakup_synth=ltlk
++
++This command would tell Speakup to look for and use a LiteTalk or
++DoubleTalk LT at boot up. You may replace the ltlk synthesizer keyword
++with the keyword for whatever synthesizer you wish to use. The
++speakup_synth parameter will accept the following keywords, provided
++that support for the related synthesizers has been built into the
++kernel.
++
++acntsa -- Accent SA
++acntpc -- Accent PC
++apolo -- Apolo
++audptr -- Audapter
++bns -- Braille 'n Speak
++dectlk -- DecTalk Express (old and new, db9 serial only)
++decext -- DecTalk (old) External
++dtlk -- DoubleTalk PC
++keypc -- Keynote Gold PC
++ltlk -- DoubleTalk LT, LiteTalk, or external Tripletalk (db9 serial only)
++spkout -- Speak Out
++txprt -- Transport
++
++Note: Speakup does * NOT * support usb connections! Speakup also does *
++NOT * support the internal Tripletalk!
++
++Speakup does support two other synthesizers, but because they work in
++conjunction with other software, they must be loaded as modules after
++their related software is loaded, and so are not available at boot up.
++These are as follows:
++
++decpc -- DecTalk PC (not available at boot up)
++sftsyn -- One of several software synthesizers (not available at boot up)
++
++See the sections on loading modules and software synthesizers later in
++this manual for further details. It should be noted here that the
++speakup_synth boot parameter will have no effect if Speakup has been
++compiled as modules. In order for Speakup modules to be loaded during
++the boot process, such action must be configured by your system
++administrator. This will mean that you will hear some, but not all, of
++the bootup messages.
++
++2. Basic operation
++
++Once you have booted the system, and if necessary, have supplied the
++proper bootup parameter for your synthesizer, Speakup will begin
++talking as soon as the kernel is loaded. In fact, it will talk a lot!
++It will speak all the boot up messages that the kernel prints on the
++screen during the boot process. This is because Speakup is not a
++separate screen reader, but is actually built into the operating
++system. Since almost all console applications must print text on the
++screen using the kernel, and must get their keyboard input through the
++kernel, they are automatically handled properly by Speakup. There are a
++few exceptions, but we'll come to those later.
++
++Note: In this guide I will refer to the numeric keypad as the keypad.
++This is done because the speakupmap.map file referred to later in this
++manual uses the term keypad instead of numeric keypad. Also I'm lazy
++and would rather only type one word. So keypad it is. Got it? Good.
++
++Most of the Speakup review keys are located on the keypad at the far
++right of the keyboard. The numlock key should be off, in order for these
++to work. If you toggle the numlock on, the keypad will produce numbers,
++which is exactly what you want for spreadsheets and such. For the
++purposes of this guide, you should have the numlock turned off, which is
++its default state at bootup.
++
++You probably won't want to listen to all the bootup messages every time
++you start your system, though it's a good idea to listen to them at
++least once, just so you'll know what kind of information is available to
++you during the boot process. You can always review these messages after
++bootup with the command:
++
++dmesg | more
++
++In order to speed the boot process, and to silence the speaking of the
++bootup messages, just press the keypad enter key. This key is located
++in the bottom right corner of the keypad. Speakup will shut up and stay
++that way, until you press another key.
++
++You can check to see if the boot process has completed by pressing the 8
++key on the keypad, which reads the current line. This also has the
++effect of starting Speakup talking again, so you can press keypad enter
++to silence it again if the boot process has not completed.
++
++When the boot process is complete, you will arrive at a "login" prompt.
++At this point, you'll need to type in your user id and password, as
++provided by your system administrator. You will hear Speakup speak the
++letters of your user id as you type it, but not the password. This is
++because the password is not displayed on the screen for security
++reasons. This has nothing to do with Speakup, it's a Linux security
++feature.
++
++Once you've logged in, you can run any Linux command or program which is
++allowed by your user id. Normal users will not be able to run programs
++which require root privileges.
++
++When you are running a program or command, Speakup will automatically
++speak new text as it arrives on the screen. You can at any time silence
++the speech with keypad enter, or use any of the Speakup review keys.
++
++Here are some basic Speakup review keys, and a short description of what
++they do.
++
++keypad 1 -- read previous character
++keypad 2 -- read current character (pressing keypad 2 twice rapidly will speak
++ the current character phonetically)
++keypad 3 -- read next character
++keypad 4 -- read previous word
++keypad 5 -- read current word (press twice rapidly to spell the current word)
++keypad 6 -- read next word
++keypad 7 -- read previous line
++keypad 8 -- read current line (press twice rapidly to hear how much the
++ text on the current line is indented)
++keypad 9 -- read next line
++keypad period -- speak current cursor position and announce current
++ virtual console
++
++It's also worth noting that the insert key on the keypad is mapped
++as the speakup key. Instead of pressing and releasing this key, as you
++do under DOS or Windows, you hold it like a shift key, and press other
++keys in combination with it. For example, repeatedly holding keypad
++insert, from now on called speakup, and keypad enter will toggle the
++speaking of new text on the screen on and off. This is not the same as
++just pressing keypad enter by itself, which just silences the speech
++until you hit another key. When you hit speakup plus keypad enter,
++Speakup will say, "You turned me off.", or "Hey, that's better." When
++Speakup is turned off, no new text on the screen will be spoken. You
++can still use the reading controls to review the screen however.
++
++3. Using the Speakup Help System
++
++Speakup has a help system, which is compiled as a module. It is loaded
++automatically whenever the Speakup help system is invoked for the first
++time, and remains loaded after that, until speakup is unloaded. Note
++that if speakup was compiled into a monolithic kernel on your system,
++you will not be able to unload Speakup from your kernel. If you try to
++use the help system, and find that it is unavailable, then your system
++administrator has not installed the Speakup help module, which is called
++speakup_help. Complain to your system administrator about this.
++
++In order to enter the Speakup help system, press and hold the speakup
++key (remember that this is the keypad insert key), and press the f1 key.
++You will hear the message:
++
++"Press space to leave help, cursor up or down to scroll, or a letter to
++go to commands in list."
++
++When you press the spacebar to leave the help system, you will hear:
++
++"Leaving help."
++
++While you are in the Speakup help system, you can scroll up or down
++through the list of available commands using the cursor keys. The list
++of commands is arranged in alphabetical order. If you wish to jump to
++commands in a specific part of the alphabet, you may press the letter of
++the alphabet you wish to jump to.
++
++You can also just explore by typing keyboard keys. Pressing keys will
++cause Speakup to speak the command associated with that key. For
++example, if you press the keypad 8 key, you will hear:
++
++"Keypad 8 is line, say current."
++
++You'll notice that some commands do not have keys assigned to them.
++This is because they are very infrequently used commands, and are also
++accessible through the proc system. We'll discuss the proc system later
++in this manual.
++
++You'll also notice that some commands have two keys assigned to them.
++This is because Speakup has a built in set of alternative key bindings
++for laptop users. The alternate speakup key is the caps lock key. You
++can press and hold the caps lock key, while pressing an alternate
++speakup command key to activate the command. On most laptops, the
++numeric keypad is defined as the keys in the j k l area of the keyboard.
++
++There is usually a function key which turns this keypad function on and
++off, and some other key which controls the numlock state. Toggling the
++keypad functionality on and off can become a royal pain. So, Speakup
++gives you a simple way to get at an alternative set of key mappings for
++your laptop. These are also available by default on desktop systems,
++because Speakup does not know whether it is running on a desktop or
++laptop. So you may choose which set of Speakup keys to use. Some
++system administrators may have chosen to compile Speakup for a desktop
++system without this set of alternate key bindings, but these details are
++beyond the scope of this manual. To use the caps lock for its normal
++purpose, hold the shift key while toggling the caps lock on and off. We
++should note here, that holding the caps lock key and pressing the z key
++will toggle the alternate j k l keypad on and off.
++
++4. Keys and Their Assigned Commands
++
++In this section, we'll go through a list of all the speakup keys and
++commands. You can also get a list of commands and assigned keys from
++the help system.
++
++The following list was taken from the speakupmap.map file. Key
++assignments are on the left of the equal sign, and the associated
++Speakup commands are on the right. The designation "spk" means to press
++and hold the speakup key, a.k.a. keypad insert, a.k.a. caps lock, while
++pressing the other specified key.
++
++spk key_f9 = punc_level_dec
++spk key_f10 = punc_level_inc
++spk key_f11 = reading_punc_dec
++spk key_f12 = reading_punc_inc
++spk key_1 = vol_dec
++spk key_2 = vol_inc
++spk key_3 = pitch_dec
++spk key_4 = pitch_inc
++spk key_5 = rate_dec
++spk key_6 = rate_inc
++key_kpasterisk = toggle_cursoring
++spk key_kpasterisk = speakup_goto
++spk key_f1 = speakup_help
++spk key_f2 = set_win
++spk key_f3 = clear_win
++spk key_f4 = enable_win
++spk key_f5 = edit_some
++spk key_f6 = edit_most
++spk key_f7 = edit_delim
++spk key_f8 = edit_repeat
++shift spk key_f9 = edit_exnum
++ key_kp7 = say_prev_line
++spk key_kp7 = left_edge
++ key_kp8 = say_line
++double key_kp8 = say_line_indent
++spk key_kp8 = say_from_top
++ key_kp9 = say_next_line
++spk key_kp9 = top_edge
++ key_kpminus = speakup_parked
++spk key_kpminus = say_char_num
++ key_kp4 = say_prev_word
++spk key_kp4 = say_from_left
++ key_kp5 = say_word
++double key_kp5 = spell_word
++spk key_kp5 = spell_phonetic
++ key_kp6 = say_next_word
++spk key_kp6 = say_to_right
++ key_kpplus = say_screen
++spk key_kpplus = say_win
++ key_kp1 = say_prev_char
++spk key_kp1 = right_edge
++ key_kp2 = say_char
++spk key_kp2 = say_to_bottom
++double key_kp2 = say_phonetic_char
++ key_kp3 = say_next_char
++spk key_kp3 = bottom_edge
++ key_kp0 = spk_key
++ key_kpdot = say_position
++spk key_kpdot = say_attributes
++key_kpenter = speakup_quiet
++spk key_kpenter = speakup_off
++key_sysrq = speech_kill
++ key_kpslash = speakup_cut
++spk key_kpslash = speakup_paste
++spk key_pageup = say_first_char
++spk key_pagedown = say_last_char
++key_capslock = spk_key
++ spk key_z = spk_lock
++key_leftmeta = spk_key
++ctrl spk key_0 = speakup_goto
++spk key_u = say_prev_line
++spk key_i = say_line
++double spk key_i = say_line_indent
++spk key_o = say_next_line
++spk key_minus = speakup_parked
++shift spk key_minus = say_char_num
++spk key_j = say_prev_word
++spk key_k = say_word
++double spk key_k = spell_word
++spk key_l = say_next_word
++spk key_m = say_prev_char
++spk key_comma = say_char
++double spk key_comma = say_phonetic_char
++spk key_dot = say_next_char
++spk key_n = say_position
++ ctrl spk key_m = left_edge
++ ctrl spk key_y = top_edge
++ ctrl spk key_dot = right_edge
++ctrl spk key_p = bottom_edge
++spk key_apostrophe = say_screen
++spk key_h = say_from_left
++spk key_y = say_from_top
++spk key_semicolon = say_to_right
++spk key_p = say_to_bottom
++spk key_slash = say_attributes
++ spk key_enter = speakup_quiet
++ ctrl spk key_enter = speakup_off
++ spk key_9 = speakup_cut
++spk key_8 = speakup_paste
++shift spk key_m = say_first_char
++ ctrl spk key_semicolon = say_last_char
++
++5. The Speakup Proc System
++
++The Speakup screen reader also creates a speakup subdirectory as a part
++of the proc system. You can see these entries by typing the command:
++
++ls -1 /proc/speakup/*
++
++If you issue the above ls command, you will get back something like
++this:
++
++/proc/speakup/attrib_bleep
++/proc/speakup/bell_pos
++/proc/speakup/bleep_time
++/proc/speakup/bleeps
++/proc/speakup/caps_start
++/proc/speakup/caps_stop
++/proc/speakup/characters
++/proc/speakup/cursor_time
++/proc/speakup/delay_time
++/proc/speakup/delimiters
++/proc/speakup/ex_num
++/proc/speakup/freq
++/proc/speakup/full_time
++/proc/speakup/jiffy_delta
++/proc/speakup/key_echo
++/proc/speakup/keymap
++/proc/speakup/no_interrupt
++/proc/speakup/pitch
++/proc/speakup/punc_all
++/proc/speakup/punc_level
++/proc/speakup/punc_most
++/proc/speakup/punc_some
++/proc/speakup/punct
++/proc/speakup/rate
++/proc/speakup/reading_punc
++/proc/speakup/repeats
++/proc/speakup/say_control
++/proc/speakup/say_word_ctl
++/proc/speakup/silent
++/proc/speakup/spell_delay
++/proc/speakup/synth_direct
++/proc/speakup/synth_name
++/proc/speakup/tone
++/proc/speakup/trigger_time
++/proc/speakup/version
++/proc/speakup/voice
++/proc/speakup/vol
++
++In addition to using the Speakup hot keys to change such things as
++volume, pitch, and rate, you can also echo values to the appropriate
++entry in the /proc/speakup directory. This is very useful, since it
++lets you control Speakup parameters from within a script. How you
++would write such scripts is somewhat beyond the scope of this manual,
++but I will include a couple of simple examples here to give you a
++general idea of what such scripts can do.
++
++Suppose for example, that you wanted to control both the punctuation
++level and the reading punctuation level at the same time. For
++simplicity, we'll call them punc0, punc1, punc2, and punc3. The scripts
++might look something like this:
++
++#!/bin/bash
++# punc0
++# set punc and reading punc levels to 0
++echo 0 >/proc/speakup/punc_level
++echo 0 >/proc/speakup/reading_punc
++echo Punctuation level set to 0.
++
++#!/bin/bash
++# punc1
++# set punc and reading punc levels to 1
++echo 1 >/proc/speakup/punc_level
++echo 1 >/proc/speakup/reading_punc
++echo Punctuation level set to 1.
++
++#!/bin/bash
++# punc2
++# set punc and reading punc levels to 2
++echo 2 >/proc/speakup/punc_level
++echo 2 >/proc/speakup/reading_punc
++echo Punctuation level set to 2.
++
++#!/bin/bash
++# punc3
++# set punc and reading punc levels to 3
++echo 3 >/proc/speakup/punc_level
++echo 3 >/proc/speakup/reading_punc
++echo Punctuation level set to 3.
++
++If you were to store these four small scripts in a directory in your
++path, perhaps /usr/local/bin, and set the permissions to 755 with the
++chmod command, then you could change the default reading punc and
++punctuation levels at the same time by issuing just one command. For
++example, if you were to execute the punc3 command at your shell prompt,
++then the reading punc and punc level would both get set to 3.
++
++I should note that the above scripts were written to work with bash, but
++regardless of which shell you use, you should be able to do something
++similar.
++
++The Speakup proc system also has another interesting use. You can echo
++Speakup parameters into the proc system in a script during system
++startup, and speakup will return to your preferred parameters every time
++the system is rebooted.
++
++Most of the Speakup proc parameters can be manipulated by a regular user
++on the system. However, there are a few parameters that are dangerous
++enough that they should only be manipulated by the root user on your
++system. There are even some parameters that are read only, and cannot
++be written to at all. For example, the version entry in the Speakup
++proc system is read only. This is because there is no reason for a user
++to tamper with the version number which is reported by Speakup. Doing
++an ls -l on /proc/speakup/version will return this:
++
++-r--r--r-- 1 root root 0 Mar 21 13:46 /proc/speakup/version
++
++As you can see, the version entry in the Speakup proc system is read
++only, is owned by root, and belongs to the root group. Doing a cat of
++/proc/speakup/version will display the Speakup version number, like
++this:
++
++cat /proc/speakup/version
++Speakup v-2.00 CVS: Thu Oct 21 10:38:21 EDT 2004
++synth dtlk version 1.1
++
++The display shows the Speakup version number, along with the version
++number of the driver for the current synthesizer.
++
++Looking at entries in the Speakup proc system can be useful in many
++ways. For example, you might wish to know what level your volume is set
++at. You could type:
++
++cat /proc/speakup/vol
++5
++
++The number five which comes back is the level at which the synthesizer
++volume is set at.
++
++All the entries in the Speakup proc system are readable, some are
++writable by root only, and some are writable by everyone. Unless you
++know what you are doing, you should probably leave the ones that are
++writable by root only alone. Most of the names are self explanatory.
++Vol for controlling volume, pitch for pitch, rate for controlling speaking
++rate, etc. If you find one you aren't sure about, you can post a query
++on the Speakup list.
++
++6. Changing Synthesizers
++
++It is possible to change to a different synthesizer while speakup is
++running. In other words, it is not necessary to reboot the system
++in order to use a different synthesizer. You can simply echo the
++synthesizer keyword to the /proc/speakup/synth_name proc entry.
++Depending on your situation, you may wish to echo none to the synth_name
++proc entry, to disable speech while one synthesizer is disconnected and
++a second one is connected in its place. Then echo the keyword for the
++new synthesizer into the synth_name proc entry in order to start speech
++with the newly connected synthesizer. See the list of synthesizer
++keywords in section 1 to find the keyword which matches your synth.
++
++7. Loading modules
++
++As mentioned earlier, Speakup can either be completely compiled into the
++kernel, with the exception of the help module, or it can be compiled as
++a series of modules. When compiled as modules, Speakup will only be
++able to speak some of the bootup messages if your system administrator
++has configured the system to load the modules at boo time. The modules
++can be loaded after the file systems have been checked and mounted, or
++from an initrd. There is a third possibility. Speakup can be compiled
++with some components built into the kernel, and others as modules. As
++we'll see in the next section, this is particularly useful when you are
++working with software synthesizers.
++
++If Speakup is completely compiled as modules, then you must use the
++modprobe command to load Speakup. You do this by loading the module for
++the synthesizer driver you wish to use. The driver modules are all
++named speakup_<keyword>, where <keyword> is the keyword for the
++synthesizer you want. So, in order to load the driver for the DecTalk
++Express, you would type the following command:
++
++modprobe speakup_dectlk
++
++Issuing this command would load the DecTalk Express driver and all other
++related Speakup modules necessary to get Speakup up and running.
++
++To completely unload Speakup, again presuming that it is entirely built
++as modules, you would give the command:
++
++modprobe -r speakup_dectlk
++
++The above command assumes you were running a DecTalk Express. If you
++were using a different synth, then you would substitute its keyword in
++place of dectlk.
++
++But now, suppose we have a situation where the main Speakup component
++is built into the kernel, and some or all of the drivers are built as
++modules. Since the main part of Speakup is compiled into the kernel, a
++partial Speakup proc system has been created which we can take advantage
++of by simply echoing the synthesizer keyword into the
++/proc/speakup/synth_name proc entry. This will cause the kernel to
++automatically load the appropriate driver module, and start Speakup
++talking. To switch to another synth, just echo a new keyword to the
++synth_name proc entry. For example, to load the DoubleTalk LT driver,
++you would type:
++
++echo ltlk >/proc/speakup/synth_name
++
++You can use the modprobe -r command to unload driver modules, regardless
++of whether the main part of Speakup has been built into the kernel or
++not.
++
++8. Using Software Synthesizers
++
++Using a software synthesizer requires that some other software be
++installed and running on your system. For this reason, software
++synthesizers are not available for use at bootup, or during a system
++installation process.
++
++In order to use a software synthesizer, you must have a package called
++Speech Dispatcher running on your system, and it must be configured to
++work with one of its supported software synthesizers.
++
++Two open source synthesizers you might use are Flite and Festival. You
++might also choose to purchase the Software DecTalk from Fonix Sales Inc.
++If you run a google search for Fonix, you'll find their web site.
++
++You can obtain a copy of Speech Dispatcher from free(b)soft at
++http://www.freebsoft.org/. Follow the installation instructions that
++come with Speech Dispatcher in order to install and configure Speech
++Dispatcher. You can check out the web site for your Linux distribution
++in order to get a copy of either Flite or Festival. Your Linux
++distribution may also have a precompiled Speech Dispatcher package.
++
++Once you've installed, configured, and tested Speech Dispatcher with your
++chosen software synthesizer, you still need one more piece of software
++in order to make things work. You need a package called speechd-up.
++You get it from the free(b)soft web site mentioned above. After you've
++compiled and installed speechd-up, you are almost ready to begin using
++your software synthesizer.
++
++Before you can use a software synthesizer, you must have created the
++/dev/softsynth device. If you have not already done so, issue the
++following commands as root:
++
++cd /dev
++mknod softsynth c 10 26
++
++While we are at it, we might just as well create the /dev/synth device,
++which can be used to let user space programs send information to your
++synthesizer. To create /dev/synth, change to the /dev directory, and
++issue the following command as root:
++
++mknod synth c 10 25
++
++Now you can begin using your software synthesizer. In order to do so,
++echo the sftsyn keyword to the synth_name proc entry like this:
++
++echo sftsyn >/proc/speakup/synth_name
++
++Next run the speechd_up command like this:
++
++speechd_up &
++
++Your synth should now start talking, and you should be able to adjust
++the pitch, rate, etc.
++
++In this section, we have assumed that your copy of Speakup was compiled
++with the speakup_sftsyn component either built into the kernel, or
++compiled as a module.
++
++9. Using The DecTalk PC Card
++
++The DecTalk PC card is an ISA card that is inserted into one of the ISA
++slots in your computer. It requires that the DecTalk PC software be
++installed on your computer, and that the software be loaded onto the
++Dectalk PC card before it can be used.
++
++You can get the dec_pc.tgz file from the linux-speakup.org site. The
++dec_pc.tgz file is in the ~ftp/pub/linux/speakup directory.
++
++After you have downloaded the dec_pc.tgz file, untar it in your home
++directory, and read the Readme file in the newly created dec_pc
++directory.
++
++The easiest way to get the software working is to copy the entire dec_pc
++directory into /user/local/lib. To do this, su to root in your home
++directory, and issue the command:
++
++cp dec_pc /usr/local/lib
++
++You will need to copy the dtload command from the dec_pc directory to a
++directory in your path. Either /usr/bin or /usr/local/bin is a good
++choice.
++
++You can now run the dtload command in order to load the DecTalk PC
++software onto the card. After you have done this, echo the decpc
++keyword to the synth_name entry in the proc system like this:
++
++echo decpc >/proc/speakup/synth_name
++
++Your DecTalk PC should start talking, and then you can adjust the pitch,
++rate, volume, voice, etc. The voice entry in the Speakup proc system
++will accept a number from 0 through 7 for the DecTalk PC synthesizer,
++which will give you access to some of the DecTalk voices.
++
++10. Using Cursor Tracking
++
++In Speakup version 2.0 and later, cursor tracking is turned on by
++default. This means that when you are using an editor, Speakup will
++automatically speak characters as you move left and right with the
++cursor keys, and lines as you move up and down with the cursor keys.
++
++This is extremely useful, and makes editing files a snap. But there are
++times when cursor tracking can get in your way. So Speakup provides a
++toggle to turn cursor tracking on and off. You do this with the keypad
++asterisk key. Pressing this key repeatedly will toggle the cursor
++tracking on and off, and you will hear Speakup say, "cursoring off", and
++"cursoring on".
++
++Some folks like to turn cursor tracking off while they are using the
++lynx web browser. You definitely want to turn cursor tracking off when
++you are using the alsamixer application. Otherwise, you won't be able
++to hear your mixer settings while you are using the arrow keys.
++
++11. Cut and Paste
++
++One of Speakup's more useful features is the ability to cut and paste
++text on the screen. This means that you can capture information from a
++program, and paste that captured text into a different place in the
++program, or into an entirely different program, which may even be
++running on a different console.
++
++For example, in this manual, we have made references to several web
++sites. It would be nice if you could cut and paste these urls into your
++web browser. Speakup does this quite nicely. Suppose you wanted to
++past the following url into your browser:
++
++http://linux-speakup.org/
++
++Use the speakup review keys to position the reading cursor on the first
++character of the above url. When the reading cursor is in position,
++press the keypad slash key once. Speakup will say, "mark". Next,
++position the reading cursor on the rightmost character of the above
++url. Press the keypad slash key once again to actually cut the text
++from the screen. Speakup will say, "cut". Although we call this
++cutting, Speakup does not actually delete the cut text from the screen.
++It makes a copy of the text in a special buffer for later pasting.
++
++Now that you have the url cut from the screen, you can paste it into
++your browser, or even paste the url on a command line as an argument to
++your browser.
++
++Suppose you want to start lynx and go to the Speakup site.
++
++You can switch to a different console with the alt left and right
++arrows, or you can switch to a specific console by typing alt and a
++function key. These are not Speakup commands, just standard Linux
++console capabilities.
++
++Once you've changed to an appropriate console, and are at a shell prompt,
++type the word lynx, followed by a space. Now press and hold the speakup
++key, while you type the keypad slash character. The url will be pasted
++onto the command line, just as though you had typed it in. Press the
++enter key to execute the command.
++
++The paste buffer will continue to hold the cut information, until a new
++mark and cut operation is carried out. This means you can paste the cut
++information as many times as you like before doing another cut
++operation.
++
++You are not limited to cutting and pasting only one line on the screen.
++You can also cut and paste rectangular regions of the screen. Just
++position the reading cursor at the top left corner of the text to be
++cut, mark it with the keypad slash key, then position the reading cursor
++at the bottom right corner of the region to be cut, and cut it with the
++keypad slash key.
++
++12. Changing the Pronunciation of Characters
++
++Through the /proc/speakup/chars proc entry, Speakup gives you the
++ability to change how Speakup pronounces a given character. You could,
++for example, change how some punctuation characters are spoken. You can
++even change how Speakup will pronounce certain letters.
++
++You may, for example, wish to change how Speakup pronounces the z
++character. The author of Speakup, Kirk Reiser, is Canadian, and thus
++believes that the z should be pronounced zed. If you are an American,
++you might wish to use the zee pronunciation instead of zed. You can
++change the pronunciation of both the upper and lower case z with the
++following two commands:
++
++echo 90 zee >/proc/speakup/characters
++echo 122 zee >/proc/speakup/characters
++
++Let's examine the parts of the two previous commands. They are issued
++at the shell prompt, and could be placed in a startup script.
++
++The word echo tells the shell that you want to have it display the
++string of characters that follow the word echo. If you were to just
++type:
++
++echo hello.
++
++You would get the word hello printed on your screen as soon as you
++pressed the enter key. In this case, we are echoing strings that we
++want to be redirected into the proc system.
++
++The numbers 90 and 122 in the above echo commands are the ascii numeric
++values for the upper and lower case z, the characters we wish to change.
++
++The string zee is the pronunciation that we want Speakup to use for the
++upper and lower case z.
++
++The > symbol redirects the output of the echo command to a file, just
++like in DOS, or at the Windows command prompt.
++
++And finally, /proc/speakup/chars is the file entry in the proc system
++where we want the output to be directed. Speakup looks at the numeric
++value of the character we want to change, and inserts the pronunciation
++string into an internal table.
++
++You can look at the whole table with the following command:
++
++cat /proc/speakup/chars
++
++Speakup will then print out the entire character pronunciation table. I
++won't display it here, but leave you to look at it at your convenience.
++
++13. Mapping Keys
++
++Speakup has the capability of allowing you to assign or "map" keys to
++internal Speakup commands. This section necessarily assumes you have a
++Linux kernel source tree installed, and that it has been patched and
++configured with Speakup. How you do this is beyond the scope of this
++manual. For this information, visit the Speakup web site at
++http://linux-speakup.org/. The reason you'll need the kernel source
++tree patched with Speakup is that the genmap utility you'll need for
++processing keymaps is in the
++/usr/src/linux-<version_number>/drivers/char/speakup directory. The
++<version_number> in the above directory path is the version number of
++the Linux source tree you are working with.
++
++So ok, you've gone off and gotten your kernel source tree, and patched
++and configured it. Now you can start manipulating keymaps.
++
++You can either use the
++/usr/src/linux-<version_number>/drivers/char/speakup/speakupmap.map file
++included with the Speakup source, or you can cut and paste the copy in
++section 4 into a separate file. If you use the one in the Speakup
++source tree, make sure you make a backup of it before you start making
++changes. You have been warned!
++
++Suppose that you want to swap the key assignments for the Speakup
++say_last_char and the Speakup say_first_char commands. The
++speakupmap.map lists the key mappings for these two commands as follows:
++
++spk key_pageup = say_first_char
++spk key_pagedown = say_last_char
++
++You can edit your copy of the speakupmap.map file and swap the command
++names on the right side of the = (equals) sign. You did make a backup,
++right? The new keymap lines would look like this:
++
++spk key_pageup = say_last_char
++spk key_pagedown = say_first_char
++
++After you edit your copy of the speakupmap.map file, save it under a new
++file name, perhaps newmap.map. Then exit your editor and return to the
++shell prompt.
++
++You are now ready to load your keymap with your swapped key assignments.
++ Assuming that you saved your new keymap as the file newmap.map, you
++would load your keymap into the proc system like this:
++
++/usr/src/linux-<version_number>/drivers/char/speakup/genmap newmap.map
++>/proc/speakup/keymap
++
++Remember to substitute your kernel version number for the
++<version_number> in the above command. Also note that although the
++above command wrapped onto two lines in this document, you should type
++it all on one line.
++
++Your say first and say last characters should now be swapped. Pressing
++speakup pagedown should read you the first non-whitespace character on
++the line your reading cursor is in, and pressing speakup pageup should
++read you the last character on the line your reading cursor is in.
++
++You should note that these new mappings will only stay in effect until
++you reboot, or until you load another keymap.
++
++One final warning. If you try to load a partial map, you will quickly
++find that all the mappings you didn't include in your file got deleted
++from the working map. Be extremely careful, and always make a backup!
++You have been warned!
++
++14. Using Speakup's Windowing Capability
++
++Speakup has the capability of defining and manipulating windows on the
++screen. Speakup uses the term "Window", to mean a user defined area of
++the screen. The key strokes for defining and manipulating Speakup
++windows are as follows:
++
++speakup + f2 -- Set the bounds of the window.
++Speakup + f3 -- clear the current window definition.
++speakup + f4 -- Toggle window silence on and off.
++speakup + keypad plus -- Say the currently defined window.
++
++These capabilities are useful for tracking a certain part of the screen
++without rereading the whole screen, or for silencing a part of the
++screen that is constantly changing, such as a clock or status line.
++
++There is no way to save these window settings, and you can only have one
++window defined for each virtual console. There is also no way to have
++windows automaticly defined for specific applications.
++
++In order to define a window, use the review keys to move your reading
++cursor to the beginning of the area you want to define. Then press
++speakup + f2. Speakup will tell you that the window starts at the
++indicated row and column position. Then move the reading cursor to the
++end of the area to be defined as a window, and press speakup + f2 again.
++ If there is more than one line in the window, Speakup will tell you
++that the window ends at the indicated row and column position. If there
++is only one line in the window, then Speakup will tell you that the
++window is the specified line on the screen. If you are only defining a
++one line window, you can just press speakup + f2 twice after placing the
++reading cursor on the line you want to define as a window. It is not
++necessary to position the reading cursor at the end of the line in order
++to define the whole line as a window.
++
++ GNU Free Documentation License
++ Version 1.2, November 2002
++
++
++ Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
++ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++
++0. PREAMBLE
++
++The purpose of this License is to make a manual, textbook, or other
++functional and useful document "free" in the sense of freedom: to
++assure everyone the effective freedom to copy and redistribute it,
++with or without modifying it, either commercially or noncommercially.
++Secondarily, this License preserves for the author and publisher a way
++to get credit for their work, while not being considered responsible
++for modifications made by others.
++
++This License is a kind of "copyleft", which means that derivative
++works of the document must themselves be free in the same sense. It
++complements the GNU General Public License, which is a copyleft
++license designed for free software.
++
++We have designed this License in order to use it for manuals for free
++software, because free software needs free documentation: a free
++program should come with manuals providing the same freedoms that the
++software does. But this License is not limited to software manuals;
++it can be used for any textual work, regardless of subject matter or
++whether it is published as a printed book. We recommend this License
++principally for works whose purpose is instruction or reference.
++
++
++1. APPLICABILITY AND DEFINITIONS
++
++This License applies to any manual or other work, in any medium, that
++contains a notice placed by the copyright holder saying it can be
++distributed under the terms of this License. Such a notice grants a
++world-wide, royalty-free license, unlimited in duration, to use that
++work under the conditions stated herein. The "Document", below,
++refers to any such manual or work. Any member of the public is a
++licensee, and is addressed as "you". You accept the license if you
++copy, modify or distribute the work in a way requiring permission
++under copyright law.
++
++A "Modified Version" of the Document means any work containing the
++Document or a portion of it, either copied verbatim, or with
++modifications and/or translated into another language.
++
++A "Secondary Section" is a named appendix or a front-matter section of
++the Document that deals exclusively with the relationship of the
++publishers or authors of the Document to the Document's overall subject
++(or to related matters) and contains nothing that could fall directly
++within that overall subject. (Thus, if the Document is in part a
++textbook of mathematics, a Secondary Section may not explain any
++mathematics.) The relationship could be a matter of historical
++connection with the subject or with related matters, or of legal,
++commercial, philosophical, ethical or political position regarding
++them.
++
++The "Invariant Sections" are certain Secondary Sections whose titles
++are designated, as being those of Invariant Sections, in the notice
++that says that the Document is released under this License. If a
++section does not fit the above definition of Secondary then it is not
++allowed to be designated as Invariant. The Document may contain zero
++Invariant Sections. If the Document does not identify any Invariant
++Sections then there are none.
++
++The "Cover Texts" are certain short passages of text that are listed,
++as Front-Cover Texts or Back-Cover Texts, in the notice that says that
++the Document is released under this License. A Front-Cover Text may
++be at most 5 words, and a Back-Cover Text may be at most 25 words.
++
++A "Transparent" copy of the Document means a machine-readable copy,
++represented in a format whose specification is available to the
++general public, that is suitable for revising the document
++straightforwardly with generic text editors or (for images composed of
++pixels) generic paint programs or (for drawings) some widely available
++drawing editor, and that is suitable for input to text formatters or
++for automatic translation to a variety of formats suitable for input
++to text formatters. A copy made in an otherwise Transparent file
++format whose markup, or absence of markup, has been arranged to thwart
++or discourage subsequent modification by readers is not Transparent.
++An image format is not Transparent if used for any substantial amount
++of text. A copy that is not "Transparent" is called "Opaque".
++
++Examples of suitable formats for Transparent copies include plain
++ASCII without markup, Texinfo input format, LaTeX input format, SGML
++or XML using a publicly available DTD, and standard-conforming simple
++HTML, PostScript or PDF designed for human modification. Examples of
++transparent image formats include PNG, XCF and JPG. Opaque formats
++include proprietary formats that can be read and edited only by
++proprietary word processors, SGML or XML for which the DTD and/or
++processing tools are not generally available, and the
++machine-generated HTML, PostScript or PDF produced by some word
++processors for output purposes only.
++
++The "Title Page" means, for a printed book, the title page itself,
++plus such following pages as are needed to hold, legibly, the material
++this License requires to appear in the title page. For works in
++formats which do not have any title page as such, "Title Page" means
++the text near the most prominent appearance of the work's title,
++preceding the beginning of the body of the text.
++
++A section "Entitled XYZ" means a named subunit of the Document whose
++title either is precisely XYZ or contains XYZ in parentheses following
++text that translates XYZ in another language. (Here XYZ stands for a
++specific section name mentioned below, such as "Acknowledgements",
++"Dedications", "Endorsements", or "History".) To "Preserve the Title"
++of such a section when you modify the Document means that it remains a
++section "Entitled XYZ" according to this definition.
++
++The Document may include Warranty Disclaimers next to the notice which
++states that this License applies to the Document. These Warranty
++Disclaimers are considered to be included by reference in this
++License, but only as regards disclaiming warranties: any other
++implication that these Warranty Disclaimers may have is void and has
++no effect on the meaning of this License.
++
++
++2. VERBATIM COPYING
++
++You may copy and distribute the Document in any medium, either
++commercially or noncommercially, provided that this License, the
++copyright notices, and the license notice saying this License applies
++to the Document are reproduced in all copies, and that you add no other
++conditions whatsoever to those of this License. You may not use
++technical measures to obstruct or control the reading or further
++copying of the copies you make or distribute. However, you may accept
++compensation in exchange for copies. If you distribute a large enough
++number of copies you must also follow the conditions in section 3.
++
++You may also lend copies, under the same conditions stated above, and
++you may publicly display copies.
++
++
++3. COPYING IN QUANTITY
++
++If you publish printed copies (or copies in media that commonly have
++printed covers) of the Document, numbering more than 100, and the
++Document's license notice requires Cover Texts, you must enclose the
++copies in covers that carry, clearly and legibly, all these Cover
++Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
++the back cover. Both covers must also clearly and legibly identify
++you as the publisher of these copies. The front cover must present
++the full title with all words of the title equally prominent and
++visible. You may add other material on the covers in addition.
++Copying with changes limited to the covers, as long as they preserve
++the title of the Document and satisfy these conditions, can be treated
++as verbatim copying in other respects.
++
++If the required texts for either cover are too voluminous to fit
++legibly, you should put the first ones listed (as many as fit
++reasonably) on the actual cover, and continue the rest onto adjacent
++pages.
++
++If you publish or distribute Opaque copies of the Document numbering
++more than 100, you must either include a machine-readable Transparent
++copy along with each Opaque copy, or state in or with each Opaque copy
++a computer-network location from which the general network-using
++public has access to download using public-standard network protocols
++a complete Transparent copy of the Document, free of added material.
++If you use the latter option, you must take reasonably prudent steps,
++when you begin distribution of Opaque copies in quantity, to ensure
++that this Transparent copy will remain thus accessible at the stated
++location until at least one year after the last time you distribute an
++Opaque copy (directly or through your agents or retailers) of that
++edition to the public.
++
++It is requested, but not required, that you contact the authors of the
++Document well before redistributing any large number of copies, to give
++them a chance to provide you with an updated version of the Document.
++
++
++4. MODIFICATIONS
++
++You may copy and distribute a Modified Version of the Document under
++the conditions of sections 2 and 3 above, provided that you release
++the Modified Version under precisely this License, with the Modified
++Version filling the role of the Document, thus licensing distribution
++and modification of the Modified Version to whoever possesses a copy
++of it. In addition, you must do these things in the Modified Version:
++
++A. Use in the Title Page (and on the covers, if any) a title distinct
++ from that of the Document, and from those of previous versions
++ (which should, if there were any, be listed in the History section
++ of the Document). You may use the same title as a previous version
++ if the original publisher of that version gives permission.
++B. List on the Title Page, as authors, one or more persons or entities
++ responsible for authorship of the modifications in the Modified
++ Version, together with at least five of the principal authors of the
++ Document (all of its principal authors, if it has fewer than five),
++ unless they release you from this requirement.
++C. State on the Title page the name of the publisher of the
++ Modified Version, as the publisher.
++D. Preserve all the copyright notices of the Document.
++E. Add an appropriate copyright notice for your modifications
++ adjacent to the other copyright notices.
++F. Include, immediately after the copyright notices, a license notice
++ giving the public permission to use the Modified Version under the
++ terms of this License, in the form shown in the Addendum below.
++G. Preserve in that license notice the full lists of Invariant Sections
++ and required Cover Texts given in the Document's license notice.
++H. Include an unaltered copy of this License.
++I. Preserve the section Entitled "History", Preserve its Title, and add
++ to it an item stating at least the title, year, new authors, and
++ publisher of the Modified Version as given on the Title Page. If
++ there is no section Entitled "History" in the Document, create one
++ stating the title, year, authors, and publisher of the Document as
++ given on its Title Page, then add an item describing the Modified
++ Version as stated in the previous sentence.
++J. Preserve the network location, if any, given in the Document for
++ public access to a Transparent copy of the Document, and likewise
++ the network locations given in the Document for previous versions
++ it was based on. These may be placed in the "History" section.
++ You may omit a network location for a work that was published at
++ least four years before the Document itself, or if the original
++ publisher of the version it refers to gives permission.
++K. For any section Entitled "Acknowledgements" or "Dedications",
++ Preserve the Title of the section, and preserve in the section all
++ the substance and tone of each of the contributor acknowledgements
++ and/or dedications given therein.
++L. Preserve all the Invariant Sections of the Document,
++ unaltered in their text and in their titles. Section numbers
++ or the equivalent are not considered part of the section titles.
++M. Delete any section Entitled "Endorsements". Such a section
++ may not be included in the Modified Version.
++N. Do not retitle any existing section to be Entitled "Endorsements"
++ or to conflict in title with any Invariant Section.
++O. Preserve any Warranty Disclaimers.
++
++If the Modified Version includes new front-matter sections or
++appendices that qualify as Secondary Sections and contain no material
++copied from the Document, you may at your option designate some or all
++of these sections as invariant. To do this, add their titles to the
++list of Invariant Sections in the Modified Version's license notice.
++These titles must be distinct from any other section titles.
++
++You may add a section Entitled "Endorsements", provided it contains
++nothing but endorsements of your Modified Version by various
++parties--for example, statements of peer review or that the text has
++been approved by an organization as the authoritative definition of a
++standard.
++
++You may add a passage of up to five words as a Front-Cover Text, and a
++passage of up to 25 words as a Back-Cover Text, to the end of the list
++of Cover Texts in the Modified Version. Only one passage of
++Front-Cover Text and one of Back-Cover Text may be added by (or
++through arrangements made by) any one entity. If the Document already
++includes a cover text for the same cover, previously added by you or
++by arrangement made by the same entity you are acting on behalf of,
++you may not add another; but you may replace the old one, on explicit
++permission from the previous publisher that added the old one.
++
++The author(s) and publisher(s) of the Document do not by this License
++give permission to use their names for publicity for or to assert or
++imply endorsement of any Modified Version.
++
++
++5. COMBINING DOCUMENTS
++
++You may combine the Document with other documents released under this
++License, under the terms defined in section 4 above for modified
++versions, provided that you include in the combination all of the
++Invariant Sections of all of the original documents, unmodified, and
++list them all as Invariant Sections of your combined work in its
++license notice, and that you preserve all their Warranty Disclaimers.
++
++The combined work need only contain one copy of this License, and
++multiple identical Invariant Sections may be replaced with a single
++copy. If there are multiple Invariant Sections with the same name but
++different contents, make the title of each such section unique by
++adding at the end of it, in parentheses, the name of the original
++author or publisher of that section if known, or else a unique number.
++Make the same adjustment to the section titles in the list of
++Invariant Sections in the license notice of the combined work.
++
++In the combination, you must combine any sections Entitled "History"
++in the various original documents, forming one section Entitled
++"History"; likewise combine any sections Entitled "Acknowledgements",
++and any sections Entitled "Dedications". You must delete all sections
++Entitled "Endorsements".
++
++
++6. COLLECTIONS OF DOCUMENTS
++
++You may make a collection consisting of the Document and other documents
++released under this License, and replace the individual copies of this
++License in the various documents with a single copy that is included in
++the collection, provided that you follow the rules of this License for
++verbatim copying of each of the documents in all other respects.
++
++You may extract a single document from such a collection, and distribute
++it individually under this License, provided you insert a copy of this
++License into the extracted document, and follow this License in all
++other respects regarding verbatim copying of that document.
++
++
++7. AGGREGATION WITH INDEPENDENT WORKS
++
++A compilation of the Document or its derivatives with other separate
++and independent documents or works, in or on a volume of a storage or
++distribution medium, is called an "aggregate" if the copyright
++resulting from the compilation is not used to limit the legal rights
++of the compilation's users beyond what the individual works permit.
++When the Document is included in an aggregate, this License does not
++apply to the other works in the aggregate which are not themselves
++derivative works of the Document.
++
++If the Cover Text requirement of section 3 is applicable to these
++copies of the Document, then if the Document is less than one half of
++the entire aggregate, the Document's Cover Texts may be placed on
++covers that bracket the Document within the aggregate, or the
++electronic equivalent of covers if the Document is in electronic form.
++Otherwise they must appear on printed covers that bracket the whole
++aggregate.
++
++
++8. TRANSLATION
++
++Translation is considered a kind of modification, so you may
++distribute translations of the Document under the terms of section 4.
++Replacing Invariant Sections with translations requires special
++permission from their copyright holders, but you may include
++translations of some or all Invariant Sections in addition to the
++original versions of these Invariant Sections. You may include a
++translation of this License, and all the license notices in the
++Document, and any Warranty Disclaimers, provided that you also include
++the original English version of this License and the original versions
++of those notices and disclaimers. In case of a disagreement between
++the translation and the original version of this License or a notice
++or disclaimer, the original version will prevail.
++
++If a section in the Document is Entitled "Acknowledgements",
++"Dedications", or "History", the requirement (section 4) to Preserve
++its Title (section 1) will typically require changing the actual
++title.
++
++
++9. TERMINATION
++
++You may not copy, modify, sublicense, or distribute the Document except
++as expressly provided for under this License. Any other attempt to
++copy, modify, sublicense or distribute the Document is void, and will
++automatically terminate your rights under this License. However,
++parties who have received copies, or rights, from you under this
++License will not have their licenses terminated so long as such
++parties remain in full compliance.
++
++
++10. FUTURE REVISIONS OF THIS LICENSE
++
++The Free Software Foundation may publish new, revised versions
++of the GNU Free Documentation License from time to time. Such new
++versions will be similar in spirit to the present version, but may
++differ in detail to address new problems or concerns. See
++http://www.gnu.org/copyleft/.
++
++Each version of the License is given a distinguishing version number.
++If the Document specifies that a particular numbered version of this
++License "or any later version" applies to it, you have the option of
++following the terms and conditions either of that specified version or
++of any later version that has been published (not as a draft) by the
++Free Software Foundation. If the Document does not specify a version
++number of this License, you may choose any version ever published (not
++as a draft) by the Free Software Foundation.
++
++
++ADDENDUM: How to use this License for your documents
++
++To use this License in a document you have written, include a copy of
++the License in the document and put the following copyright and
++license notices just after the title page:
++
++ Copyright (c) YEAR YOUR NAME.
++ Permission is granted to copy, distribute and/or modify this document
++ under the terms of the GNU Free Documentation License, Version 1.2
++ or any later version published by the Free Software Foundation;
++ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
++ A copy of the license is included in the section entitled "GNU
++ Free Documentation License".
++
++If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
++replace the "with...Texts." line with this:
++
++ with the Invariant Sections being LIST THEIR TITLES, with the
++ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
++
++If you have Invariant Sections without Cover Texts, or some other
++combination of the three, merge those two alternatives to suit the
++situation.
++
++If your document contains nontrivial examples of program code, we
++recommend releasing these examples in parallel under your choice of
++free software license, such as the GNU General Public License,
++to permit their use in free software.
++
++The End.
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/drivers/char/consolemap.c linux-2.6.20-spk/drivers/char/consolemap.c
+--- linux-2.6.20/drivers/char/consolemap.c 2007-02-04 16:08:01.000000000 -0500
++++ linux-2.6.20-spk/drivers/char/consolemap.c 2007-02-04 16:09:21.000000000 -0500
+@@ -667,3 +667,4 @@ console_map_init(void)
+ }
+
+ EXPORT_SYMBOL(con_copy_unimap);
++EXPORT_SYMBOL_GPL(inverse_translate);
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/drivers/char/keyboard.c linux-2.6.20-spk/drivers/char/keyboard.c
+--- linux-2.6.20/drivers/char/keyboard.c 2007-02-04 16:08:01.000000000 -0500
++++ linux-2.6.20-spk/drivers/char/keyboard.c 2007-02-04 16:09:21.000000000 -0500
+@@ -41,6 +41,22 @@
+ #include <linux/input.h>
+ #include <linux/reboot.h>
+
++
++#include <linux/speakup.h>
++
++#if defined(CONFIG_SPEAKUP_MODULE)
++
++spk_key_func addr_spk_key = NULL;
++#define speakup_key_call(__vc, __shift, __keycode, __key, __down) \
++ (addr_spk_key && (*addr_spk_key)(__vc, __shift, __keycode, __key, __down))
++
++#elif defined(CONFIG_SPEAKUP)
++#define speakup_key_call(__vc, __shift, __keycode, __key, __down) \
++ speakup_key(__vc, __shift, __keycode, __key, __down)
++#else
++#define speakup_key_call(__vc, __shift, __keycode, __key, __down) 0
++#endif
++
+ static void kbd_disconnect(struct input_handle *handle);
+ extern void ctrl_alt_del(void);
+
+@@ -65,6 +81,10 @@ extern void ctrl_alt_del(void);
+
+ #define KBD_DEFLOCK 0
+
++/* Key types processed even in raw modes */
++
++#define TYPES_ALLOWED_IN_RAW_MODE ((1 << KT_SPEC) | (1 << KT_SHIFT) | (1 << KT_SPKUP))
++
+ void compute_shiftstate(void);
+
+ /*
+@@ -80,7 +100,7 @@ void compute_shiftstate(void);
+ typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
+ char up_flag);
+ static k_handler_fn K_HANDLERS;
+-static k_handler_fn *k_handler[16] = { K_HANDLERS };
++k_handler_fn *k_handler[16] = { K_HANDLERS };
+
+ #define FN_HANDLERS\
+ fn_null, fn_enter, fn_show_ptregs, fn_show_mem,\
+@@ -101,13 +121,16 @@ static fn_handler_fn *fn_handler[] = { F
+ const int max_vals[] = {
+ 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1,
+ NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1,
+- 255, NR_LOCK - 1, 255, NR_BRL - 1
++ 255, NR_LOCK - 1, 255, NR_BRL - 1, 255
+ };
+
+ const int NR_TYPES = ARRAY_SIZE(max_vals);
+
+ struct kbd_struct kbd_table[MAX_NR_CONSOLES];
+-static struct kbd_struct *kbd = kbd_table;
++struct kbd_struct *kbd = kbd_table;
++
++EXPORT_SYMBOL_GPL(kbd);
++EXPORT_SYMBOL_GPL(k_handler);
+
+ struct vt_spawn_console vt_spawn_con = {
+ .lock = SPIN_LOCK_UNLOCKED,
+@@ -266,6 +289,8 @@ void kd_mksound(unsigned int hz, unsigne
+ kd_nosound(0);
+ }
+
++EXPORT_SYMBOL_GPL(kd_mksound);
++
+ /*
+ * Setting the keyboard rate.
+ */
+@@ -623,6 +648,7 @@ static void k_spec(struct vc_data *vc, u
+ if (up_flag)
+ return;
+ if (value >= ARRAY_SIZE(fn_handler))
++ if (up_flag || (value >= ARRAY_SIZE(fn_handler)))
+ return;
+ if ((kbd->kbdmode == VC_RAW ||
+ kbd->kbdmode == VC_MEDIUMRAW) &&
+@@ -1233,6 +1259,9 @@ static void kbd_keycode(unsigned int key
+ key_map = key_maps[shift_final];
+
+ if (!key_map) {
++ if (speakup_key_call(vc, shift_final, keycode, K(KT_SHIFT,0), !down))
++ return;
++
+ compute_shiftstate();
+ kbd->slockstate = 0;
+ return;
+@@ -1256,6 +1285,9 @@ static void kbd_keycode(unsigned int key
+
+ type -= 0xf0;
+
++ if (speakup_key_call(vc, shift_final, keycode, keysym, !down))
++ return;
++
+ if (raw_mode && type != KT_SPEC && type != KT_SHIFT)
+ return;
+
+@@ -1274,6 +1306,9 @@ static void kbd_keycode(unsigned int key
+ kbd->slockstate = 0;
+ }
+
++struct input_dev *fakekeydev=NULL;
++EXPORT_SYMBOL_GPL(fakekeydev);
++
+ static void kbd_event(struct input_handle *handle, unsigned int event_type,
+ unsigned int event_code, int value)
+ {
+@@ -1311,6 +1346,7 @@ static struct input_handle *kbd_connect(
+ return NULL;
+
+ handle->dev = dev;
++ fakekeydev = dev;
+ handle->handler = handler;
+ handle->name = "kbd";
+
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/drivers/char/Makefile linux-2.6.20-spk/drivers/char/Makefile
+--- linux-2.6.20/drivers/char/Makefile 2007-02-04 16:08:01.000000000 -0500
++++ linux-2.6.20-spk/drivers/char/Makefile 2007-02-04 16:09:21.000000000 -0500
+@@ -91,6 +91,7 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.
+ obj-$(CONFIG_TANBAC_TB0219) += tb0219.o
+ obj-$(CONFIG_TELCLOCK) += tlclk.o
+
++obj-$(CONFIG_SPEAKUP) += speakup/
+ obj-$(CONFIG_WATCHDOG) += watchdog/
+ obj-$(CONFIG_MWAVE) += mwave/
+ obj-$(CONFIG_AGP) += agp/
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/drivers/char/speakup/Config.in linux-2.6.20-spk/drivers/char/speakup/Config.in
+--- linux-2.6.20/drivers/char/speakup/Config.in 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20-spk/drivers/char/speakup/Config.in 2007-02-04 16:09:19.000000000 -0500
+@@ -0,0 +1,26 @@
++tristate 'Speakup console speech' CONFIG_SPEAKUP
++if [ "$CONFIG_SPEAKUP" != "n" ]; then
++ comment 'Type "y" for each synthesizer you want built into the kernel.'
++ dep_tristate "Accent SA, acntsa" CONFIG_SPEAKUP_ACNTSA $CONFIG_SPEAKUP
++ dep_tristate "Accent PC, acntpc" CONFIG_SPEAKUP_ACNTPC $CONFIG_SPEAKUP
++ dep_tristate "Apollo, apollo" CONFIG_SPEAKUP_APOLLO $CONFIG_SPEAKUP
++ dep_tristate "Audapter, audptr" CONFIG_SPEAKUP_AUDPTR $CONFIG_SPEAKUP
++ dep_tristate "Braille 'n' Speak, bns" CONFIG_SPEAKUP_BNS $CONFIG_SPEAKUP
++ dep_tristate "DECtalk Express, dectlk" CONFIG_SPEAKUP_DECTLK $CONFIG_SPEAKUP
++ dep_tristate "DECtalk External (old), decext" CONFIG_SPEAKUP_DECEXT $CONFIG_SPEAKUP
++ dep_tristate "DECtalk PC (big ISA card), decpc" CONFIG_SPEAKUP_DECPC $CONFIG_SPEAKUP
++ if [ "$CONFIG_SPEAKUP_DECPC" = "y" ] ;then
++ comment 'warning: decpc can only be built as a module'
++ fi
++ comment 'In order to use this you will need'
++ comment 'the dtload program and DECPC software files '
++ comment 'Read the accompanying DECPC documentation for more details'
++ dep_tristate "DoubleTalk PC, dtlk" CONFIG_SPEAKUP_DTLK $CONFIG_SPEAKUP
++ dep_tristate "Keynote Gold PC, keypc" CONFIG_SPEAKUP_KEYPC $CONFIG_SPEAKUP
++ dep_tristate "DoubleTalk LT or LiteTalk, ltlk" CONFIG_SPEAKUP_LTLK $CONFIG_SPEAKUP
++ dep_tristate "Software synthesizers /dev/sftsyn, sftsyn" CONFIG_SPEAKUP_SFTSYN $CONFIG_SPEAKUP
++ dep_tristate "Speak Out, spkout" CONFIG_SPEAKUP_SPKOUT $CONFIG_SPEAKUP
++ dep_tristate "Transport, txprt" CONFIG_SPEAKUP_TXPRT $CONFIG_SPEAKUP
++ comment 'Enter the three to six character synth string from above or none.'
++ string "Default synthesizer for Speakup" CONFIG_SPEAKUP_DEFAULT "none"
++fi
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/drivers/char/speakup/cvsversion.h linux-2.6.20-spk/drivers/char/speakup/cvsversion.h
+--- linux-2.6.20/drivers/char/speakup/cvsversion.h 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20-spk/drivers/char/speakup/cvsversion.h 2007-02-04 16:09:20.000000000 -0500
+@@ -0,0 +1 @@
++#define CVSVERSION " CVS: Sat Oct 7 10:52:29 EDT 2006 "
+diff --exclude='*.orig' --exclude='*.patch' -urNp linux-2.6.20/drivers/char/speakup/dtload.c linux-2.6.20-spk/drivers/char/speakup/dtload.c
+--- linux-2.6.20/drivers/char/speakup/dtload.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20-spk/drivers/char/speakup/dtload.c 2007-02-04 16:09:19.000000000 -0500
+@@ -0,0 +1,553 @@
++/*
++ * This is the DECtalk PC firmware loader for the Linux kernel, version 1.0
++ *
++ * Original 386BSD source:
++ * Copyright ( c ) 1996 Brian Buhrow <buhrow@lothlorien.nfbcal.org>
++ *
++ * Adapted for Linux:
++ * Copyright ( c ) 1997 Nicolas Pitre <nico@cam.org>
++ *
++ * Adapted for speakup:
++ * Copyright ( c ) 2003 David Borowski <david575@golden.net>
++ *
++ * All rights reserved.
++ *
++ * 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.
++ *
++ * 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <string.h>
++#include <sys/types.h>