Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3741

kernel-2.6.18-194.11.1.el5.src.rpm

From: Steve Grubb <sgrubb@redhat.com>
Subject: [RHEL5 PATCH] squashfs fixup
Date: Wed, 13 Dec 2006 15:27:04 -0500
Bugzilla: 219534
Message-Id: <200612131527.04499.sgrubb@redhat.com>
Changelog: squashfs fixup


Hi,

The patch below is to fix BZ 219534. It is from the upstream 
maintainer of squashfs. The problem is that when fsfuzz is run 
against squashfs, random applications segfault due to corrupted
kernel memory. There are several Oopses, too.

This patch has survived fsfuzzer and appears to be safer than
what we current ship.

-Steve


diff -urp linux-2.6.18.x86_64.orig/fs/squashfs/inode.c linux-2.6.18.x86_64/fs/squashfs/inode.c
--- linux-2.6.18.x86_64.orig/fs/squashfs/inode.c	2006-12-13 13:42:35.000000000 -0500
+++ linux-2.6.18.x86_64/fs/squashfs/inode.c	2006-12-13 13:43:42.000000000 -0500
@@ -56,12 +56,14 @@ static int init_inodecache(void);
 static void destroy_inodecache(void);
 static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
 				struct nameidata *);
-static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode);
+static 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 struct super_block *squashfs_get_sb(struct file_system_type *, int,
-				const char *, void *, struct vfsmount *);
+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,
@@ -75,13 +77,25 @@ static unsigned char squashfs_filetype_t
 	DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
 };
 
-static struct super_operations squashfs_ops = {
+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
 };
@@ -173,14 +187,15 @@ out:
 
 SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
 			long long index, unsigned int length,
-			long long *next_index)
+			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;
+	int bytes, avail_bytes, b = 0, k = 0;
 	char *c_buffer;
 	unsigned int compressed;
 	unsigned int c_byte = length;
@@ -191,8 +206,11 @@ SQSH_EXTERN unsigned int squashfs_read_d
 		c_buffer = compressed ? msblk->read_data : buffer;
 		c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
 
-		TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
-					? "" : "un", (unsigned int) c_byte);
+		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;
@@ -204,6 +222,9 @@ SQSH_EXTERN unsigned int squashfs_read_d
 		}
 		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;
@@ -216,6 +237,9 @@ SQSH_EXTERN unsigned int squashfs_read_d
 		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;
@@ -227,7 +251,7 @@ SQSH_EXTERN unsigned int squashfs_read_d
 	if (compressed)
 		down(&msblk->read_data_mutex);
 
-	for (bytes = 0, k = 0; k < b; k++) {
+	for (bytes = 0; k < b; k++) {
 		avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
 					msblk->devblksize - offset :
 					c_byte - bytes;
@@ -249,14 +273,17 @@ SQSH_EXTERN unsigned int squashfs_read_d
 		msblk->stream.next_in = c_buffer;
 		msblk->stream.avail_in = c_byte;
 		msblk->stream.next_out = buffer;
-		msblk->stream.avail_out = msblk->read_size;
+		//msblk->stream.avail_out = msblk->read_size;//srclength;
+		msblk->stream.avail_out = srclength;
 
 		if (((zlib_err = zlib_inflateInit(&msblk->stream)) != Z_OK) ||
 				((zlib_err = zlib_inflate(&msblk->stream, Z_FINISH))
 				 != Z_STREAM_END) || ((zlib_err =
 				zlib_inflateEnd(&msblk->stream)) != Z_OK)) {
-			ERROR("zlib_fs returned unexpected result 0x%x\n",
-				zlib_err);
+			//ERROR("zlib_fs returned unexpected result 0x%x\n",
+			//	zlib_err);
+			ERROR("zlib_fs returned unexpected result 0x%x, srclength %d\n",
+				zlib_err, srclength);
 			bytes = 0;
 		} else
 			bytes = msblk->stream.total_out;
@@ -271,8 +298,8 @@ SQSH_EXTERN unsigned int squashfs_read_d
 	return bytes;
 
 block_release:
-	while (--b >= 0)
-		brelse(bh[b]);
+	for (; k < b; k++)
+		brelse(bh[k]);
 
 read_failure:
 	ERROR("sb_bread failed reading block 0x%x\n", cur_index);
@@ -336,12 +363,16 @@ SQSH_EXTERN int squashfs_get_cached_bloc
 			msblk->block_cache[i].block = SQUASHFS_USED_BLK;
 			up(&msblk->block_cache_mutex);
 
-			if (!(msblk->block_cache[i].length =
-						squashfs_read_data(s,
-						msblk->block_cache[i].data,
-						block, 0, &next_index))) {
+			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;
 			}
 
@@ -357,7 +388,12 @@ SQSH_EXTERN int squashfs_get_cached_bloc
 			continue;
 		}
 
-		if ((bytes = msblk->block_cache[i].length - offset) >= length) {
+		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);
@@ -442,6 +478,7 @@ SQSH_EXTERN struct squashfs_fragment_cac
 {
 	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);
@@ -487,7 +524,7 @@ SQSH_EXTERN struct squashfs_fragment_cac
 
 			if (!(msblk->fragment[i].length = squashfs_read_data(s,
 						msblk->fragment[i].data,
-						start_block, length, NULL))) {
+						start_block, length, NULL, sblk->block_size))) {
 				ERROR("Unable to read fragment cache block "
 							"[%llx]\n", start_block);
 				msblk->fragment[i].locked = 0;
@@ -516,33 +553,105 @@ out:
 }
 
 
-static struct inode *squashfs_new_inode(struct super_block *s,
+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;
-	struct inode *i = new_inode(s);
+	long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)];
+	int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1);
+	squashfs_inode_t inode;
 
-	if (i) {
-		i->i_ino = inodeb->inode_number;
-		i->i_mtime.tv_sec = inodeb->mtime;
-		i->i_atime.tv_sec = inodeb->mtime;
-		i->i_ctime.tv_sec = inodeb->mtime;
-		i->i_uid = msblk->uid[inodeb->uid];
-		i->i_mode = inodeb->mode;
-		i->i_size = 0;
-		if (inodeb->guid == SQUASHFS_GUIDS)
-			i->i_gid = i->i_uid;
-		else
-			i->i_gid = msblk->guid[inodeb->guid];
+	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 struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode)
+static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode)
 {
-	struct inode *i;
+	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) +
@@ -554,7 +663,7 @@ static struct inode *squashfs_iget(struc
 	struct squashfs_base_inode_header *inodeb = &id.base,
 					  *sinodeb = &sid.base;
 
-	TRACE("Entered squashfs_iget\n");
+	TRACE("Entered squashfs_read_inode\n");
 
 	if (msblk->swap) {
 		if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
@@ -569,6 +678,8 @@ static struct inode *squashfs_iget(struc
 					&next_offset))
 			goto failed_read;
 
+	squashfs_new_inode(msblk, i, inodeb);
+
 	switch(inodeb->inode_type) {
 		case SQUASHFS_FILE_TYPE: {
 			unsigned int frag_size;
@@ -596,9 +707,6 @@ static struct inode *squashfs_iget(struc
 					inodep->fragment, &frag_blk, &frag_size))
 				goto failed_read;
 				
-			if((i = squashfs_new_inode(s, inodeb)) == NULL)
-				goto failed_read1;
-
 			i->i_nlink = 1;
 			i->i_size = inodep->file_size;
 			i->i_fop = &generic_ro_fops;
@@ -648,9 +756,6 @@ static struct inode *squashfs_iget(struc
 					inodep->fragment, &frag_blk, &frag_size))
 				goto failed_read;
 				
-			if((i = squashfs_new_inode(s, inodeb)) == NULL)
-				goto failed_read1;
-
 			i->i_nlink = inodep->nlink;
 			i->i_size = inodep->file_size;
 			i->i_fop = &generic_ro_fops;
@@ -692,9 +797,6 @@ static struct inode *squashfs_iget(struc
 						&next_offset))
 					goto failed_read;
 
-			if((i = squashfs_new_inode(s, inodeb)) == NULL)
-				goto failed_read1;
-
 			i->i_nlink = inodep->nlink;
 			i->i_size = inodep->file_size;
 			i->i_op = &squashfs_dir_inode_ops;
@@ -730,9 +832,6 @@ static struct inode *squashfs_iget(struc
 						&next_offset))
 					goto failed_read;
 
-			if((i = squashfs_new_inode(s, inodeb)) == NULL)
-				goto failed_read1;
-
 			i->i_nlink = inodep->nlink;
 			i->i_size = inodep->file_size;
 			i->i_op = &squashfs_dir_inode_ops;
@@ -774,9 +873,6 @@ static struct inode *squashfs_iget(struc
 						&next_offset))
 					goto failed_read;
 
-			if((i = squashfs_new_inode(s, inodeb)) == NULL)
-				goto failed_read1;
-
 			i->i_nlink = inodep->nlink;
 			i->i_size = inodep->symlink_size;
 			i->i_op = &page_symlink_inode_operations;
@@ -810,9 +906,6 @@ static struct inode *squashfs_iget(struc
 						&next_offset))
 					goto failed_read;
 
-			if ((i = squashfs_new_inode(s, inodeb)) == NULL)
-				goto failed_read1;
-
 			i->i_nlink = inodep->nlink;
 			i->i_mode |= (inodeb->inode_type ==
 					SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
@@ -844,9 +937,6 @@ static struct inode *squashfs_iget(struc
 						&next_offset))
 					goto failed_read;
 
-			if ((i = squashfs_new_inode(s, inodeb)) == NULL)
-				goto failed_read1;
-
 			i->i_nlink = inodep->nlink;
 			i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
 							? S_IFIFO : S_IFSOCK;
@@ -859,14 +949,50 @@ static struct inode *squashfs_iget(struc
 			goto failed_read1;
 	}
 	
-	insert_inode_hash(i);
-	return i;
+	return 1;
 
 failed_read:
 	ERROR("Unable to read inode [%llx:%x]\n", block, offset);
 
 failed_read1:
-	return NULL;
+	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;
 }
 
 
@@ -874,21 +1000,20 @@ static int read_fragment_index_table(str
 {
 	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(SQUASHFS_FRAGMENT_INDEX_BYTES
-					(sblk->fragments), GFP_KERNEL))) {
-		ERROR("Failed to allocate uid/gid table\n");
+	if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) {
+		ERROR("Failed to allocate fragment index table\n");
 		return 0;
 	}
    
-	if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) &&
-					!squashfs_read_data(s, (char *)
-					msblk->fragment_index,
-					sblk->fragment_table_start,
-					SQUASHFS_FRAGMENT_INDEX_BYTES
-					(sblk->fragments) |
-					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+	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;
 	}
@@ -897,8 +1022,7 @@ static int read_fragment_index_table(str
 		int i;
 		long long fragment;
 
-		for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments);
-									i++) {
+		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;
@@ -913,7 +1037,7 @@ static int supported_squashfs_filesystem
 {
 	struct squashfs_super_block *sblk = &msblk->sblk;
 
-	msblk->iget = squashfs_iget;
+	msblk->read_inode = squashfs_read_inode;
 	msblk->read_blocklist = read_blocklist;
 	msblk->read_fragment_index_table = read_fragment_index_table;
 
@@ -980,9 +1104,10 @@ static int squashfs_fill_super(struct su
 	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)) {
+					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) {
 		SERROR("unable to read superblock\n");
 		goto failed_mount;
 	}
@@ -1010,6 +1135,15 @@ static int squashfs_fill_super(struct su
 	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
@@ -1035,7 +1169,7 @@ static int squashfs_fill_super(struct su
 	TRACE("sblk->uid_start %llx\n", sblk->uid_start);
 
 	s->s_flags |= MS_RDONLY;
-	s->s_op = &squashfs_ops;
+	s->s_op = &squashfs_super_ops;
 
 	/* Init inode_table block pointer array */
 	if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
@@ -1079,7 +1213,7 @@ static int squashfs_fill_super(struct su
 		if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
 					((sblk->no_uids + sblk->no_guids) *
 					 sizeof(unsigned int)) |
-					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+					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;
 		}
@@ -1090,7 +1224,7 @@ static int squashfs_fill_super(struct su
 		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)) {
+					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;
 		}
@@ -1113,13 +1247,25 @@ static int squashfs_fill_super(struct su
 
 	msblk->next_fragment = 0;
 
-	/* Allocate fragment index table */
+	/* Allocate and read fragment index table */
 	if (msblk->read_fragment_index_table(s) == 0)
 		goto failed_mount;
 
+	if(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:
-	if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL)
+	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");
@@ -1131,6 +1277,7 @@ allocate_root:
 	return 0;
 
 failed_mount:
+	kfree(msblk->inode_lookup_table);
 	kfree(msblk->fragment_index);
 	kfree(msblk->fragment);
 	kfree(msblk->uid);
@@ -1148,9 +1295,9 @@ failure:
 }
 
 
-static int squashfs_statfs(struct dentry *s, struct kstatfs *buf)
+static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
-	struct squashfs_sb_info *msblk = s->d_sb->s_fs_info;
+	struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
 	struct squashfs_super_block *sblk = &msblk->sblk;
 
 	TRACE("Entered squashfs_statfs\n");
@@ -1376,6 +1523,8 @@ static int get_meta_index(struct inode *
 							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];
@@ -1516,7 +1665,7 @@ static int squashfs_readpage(struct file
 		down(&msblk->read_page_mutex);
 		
 		if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
-					block, bsize, NULL))) {
+					block, bsize, NULL, sblk->block_size))) {
 			ERROR("Unable to read page, block %llx, size %x\n", block,
 					bsize);
 			up(&msblk->read_page_mutex);
@@ -1545,43 +1694,31 @@ static int squashfs_readpage(struct file
 	for (i = start_index; i <= end_index && byte_offset < bytes;
 					i++, byte_offset += PAGE_CACHE_SIZE) {
 		struct page *push_page;
-		int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
+		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, available_bytes);
+					bytes, i, byte_offset, avail);
 
-		if (i == page->index)  {
-			pageaddr = kmap_atomic(page, KM_USER0);
-			memcpy(pageaddr, data_ptr + byte_offset,
-					available_bytes);
-			memset(pageaddr + available_bytes, 0,
-					PAGE_CACHE_SIZE - available_bytes);
-			kunmap_atomic(pageaddr, KM_USER0);
-			flush_dcache_page(page);
-			SetPageUptodate(page);
-			unlock_page(page);
-		} else {
-			push_page = grab_cache_page_nowait(page->mapping, i);
-			if (!push_page)
-				continue;
-			if (PageUptodate(push_page)) {
-				unlock_page(push_page);
-				page_cache_release(push_page);
-				continue;
-			}
- 			pageaddr = kmap_atomic(push_page, KM_USER0);
+		push_page = (i == page->index) ? page :
+			grab_cache_page_nowait(page->mapping, i);
 
-			memcpy(pageaddr, data_ptr + byte_offset,
-					available_bytes);
-			memset(pageaddr + available_bytes, 0,
-					PAGE_CACHE_SIZE - available_bytes);
-			kunmap_atomic(pageaddr, KM_USER0);
-			flush_dcache_page(push_page);
-			SetPageUptodate(push_page);
-			unlock_page(push_page);
+		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
@@ -1623,15 +1760,12 @@ static int squashfs_readpage4K(struct fi
 
 	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
 					PAGE_CACHE_SHIFT)) {
-		pageaddr = kmap_atomic(page, KM_USER0);
 		block_list = NULL;
 		goto skip_read;
 	}
 
 	if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
 		ERROR("Failed to allocate block_list\n");
-		pageaddr = kmap_atomic(page, KM_USER0);
-		block_list = NULL;
 		goto skip_read;
 	}
 
@@ -1640,14 +1774,17 @@ static int squashfs_readpage4K(struct fi
 					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);
-		pageaddr = kmap_atomic(page, KM_USER0);
-		if (bytes)
+					bsize, NULL, sblk->block_size);
+		if (bytes) {
+			pageaddr = kmap_atomic(page, KM_USER0);
 			memcpy(pageaddr, msblk->read_page, bytes);
-		else
+			kunmap_atomic(pageaddr, KM_USER0);
+		} else
 			ERROR("Unable to read page, block %llx, size %x\n",
 					block, bsize);
 		up(&msblk->read_page_mutex);
@@ -1657,11 +1794,12 @@ static int squashfs_readpage4K(struct fi
 					SQUASHFS_I(inode)->
 					u.s1.fragment_start_block,
 					SQUASHFS_I(inode)-> u.s1.fragment_size);
-		pageaddr = kmap_atomic(page, KM_USER0);
 		if (fragment) {
 			bytes = i_size_read(inode) & (sblk->block_size - 1);
+			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",
@@ -1671,6 +1809,7 @@ static int squashfs_readpage4K(struct fi
 	}
 
 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);
@@ -1925,6 +2064,7 @@ finish:
 failed_read:
 	ERROR("Unable to read directory block [%llx:%x]\n", next_block,
 		next_offset);
+	kfree(dire);
 	return 0;
 }
 
@@ -1949,11 +2089,11 @@ static struct dentry *squashfs_lookup(st
 	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;
+		goto exit_lookup;
 	}
 
 	if (len > SQUASHFS_NAME_LEN)
-		goto exit_loop;
+		goto exit_lookup;
 
 	length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
 				SQUASHFS_I(i)->u.s2.directory_index_start,
@@ -2011,35 +2151,35 @@ static struct dentry *squashfs_lookup(st
 			length += dire->size + 1;
 
 			if (name[0] < dire->name[0])
-				goto exit_loop;
+				goto exit_lookup;
 
-			if ((len == dire->size + 1) && !strncmp(name,
-						dire->name, len)) {
-				squashfs_inode_t ino =
-					SQUASHFS_MKINODE(dirh.start_block,
-					dire->offset);
+			if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) {
+				squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block,
+								dire->offset);
 
 				TRACE("calling squashfs_iget for directory "
 					"entry %s, inode %x:%x, %d\n", name,
 					dirh.start_block, dire->offset,
 					dirh.inode_number + dire->inode_number);
 
-				inode = (msblk->iget)(i->i_sb, ino);
+				inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number);
 
-				goto exit_loop;
+				goto exit_lookup;
 			}
 		}
 	}
 
-exit_loop:
+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_loop;
+	goto exit_lookup;
 }
 
 
@@ -2072,10 +2212,12 @@ static void squashfs_put_super(struct su
 }
 
 
-static struct super_block *squashfs_get_sb(struct file_system_type *fs_type,
-				int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+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);
+	return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
+				mnt);
 }
 
 
@@ -2085,7 +2227,7 @@ static int __init init_squashfs_fs(void)
 	if (err)
 		goto out;
 
-	printk(KERN_INFO "squashfs: version 3.1 (2006/08/09) "
+	printk(KERN_INFO "squashfs: version 3.2-alpha (2006/12/12) "
 		"Phillip Lougher\n");
 
 	if ((err = register_filesystem(&squashfs_fs_type)))
@@ -2146,14 +2288,12 @@ static int __init init_inodecache(void)
 
 static void destroy_inodecache(void)
 {
-	if (kmem_cache_destroy(squashfs_inode_cachep))
-		printk(KERN_INFO "squashfs_inode_cache: not all structures "
-			"were freed\n");
+	kmem_cache_destroy(squashfs_inode_cachep);
 }
 
 
 module_init(init_squashfs_fs);
 module_exit(exit_squashfs_fs);
-MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem");
+MODULE_DESCRIPTION("squashfs 3.2, a compressed read-only filesystem");
 MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>");
 MODULE_LICENSE("GPL");
diff -urp linux-2.6.18.x86_64.orig/fs/squashfs/squashfs2_0.c linux-2.6.18.x86_64/fs/squashfs/squashfs2_0.c
--- linux-2.6.18.x86_64.orig/fs/squashfs/squashfs2_0.c	2006-12-13 13:42:35.000000000 -0500
+++ linux-2.6.18.x86_64/fs/squashfs/squashfs2_0.c	2006-12-13 13:43:42.000000000 -0500
@@ -78,7 +78,7 @@ static int read_fragment_index_table_2(s
 					sblk->fragment_table_start,
 					SQUASHFS_FRAGMENT_INDEX_BYTES_2
 					(sblk->fragments) |
-					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
 		ERROR("unable to read fragment index table\n");
 		return 0;
 	}
@@ -135,42 +135,35 @@ out:
 }
 
 
-static struct inode *squashfs_new_inode(struct super_block *s,
+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_sb_info *msblk = s->s_fs_info;
 	struct squashfs_super_block *sblk = &msblk->sblk;
-	struct inode *i = new_inode(s);
-
-	if (i) {
-		i->i_ino = ino;
-		i->i_mtime.tv_sec = sblk->mkfs_time;
-		i->i_atime.tv_sec = sblk->mkfs_time;
-		i->i_ctime.tv_sec = sblk->mkfs_time;
-		i->i_uid = msblk->uid[inodeb->uid];
-		i->i_mode = inodeb->mode;
-		i->i_nlink = 1;
-		i->i_size = 0;
-		if (inodeb->guid == SQUASHFS_GUIDS)
-			i->i_gid = i->i_uid;
-		else
-			i->i_gid = msblk->guid[inodeb->guid];
-	}
 
-	return i;
+	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 struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode)
+static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
 {
-	struct inode *i;
+	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 = SQUASHFS_MK_VFS_INODE(block
-		- sblk->inode_table_start, offset);
+	unsigned int ino = i->i_ino;
 	long long next_block;
 	unsigned int next_offset;
 	union squashfs_inode_header_2 id, sid;
@@ -192,6 +185,8 @@ static struct inode *squashfs_iget_2(str
 					&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;
@@ -219,9 +214,6 @@ static struct inode *squashfs_iget_2(str
 					inodep->fragment, &frag_blk, &frag_size))
 				goto failed_read;
 				
-			if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
-				goto failed_read1;
-
 			i->i_size = inodep->file_size;
 			i->i_fop = &generic_ro_fops;
 			i->i_mode |= S_IFREG;
@@ -265,9 +257,6 @@ static struct inode *squashfs_iget_2(str
 						&next_offset))
 					goto failed_read;
 
-			if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
-				goto failed_read1;
-
 			i->i_size = inodep->file_size;
 			i->i_op = &squashfs_dir_inode_ops_2;
 			i->i_fop = &squashfs_dir_ops_2;
@@ -305,9 +294,6 @@ static struct inode *squashfs_iget_2(str
 						&next_offset))
 					goto failed_read;
 
-			if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
-				goto failed_read1;
-
 			i->i_size = inodep->file_size;
 			i->i_op = &squashfs_dir_inode_ops_2;
 			i->i_fop = &squashfs_dir_ops_2;
@@ -351,9 +337,6 @@ static struct inode *squashfs_iget_2(str
 						&next_offset))
 					goto failed_read;
 
-			if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
-				goto failed_read1;
-
 			i->i_size = inodep->symlink_size;
 			i->i_op = &page_symlink_inode_operations;
 			i->i_data.a_ops = &squashfs_symlink_aops;
@@ -386,9 +369,6 @@ static struct inode *squashfs_iget_2(str
 						&next_offset))
 					goto failed_read;
 
-			if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
-				goto failed_read1;
-
 			i->i_mode |= (inodeb->inode_type ==
 					SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
 					S_IFBLK;
@@ -402,8 +382,6 @@ static struct inode *squashfs_iget_2(str
 		 }
 		 case SQUASHFS_FIFO_TYPE:
 		 case SQUASHFS_SOCKET_TYPE: {
-			if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
-				goto failed_read1;
 
 			i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
 							? S_IFIFO : S_IFSOCK;
@@ -416,14 +394,13 @@ static struct inode *squashfs_iget_2(str
 			goto failed_read1;
 	}
 	
-	insert_inode_hash(i);
-	return i;
+	return 1;
 
 failed_read:
 	ERROR("Unable to read inode [%x:%x]\n", block, offset);
 
 failed_read1:
-	return NULL;
+	return 0;
 }
 
 
@@ -485,12 +462,18 @@ static int get_dir_index_using_name(stru
 	struct squashfs_sb_info *msblk = s->s_fs_info;
 	struct squashfs_super_block *sblk = &msblk->sblk;
 	int i, length = 0;
-	char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1];
-	struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer;
-	char str[SQUASHFS_NAME_LEN + 1];
+	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';
 
@@ -522,6 +505,8 @@ static int get_dir_index_using_name(stru
 	}
 
 	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+	kfree(str);
+failure:
 	return length;
 }
 
@@ -533,14 +518,19 @@ static int squashfs_readdir_2(struct fil
 	struct squashfs_super_block *sblk = &msblk->sblk;
 	long long next_block = SQUASHFS_I(i)->start_block +
 		sblk->directory_table_start;
-	int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0,
+	int next_offset = SQUASHFS_I(i)->offset, length = 0,
 		dir_count;
 	struct squashfs_dir_header_2 dirh;
-	char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1];
-	struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
+	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,
@@ -618,16 +608,17 @@ static int squashfs_readdir_2(struct fil
 				goto finish;
 			}
 			file->f_pos = length;
-			dirs_read++;
 		}
 	}
 
 finish:
-	return dirs_read;
+	kfree(dire);
+	return 0;
 
 failed_read:
 	ERROR("Unable to read directory block [%llx:%x]\n", next_block,
 		next_offset);
+	kfree(dire);
 	return 0;
 }
 
@@ -645,11 +636,16 @@ static struct dentry *squashfs_lookup_2(
 	int next_offset = SQUASHFS_I(i)->offset, length = 0,
 				dir_count;
 	struct squashfs_dir_header_2 dirh;
-	char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN];
-	struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
+	struct squashfs_dir_entry_2 *dire;
 	int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
 
-	TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
+	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;
@@ -717,12 +713,14 @@ static struct dentry *squashfs_lookup_2(
 				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 = (msblk->iget)(i->i_sb, ino);
+				inode = squashfs_iget(i->i_sb, ino, inode_number);
 
 				goto exit_loop;
 			}
@@ -730,6 +728,7 @@ static struct dentry *squashfs_lookup_2(
 	}
 
 exit_loop:
+	kfree(dire);
 	d_add(dentry, inode);
 	return ERR_PTR(0);
 
@@ -744,7 +743,7 @@ int squashfs_2_0_supported(struct squash
 {
 	struct squashfs_super_block *sblk = &msblk->sblk;
 
-	msblk->iget = squashfs_iget_2;
+	msblk->read_inode = squashfs_read_inode_2;
 	msblk->read_fragment_index_table = read_fragment_index_table_2;
 
 	sblk->bytes_used = sblk->bytes_used_2;
diff -urp linux-2.6.18.x86_64.orig/fs/squashfs/squashfs.h linux-2.6.18.x86_64/fs/squashfs/squashfs.h
--- linux-2.6.18.x86_64.orig/fs/squashfs/squashfs.h	2006-12-13 13:42:35.000000000 -0500
+++ linux-2.6.18.x86_64/fs/squashfs/squashfs.h	2006-12-13 13:43:43.000000000 -0500
@@ -31,7 +31,7 @@
 #define TRACE(s, args...)	{}
 #endif
 
-#define ERROR(s, args...)	printk(KERN_ERR "SQUASHFS error: "s, ## args)
+#define ERROR(s, args...)	printk(KERN_NOTICE "SQUASHFS error: "s, ## args)
 
 #define SERROR(s, args...)	do { \
 				if (!silent) \
@@ -49,7 +49,7 @@ static inline struct squashfs_inode_info
 #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);
+				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,
@@ -59,6 +59,7 @@ extern void release_cached_fragment(stru
 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;
diff -urp linux-2.6.18.x86_64.orig/include/linux/squashfs_fs.h linux-2.6.18.x86_64/include/linux/squashfs_fs.h
--- linux-2.6.18.x86_64.orig/include/linux/squashfs_fs.h	2006-12-13 13:42:37.000000000 -0500
+++ linux-2.6.18.x86_64/include/linux/squashfs_fs.h	2006-12-13 13:43:19.000000000 -0500
@@ -35,7 +35,7 @@
 #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_CACHED_FRAGMENTS	3	
 #define SQUASHFS_MAJOR			3
 #define SQUASHFS_MINOR			0
 #define SQUASHFS_MAGIC			0x73717368
@@ -72,6 +72,7 @@
 #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)
 
@@ -93,13 +94,16 @@
 #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)	(noi | (nod << 1) | (check_data << 2) \
+		duplicate_checking, exortable)	(noi | (nod << 1) | (check_data << 2) \
 		| (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
-		(duplicate_checking << 6))
+		(duplicate_checking << 6) | (exportable << 7))
 
 /* Max number of types and file types */
 #define SQUASHFS_DIR_TYPE		1
@@ -153,7 +157,7 @@
 #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_BYTES(A)	((A) * sizeof(struct squashfs_fragment_entry))
 
 #define SQUASHFS_FRAGMENT_INDEX(A)	(SQUASHFS_FRAGMENT_BYTES(A) / \
 					SQUASHFS_METADATA_SIZE)
@@ -168,6 +172,22 @@
 #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
 
@@ -235,7 +255,7 @@ struct squashfs_super_block {
 	long long		inode_table_start;
 	long long		directory_table_start;
 	long long		fragment_table_start;
-	long long		unused;
+	long long		lookup_table_start;
 } __attribute__ ((packed));
 
 struct squashfs_dir_index {
@@ -342,7 +362,7 @@ struct squashfs_dir_header {
 struct squashfs_fragment_entry {
 	long long		start_block;
 	unsigned int		size;
-	unsigned int		unused;
+	unsigned int		pending;
 } __attribute__ ((packed));
 
 extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
@@ -391,7 +411,7 @@ extern int squashfs_uncompress_exit(void
 	SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
 	SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
 	SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
-	SQUASHFS_SWAP((s)->unused, d, 888, 64);\
+	SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
 }
 
 #define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
@@ -507,6 +527,8 @@ extern int squashfs_uncompress_exit(void
 	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;\
@@ -548,6 +570,7 @@ extern int squashfs_uncompress_exit(void
 }
 
 #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
 
diff -urp linux-2.6.18.x86_64.orig/include/linux/squashfs_fs_sb.h linux-2.6.18.x86_64/include/linux/squashfs_fs_sb.h
--- linux-2.6.18.x86_64.orig/include/linux/squashfs_fs_sb.h	2006-12-13 13:42:36.000000000 -0500
+++ linux-2.6.18.x86_64/include/linux/squashfs_fs_sb.h	2006-12-13 13:43:20.000000000 -0500
@@ -65,7 +65,8 @@ struct squashfs_sb_info {
 	wait_queue_head_t	fragment_wait_queue;
 	struct meta_index	*meta_index;
 	z_stream		stream;
-	struct inode		*(*iget)(struct super_block *s,  squashfs_inode_t \
+	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, \