diff options
Diffstat (limited to 'target/linux/generic-2.6/patches-2.6.26/001-squashfs.patch')
| -rw-r--r-- | target/linux/generic-2.6/patches-2.6.26/001-squashfs.patch | 4170 | 
1 files changed, 4170 insertions, 0 deletions
diff --git a/target/linux/generic-2.6/patches-2.6.26/001-squashfs.patch b/target/linux/generic-2.6/patches-2.6.26/001-squashfs.patch new file mode 100644 index 000000000..4f55eb539 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.26/001-squashfs.patch @@ -0,0 +1,4170 @@ +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -1367,6 +1367,71 @@ +  + 	  If unsure, say N. +  ++config SQUASHFS ++	tristate "SquashFS 3.0 - Squashed file system support" ++	select ZLIB_INFLATE ++	help ++	  Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File ++	  System).  Squashfs is a highly compressed read-only filesystem for Linux. ++	  It uses zlib compression to compress both files, inodes and directories. ++	  Inodes in the system are very small and all blocks are packed to minimise ++	  data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. ++	  SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full ++	  uid/gid information, hard links and timestamps. ++ ++	  Squashfs is intended for general read-only filesystem use, for archival ++	  use (i.e. in cases where a .tar.gz file may be used), and in embedded ++	  systems where low overhead is needed.  Further information and filesystem tools ++	  are available from http://squashfs.sourceforge.net. ++ ++	  If you want to compile this as a module ( = code which can be ++	  inserted in and removed from the running kernel whenever you want), ++	  say M here and read <file:Documentation/modules.txt>.  The module ++	  will be called squashfs.  Note that the root file system (the one ++	  containing the directory /) cannot be compiled as a module. ++ ++	  If unsure, say N. ++ ++config SQUASHFS_EMBEDDED ++ ++	bool "Additional options for memory-constrained systems" ++	depends on SQUASHFS ++	default n ++	help ++	  Saying Y here allows you to specify cache sizes and how Squashfs ++	  allocates memory.  This is only intended for memory constrained ++	  systems. ++ ++	  If unsure, say N. ++ ++config SQUASHFS_FRAGMENT_CACHE_SIZE ++	int "Number of fragments cached" if SQUASHFS_EMBEDDED ++	depends on SQUASHFS ++	default "3" ++	help ++	  By default SquashFS caches the last 3 fragments read from ++	  the filesystem.  Increasing this amount may mean SquashFS ++	  has to re-read fragments less often from disk, at the expense ++	  of extra system memory.  Decreasing this amount will mean ++	  SquashFS uses less memory at the expense of extra reads from disk. ++ ++	  Note there must be at least one cached fragment.  Anything ++	  much more than three will probably not make much difference. ++ ++config SQUASHFS_VMALLOC ++	bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED ++	depends on SQUASHFS ++	default n ++	help ++	  By default SquashFS uses kmalloc to obtain fragment cache memory. ++	  Kmalloc memory is the standard kernel allocator, but it can fail ++	  on memory constrained systems.  Because of the way Vmalloc works, ++	  Vmalloc can succeed when kmalloc fails.  Specifying this option ++	  will make SquashFS always use Vmalloc to allocate the ++	  fragment cache memory. ++ ++	  If unsure, say N. ++ + config VXFS_FS + 	tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" + 	depends on BLOCK +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -73,6 +73,7 @@ + obj-$(CONFIG_JBD2)		+= jbd2/ + obj-$(CONFIG_EXT2_FS)		+= ext2/ + obj-$(CONFIG_CRAMFS)		+= cramfs/ ++obj-$(CONFIG_SQUASHFS)		+= squashfs/ + obj-y				+= ramfs/ + obj-$(CONFIG_HUGETLBFS)		+= hugetlbfs/ + obj-$(CONFIG_CODA_FS)		+= coda/ +--- /dev/null ++++ b/fs/squashfs/inode.c +@@ -0,0 +1,2122 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * inode.c ++ */ ++ ++#include <linux/types.h> ++#include <linux/squashfs_fs.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/fs.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++#include <linux/squashfs_fs_sb.h> ++#include <linux/squashfs_fs_i.h> ++#include <linux/buffer_head.h> ++#include <linux/vfs.h> ++#include <linux/init.h> ++#include <linux/dcache.h> ++#include <linux/wait.h> ++#include <linux/zlib.h> ++#include <linux/blkdev.h> ++#include <linux/vmalloc.h> ++#include <asm/uaccess.h> ++#include <asm/semaphore.h> ++ ++#include "squashfs.h" ++ ++static void squashfs_put_super(struct super_block *); ++static int squashfs_statfs(struct dentry *, struct kstatfs *); ++static int squashfs_symlink_readpage(struct file *file, struct page *page); ++static int squashfs_readpage(struct file *file, struct page *page); ++static int squashfs_readpage4K(struct file *file, struct page *page); ++static int squashfs_readdir(struct file *, void *, filldir_t); ++static struct inode *squashfs_alloc_inode(struct super_block *sb); ++static void squashfs_destroy_inode(struct inode *inode); ++static int init_inodecache(void); ++static void destroy_inodecache(void); ++static struct dentry *squashfs_lookup(struct inode *, struct dentry *, ++				struct nameidata *); ++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode); ++static long long read_blocklist(struct inode *inode, int index, ++				int readahead_blks, char *block_list, ++				unsigned short **block_p, unsigned int *bsize); ++static int squashfs_get_sb(struct file_system_type *, int, ++			const char *, void *, struct vfsmount *); ++ ++ ++static z_stream stream; ++ ++static struct file_system_type squashfs_fs_type = { ++	.owner = THIS_MODULE, ++	.name = "squashfs", ++	.get_sb = squashfs_get_sb, ++	.kill_sb = kill_block_super, ++	.fs_flags = FS_REQUIRES_DEV ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++	DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static struct super_operations squashfs_ops = { ++	.alloc_inode = squashfs_alloc_inode, ++	.destroy_inode = squashfs_destroy_inode, ++	.statfs = squashfs_statfs, ++	.put_super = squashfs_put_super, ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = { ++	.readpage = squashfs_symlink_readpage ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_aops = { ++	.readpage = squashfs_readpage ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_aops_4K = { ++	.readpage = squashfs_readpage4K ++}; ++ ++static struct file_operations squashfs_dir_ops = { ++	.read = generic_read_dir, ++	.readdir = squashfs_readdir ++}; ++ ++SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = { ++	.lookup = squashfs_lookup ++}; ++ ++ ++static struct buffer_head *get_block_length(struct super_block *s, ++				int *cur_index, int *offset, int *c_byte) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	unsigned short temp; ++	struct buffer_head *bh; ++ ++	if (!(bh = sb_bread(s, *cur_index))) ++		goto out; ++ ++	if (msblk->devblksize - *offset == 1) { ++		if (msblk->swap) ++			((unsigned char *) &temp)[1] = *((unsigned char *) ++				(bh->b_data + *offset)); ++		else ++			((unsigned char *) &temp)[0] = *((unsigned char *) ++				(bh->b_data + *offset)); ++		brelse(bh); ++		if (!(bh = sb_bread(s, ++(*cur_index)))) ++			goto out; ++		if (msblk->swap) ++			((unsigned char *) &temp)[0] = *((unsigned char *) ++				bh->b_data); ++		else ++			((unsigned char *) &temp)[1] = *((unsigned char *) ++				bh->b_data); ++		*c_byte = temp; ++		*offset = 1; ++	} else { ++		if (msblk->swap) { ++			((unsigned char *) &temp)[1] = *((unsigned char *) ++				(bh->b_data + *offset)); ++			((unsigned char *) &temp)[0] = *((unsigned char *) ++				(bh->b_data + *offset + 1)); ++		} else { ++			((unsigned char *) &temp)[0] = *((unsigned char *) ++				(bh->b_data + *offset)); ++			((unsigned char *) &temp)[1] = *((unsigned char *) ++				(bh->b_data + *offset + 1)); ++		} ++		*c_byte = temp; ++		*offset += 2; ++	} ++ ++	if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { ++		if (*offset == msblk->devblksize) { ++			brelse(bh); ++			if (!(bh = sb_bread(s, ++(*cur_index)))) ++				goto out; ++			*offset = 0; ++		} ++		if (*((unsigned char *) (bh->b_data + *offset)) != ++						SQUASHFS_MARKER_BYTE) { ++			ERROR("Metadata block marker corrupt @ %x\n", ++						*cur_index); ++			brelse(bh); ++			goto out; ++		} ++		(*offset)++; ++	} ++	return bh; ++ ++out: ++	return NULL; ++} ++ ++ ++SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++			long long index, unsigned int length, ++			long long *next_index) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> ++			msblk->devblksize_log2) + 2]; ++	unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); ++	unsigned int cur_index = index >> msblk->devblksize_log2; ++	int bytes, avail_bytes, b = 0, k; ++	char *c_buffer; ++	unsigned int compressed; ++	unsigned int c_byte = length; ++ ++	if (c_byte) { ++		bytes = msblk->devblksize - offset; ++		compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); ++		c_buffer = compressed ? msblk->read_data : buffer; ++		c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); ++ ++		TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++					? "" : "un", (unsigned int) c_byte); ++ ++		if (!(bh[0] = sb_getblk(s, cur_index))) ++			goto block_release; ++ ++		for (b = 1; bytes < c_byte; b++) { ++			if (!(bh[b] = sb_getblk(s, ++cur_index))) ++				goto block_release; ++			bytes += msblk->devblksize; ++		} ++		ll_rw_block(READ, b, bh); ++	} else { ++		if (!(bh[0] = get_block_length(s, &cur_index, &offset, ++								&c_byte))) ++			goto read_failure; ++ ++		bytes = msblk->devblksize - offset; ++		compressed = SQUASHFS_COMPRESSED(c_byte); ++		c_buffer = compressed ? msblk->read_data : buffer; ++		c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); ++ ++		TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++					? "" : "un", (unsigned int) c_byte); ++ ++		for (b = 1; bytes < c_byte; b++) { ++			if (!(bh[b] = sb_getblk(s, ++cur_index))) ++				goto block_release; ++			bytes += msblk->devblksize; ++		} ++		ll_rw_block(READ, b - 1, bh + 1); ++	} ++ ++	if (compressed) ++		down(&msblk->read_data_mutex); ++ ++	for (bytes = 0, k = 0; k < b; k++) { ++		avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? ++					msblk->devblksize - offset : ++					c_byte - bytes; ++		wait_on_buffer(bh[k]); ++		if (!buffer_uptodate(bh[k])) ++			goto block_release; ++		memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes); ++		bytes += avail_bytes; ++		offset = 0; ++		brelse(bh[k]); ++	} ++ ++	/* ++	 * uncompress block ++	 */ ++	if (compressed) { ++		int zlib_err; ++ ++		stream.next_in = c_buffer; ++		stream.avail_in = c_byte; ++		stream.next_out = buffer; ++		stream.avail_out = msblk->read_size; ++ ++		if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || ++				((zlib_err = zlib_inflate(&stream, Z_FINISH)) ++				 != Z_STREAM_END) || ((zlib_err = ++				zlib_inflateEnd(&stream)) != Z_OK)) { ++			ERROR("zlib_fs returned unexpected result 0x%x\n", ++				zlib_err); ++			bytes = 0; ++		} else ++			bytes = stream.total_out; ++ ++		up(&msblk->read_data_mutex); ++	} ++ ++	if (next_index) ++		*next_index = index + c_byte + (length ? 0 : ++				(SQUASHFS_CHECK_DATA(msblk->sblk.flags) ++				 ? 3 : 2)); ++	return bytes; ++ ++block_release: ++	while (--b >= 0) ++		brelse(bh[b]); ++ ++read_failure: ++	ERROR("sb_bread failed reading block 0x%x\n", cur_index); ++	return 0; ++} ++ ++ ++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer, ++				long long block, unsigned int offset, ++				int length, long long *next_block, ++				unsigned int *next_offset) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	int n, i, bytes, return_length = length; ++	long long next_index; ++ ++	TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); ++ ++	while ( 1 ) { ++		for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++			if (msblk->block_cache[i].block == block) ++				break; ++ ++		down(&msblk->block_cache_mutex); ++ ++		if (i == SQUASHFS_CACHED_BLKS) { ++			/* read inode header block */ ++			for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS; ++					n ; n --, i = (i + 1) % ++					SQUASHFS_CACHED_BLKS) ++				if (msblk->block_cache[i].block != ++							SQUASHFS_USED_BLK) ++					break; ++ ++			if (n == 0) { ++				wait_queue_t wait; ++ ++				init_waitqueue_entry(&wait, current); ++				add_wait_queue(&msblk->waitq, &wait); ++				set_current_state(TASK_UNINTERRUPTIBLE); ++ 				up(&msblk->block_cache_mutex); ++				schedule(); ++				set_current_state(TASK_RUNNING); ++				remove_wait_queue(&msblk->waitq, &wait); ++				continue; ++			} ++			msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; ++ ++			if (msblk->block_cache[i].block == ++							SQUASHFS_INVALID_BLK) { ++				if (!(msblk->block_cache[i].data = ++						kmalloc(SQUASHFS_METADATA_SIZE, ++						GFP_KERNEL))) { ++					ERROR("Failed to allocate cache" ++							"block\n"); ++					up(&msblk->block_cache_mutex); ++					goto out; ++				} ++			} ++ ++			msblk->block_cache[i].block = SQUASHFS_USED_BLK; ++			up(&msblk->block_cache_mutex); ++ ++			if (!(msblk->block_cache[i].length = ++						squashfs_read_data(s, ++						msblk->block_cache[i].data, ++						block, 0, &next_index))) { ++				ERROR("Unable to read cache block [%llx:%x]\n", ++						block, offset); ++				goto out; ++			} ++ ++			down(&msblk->block_cache_mutex); ++			wake_up(&msblk->waitq); ++			msblk->block_cache[i].block = block; ++			msblk->block_cache[i].next_index = next_index; ++			TRACE("Read cache block [%llx:%x]\n", block, offset); ++		} ++ ++		if (msblk->block_cache[i].block != block) { ++			up(&msblk->block_cache_mutex); ++			continue; ++		} ++ ++		if ((bytes = msblk->block_cache[i].length - offset) >= length) { ++			if (buffer) ++				memcpy(buffer, msblk->block_cache[i].data + ++						offset, length); ++			if (msblk->block_cache[i].length - offset == length) { ++				*next_block = msblk->block_cache[i].next_index; ++				*next_offset = 0; ++			} else { ++				*next_block = block; ++				*next_offset = offset + length; ++			} ++			up(&msblk->block_cache_mutex); ++			goto finish; ++		} else { ++			if (buffer) { ++				memcpy(buffer, msblk->block_cache[i].data + ++						offset, bytes); ++				buffer += bytes; ++			} ++			block = msblk->block_cache[i].next_index; ++			up(&msblk->block_cache_mutex); ++			length -= bytes; ++			offset = 0; ++		} ++	} ++ ++finish: ++	return return_length; ++out: ++	return 0; ++} ++ ++ ++static int get_fragment_location(struct super_block *s, unsigned int fragment, ++				long long *fragment_start_block, ++				unsigned int *fragment_size) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	long long start_block = ++		msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; ++	int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); ++	struct squashfs_fragment_entry fragment_entry; ++ ++	if (msblk->swap) { ++		struct squashfs_fragment_entry sfragment_entry; ++ ++		if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++					start_block, offset, ++					sizeof(sfragment_entry), &start_block, ++					&offset)) ++			goto out; ++		SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); ++	} else ++		if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++					start_block, offset, ++					sizeof(fragment_entry), &start_block, ++					&offset)) ++			goto out; ++ ++	*fragment_start_block = fragment_entry.start_block; ++	*fragment_size = fragment_entry.size; ++ ++	return 1; ++ ++out: ++	return 0; ++} ++ ++ ++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++					squashfs_fragment_cache *fragment) ++{ ++	down(&msblk->fragment_mutex); ++	fragment->locked --; ++	wake_up(&msblk->fragment_wait_queue); ++	up(&msblk->fragment_mutex); ++} ++ ++ ++SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++					*s, long long start_block, ++					int length) ++{ ++	int i, n; ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++ ++	while ( 1 ) { ++		down(&msblk->fragment_mutex); ++ ++		for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && ++				msblk->fragment[i].block != start_block; i++); ++ ++		if (i == SQUASHFS_CACHED_FRAGMENTS) { ++			for (i = msblk->next_fragment, n = ++				SQUASHFS_CACHED_FRAGMENTS; n && ++				msblk->fragment[i].locked; n--, i = (i + 1) % ++				SQUASHFS_CACHED_FRAGMENTS); ++ ++			if (n == 0) { ++				wait_queue_t wait; ++ ++				init_waitqueue_entry(&wait, current); ++				add_wait_queue(&msblk->fragment_wait_queue, ++									&wait); ++				set_current_state(TASK_UNINTERRUPTIBLE); ++				up(&msblk->fragment_mutex); ++				schedule(); ++				set_current_state(TASK_RUNNING); ++				remove_wait_queue(&msblk->fragment_wait_queue, ++									&wait); ++				continue; ++			} ++			msblk->next_fragment = (msblk->next_fragment + 1) % ++				SQUASHFS_CACHED_FRAGMENTS; ++ ++			if (msblk->fragment[i].data == NULL) ++				if (!(msblk->fragment[i].data = SQUASHFS_ALLOC ++						(SQUASHFS_FILE_MAX_SIZE))) { ++					ERROR("Failed to allocate fragment " ++							"cache block\n"); ++					up(&msblk->fragment_mutex); ++					goto out; ++				} ++ ++			msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++			msblk->fragment[i].locked = 1; ++			up(&msblk->fragment_mutex); ++ ++			if (!(msblk->fragment[i].length = squashfs_read_data(s, ++						msblk->fragment[i].data, ++						start_block, length, NULL))) { ++				ERROR("Unable to read fragment cache block " ++							"[%llx]\n", start_block); ++				msblk->fragment[i].locked = 0; ++				goto out; ++			} ++ ++			msblk->fragment[i].block = start_block; ++			TRACE("New fragment %d, start block %lld, locked %d\n", ++						i, msblk->fragment[i].block, ++						msblk->fragment[i].locked); ++			break; ++		} ++ ++		msblk->fragment[i].locked++; ++		up(&msblk->fragment_mutex); ++		TRACE("Got fragment %d, start block %lld, locked %d\n", i, ++						msblk->fragment[i].block, ++						msblk->fragment[i].locked); ++		break; ++	} ++ ++	return &msblk->fragment[i]; ++ ++out: ++	return NULL; ++} ++ ++ ++static struct inode *squashfs_new_inode(struct super_block *s, ++		struct squashfs_base_inode_header *inodeb) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct inode *i = new_inode(s); ++ ++	if (i) { ++		i->i_ino = inodeb->inode_number; ++		i->i_mtime.tv_sec = inodeb->mtime; ++		i->i_atime.tv_sec = inodeb->mtime; ++		i->i_ctime.tv_sec = inodeb->mtime; ++		i->i_uid = msblk->uid[inodeb->uid]; ++		i->i_mode = inodeb->mode; ++		i->i_size = 0; ++		if (inodeb->guid == SQUASHFS_GUIDS) ++			i->i_gid = i->i_uid; ++		else ++			i->i_gid = msblk->guid[inodeb->guid]; ++	} ++ ++	return i; ++} ++ ++ ++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode) ++{ ++	struct inode *i; ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	long long block = SQUASHFS_INODE_BLK(inode) + ++		sblk->inode_table_start; ++	unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++	long long next_block; ++	unsigned int next_offset; ++	union squashfs_inode_header id, sid; ++	struct squashfs_base_inode_header *inodeb = &id.base, ++					  *sinodeb = &sid.base; ++ ++	TRACE("Entered squashfs_iget\n"); ++ ++	if (msblk->swap) { ++		if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++					offset, sizeof(*sinodeb), &next_block, ++					&next_offset)) ++			goto failed_read; ++		SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, ++					sizeof(*sinodeb)); ++	} else ++		if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++					offset, sizeof(*inodeb), &next_block, ++					&next_offset)) ++			goto failed_read; ++ ++	switch(inodeb->inode_type) { ++		case SQUASHFS_FILE_TYPE: { ++			unsigned int frag_size; ++			long long frag_blk; ++			struct squashfs_reg_inode_header *inodep = &id.reg; ++			struct squashfs_reg_inode_header *sinodep = &sid.reg; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			frag_blk = SQUASHFS_INVALID_BLK; ++			if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++					!get_fragment_location(s, ++					inodep->fragment, &frag_blk, &frag_size)) ++				goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb)) == NULL) ++				goto failed_read1; ++ ++			i->i_nlink = 1; ++			i->i_size = inodep->file_size; ++			i->i_fop = &generic_ro_fops; ++			i->i_mode |= S_IFREG; ++			i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++			SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++			SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++			SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++			SQUASHFS_I(i)->start_block = inodep->start_block; ++			SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++			SQUASHFS_I(i)->offset = next_offset; ++			if (sblk->block_size > 4096) ++				i->i_data.a_ops = &squashfs_aops; ++			else ++				i->i_data.a_ops = &squashfs_aops_4K; ++ ++			TRACE("File inode %x:%x, start_block %llx, " ++					"block_list_start %llx, offset %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					inodep->start_block, next_block, ++					next_offset); ++			break; ++		} ++		case SQUASHFS_LREG_TYPE: { ++			unsigned int frag_size; ++			long long frag_blk; ++			struct squashfs_lreg_inode_header *inodep = &id.lreg; ++			struct squashfs_lreg_inode_header *sinodep = &sid.lreg; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			frag_blk = SQUASHFS_INVALID_BLK; ++			if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++					!get_fragment_location(s, ++					inodep->fragment, &frag_blk, &frag_size)) ++				goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb)) == NULL) ++				goto failed_read1; ++ ++			i->i_nlink = inodep->nlink; ++			i->i_size = inodep->file_size; ++			i->i_fop = &generic_ro_fops; ++			i->i_mode |= S_IFREG; ++			i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++			SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++			SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++			SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++			SQUASHFS_I(i)->start_block = inodep->start_block; ++			SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++			SQUASHFS_I(i)->offset = next_offset; ++			if (sblk->block_size > 4096) ++				i->i_data.a_ops = &squashfs_aops; ++			else ++				i->i_data.a_ops = &squashfs_aops_4K; ++ ++			TRACE("File inode %x:%x, start_block %llx, " ++					"block_list_start %llx, offset %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					inodep->start_block, next_block, ++					next_offset); ++			break; ++		} ++		case SQUASHFS_DIR_TYPE: { ++			struct squashfs_dir_inode_header *inodep = &id.dir; ++			struct squashfs_dir_inode_header *sinodep = &sid.dir; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb)) == NULL) ++				goto failed_read1; ++ ++			i->i_nlink = inodep->nlink; ++			i->i_size = inodep->file_size; ++			i->i_op = &squashfs_dir_inode_ops; ++			i->i_fop = &squashfs_dir_ops; ++			i->i_mode |= S_IFDIR; ++			SQUASHFS_I(i)->start_block = inodep->start_block; ++			SQUASHFS_I(i)->offset = inodep->offset; ++			SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++			SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++			TRACE("Directory inode %x:%x, start_block %x, offset " ++					"%x\n", SQUASHFS_INODE_BLK(inode), ++					offset, inodep->start_block, ++					inodep->offset); ++			break; ++		} ++		case SQUASHFS_LDIR_TYPE: { ++			struct squashfs_ldir_inode_header *inodep = &id.ldir; ++			struct squashfs_ldir_inode_header *sinodep = &sid.ldir; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, ++						sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb)) == NULL) ++				goto failed_read1; ++ ++			i->i_nlink = inodep->nlink; ++			i->i_size = inodep->file_size; ++			i->i_op = &squashfs_dir_inode_ops; ++			i->i_fop = &squashfs_dir_ops; ++			i->i_mode |= S_IFDIR; ++			SQUASHFS_I(i)->start_block = inodep->start_block; ++			SQUASHFS_I(i)->offset = inodep->offset; ++			SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++			SQUASHFS_I(i)->u.s2.directory_index_offset = ++								next_offset; ++			SQUASHFS_I(i)->u.s2.directory_index_count = ++								inodep->i_count; ++			SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++			TRACE("Long directory inode %x:%x, start_block %x, " ++					"offset %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					inodep->start_block, inodep->offset); ++			break; ++		} ++		case SQUASHFS_SYMLINK_TYPE: { ++			struct squashfs_symlink_inode_header *inodep = ++								&id.symlink; ++			struct squashfs_symlink_inode_header *sinodep = ++								&sid.symlink; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, ++								sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb)) == NULL) ++				goto failed_read1; ++ ++			i->i_nlink = inodep->nlink; ++			i->i_size = inodep->symlink_size; ++			i->i_op = &page_symlink_inode_operations; ++			i->i_data.a_ops = &squashfs_symlink_aops; ++			i->i_mode |= S_IFLNK; ++			SQUASHFS_I(i)->start_block = next_block; ++			SQUASHFS_I(i)->offset = next_offset; ++ ++			TRACE("Symbolic link inode %x:%x, start_block %llx, " ++					"offset %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					next_block, next_offset); ++			break; ++		 } ++		 case SQUASHFS_BLKDEV_TYPE: ++		 case SQUASHFS_CHRDEV_TYPE: { ++			struct squashfs_dev_inode_header *inodep = &id.dev; ++			struct squashfs_dev_inode_header *sinodep = &sid.dev; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if ((i = squashfs_new_inode(s, inodeb)) == NULL) ++				goto failed_read1; ++ ++			i->i_nlink = inodep->nlink; ++			i->i_mode |= (inodeb->inode_type == ++					SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR : ++					S_IFBLK; ++			init_special_inode(i, i->i_mode, ++					old_decode_dev(inodep->rdev)); ++ ++			TRACE("Device inode %x:%x, rdev %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					inodep->rdev); ++			break; ++		 } ++		 case SQUASHFS_FIFO_TYPE: ++		 case SQUASHFS_SOCKET_TYPE: { ++			struct squashfs_ipc_inode_header *inodep = &id.ipc; ++			struct squashfs_ipc_inode_header *sinodep = &sid.ipc; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if ((i = squashfs_new_inode(s, inodeb)) == NULL) ++				goto failed_read1; ++ ++			i->i_nlink = inodep->nlink; ++			i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++							? S_IFIFO : S_IFSOCK; ++			init_special_inode(i, i->i_mode, 0); ++			break; ++		 } ++		 default: ++			ERROR("Unknown inode type %d in squashfs_iget!\n", ++					inodeb->inode_type); ++			goto failed_read1; ++	} ++ ++	insert_inode_hash(i); ++	return i; ++ ++failed_read: ++	ERROR("Unable to read inode [%llx:%x]\n", block, offset); ++ ++failed_read1: ++	return NULL; ++} ++ ++ ++static int read_fragment_index_table(struct super_block *s) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++ ++	/* Allocate fragment index table */ ++	if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES ++					(sblk->fragments), GFP_KERNEL))) { ++		ERROR("Failed to allocate uid/gid table\n"); ++		return 0; ++	} ++ ++	if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) && ++					!squashfs_read_data(s, (char *) ++					msblk->fragment_index, ++					sblk->fragment_table_start, ++					SQUASHFS_FRAGMENT_INDEX_BYTES ++					(sblk->fragments) | ++					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++		ERROR("unable to read fragment index table\n"); ++		return 0; ++	} ++ ++	if (msblk->swap) { ++		int i; ++		long long fragment; ++ ++		for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); ++									i++) { ++			SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), ++						&msblk->fragment_index[i], 1); ++			msblk->fragment_index[i] = fragment; ++		} ++	} ++ ++	return 1; ++} ++ ++ ++static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) ++{ ++	struct squashfs_super_block *sblk = &msblk->sblk; ++ ++	msblk->iget = squashfs_iget; ++	msblk->read_blocklist = read_blocklist; ++	msblk->read_fragment_index_table = read_fragment_index_table; ++ ++	if (sblk->s_major == 1) { ++		if (!squashfs_1_0_supported(msblk)) { ++			SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " ++				"are unsupported\n"); ++			SERROR("Please recompile with " ++				"Squashfs 1.0 support enabled\n"); ++			return 0; ++		} ++	} else if (sblk->s_major == 2) { ++		if (!squashfs_2_0_supported(msblk)) { ++			SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " ++				"are unsupported\n"); ++			SERROR("Please recompile with " ++				"Squashfs 2.0 support enabled\n"); ++			return 0; ++		} ++	} else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > ++			SQUASHFS_MINOR) { ++		SERROR("Major/Minor mismatch, trying to mount newer %d.%d " ++				"filesystem\n", sblk->s_major, sblk->s_minor); ++		SERROR("Please update your kernel\n"); ++		return 0; ++	} ++ ++	return 1; ++} ++ ++ ++static int squashfs_fill_super(struct super_block *s, void *data, int silent) ++{ ++	struct squashfs_sb_info *msblk; ++	struct squashfs_super_block *sblk; ++	int i; ++	char b[BDEVNAME_SIZE]; ++	struct inode *root; ++ ++	TRACE("Entered squashfs_read_superblock\n"); ++ ++	if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info), ++						GFP_KERNEL))) { ++		ERROR("Failed to allocate superblock\n"); ++		goto failure; ++	} ++	memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info)); ++	msblk = s->s_fs_info; ++	sblk = &msblk->sblk; ++ ++	msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); ++	msblk->devblksize_log2 = ffz(~msblk->devblksize); ++ ++	init_MUTEX(&msblk->read_data_mutex); ++	init_MUTEX(&msblk->read_page_mutex); ++	init_MUTEX(&msblk->block_cache_mutex); ++	init_MUTEX(&msblk->fragment_mutex); ++	init_MUTEX(&msblk->meta_index_mutex); ++ ++	init_waitqueue_head(&msblk->waitq); ++	init_waitqueue_head(&msblk->fragment_wait_queue); ++ ++	if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, ++					sizeof(struct squashfs_super_block) | ++					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++		SERROR("unable to read superblock\n"); ++		goto failed_mount; ++	} ++ ++	/* Check it is a SQUASHFS superblock */ ++	msblk->swap = 0; ++	if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { ++		if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { ++			struct squashfs_super_block ssblk; ++ ++			WARNING("Mounting a different endian SQUASHFS " ++				"filesystem on %s\n", bdevname(s->s_bdev, b)); ++ ++			SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); ++			memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); ++			msblk->swap = 1; ++		} else  { ++			SERROR("Can't find a SQUASHFS superblock on %s\n", ++							bdevname(s->s_bdev, b)); ++			goto failed_mount; ++		} ++	} ++ ++	/* Check the MAJOR & MINOR versions */ ++	if(!supported_squashfs_filesystem(msblk, silent)) ++		goto failed_mount; ++ ++	TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); ++	TRACE("Inodes are %scompressed\n", ++					SQUASHFS_UNCOMPRESSED_INODES ++					(sblk->flags) ? "un" : ""); ++	TRACE("Data is %scompressed\n", ++					SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) ++					? "un" : ""); ++	TRACE("Check data is %s present in the filesystem\n", ++					SQUASHFS_CHECK_DATA(sblk->flags) ? ++					"" : "not"); ++	TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); ++	TRACE("Block size %d\n", sblk->block_size); ++	TRACE("Number of inodes %d\n", sblk->inodes); ++	if (sblk->s_major > 1) ++		TRACE("Number of fragments %d\n", sblk->fragments); ++	TRACE("Number of uids %d\n", sblk->no_uids); ++	TRACE("Number of gids %d\n", sblk->no_guids); ++	TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); ++	TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); ++	if (sblk->s_major > 1) ++		TRACE("sblk->fragment_table_start %llx\n", ++					sblk->fragment_table_start); ++	TRACE("sblk->uid_start %llx\n", sblk->uid_start); ++ ++	s->s_flags |= MS_RDONLY; ++	s->s_op = &squashfs_ops; ++ ++	/* Init inode_table block pointer array */ ++	if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * ++					SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { ++		ERROR("Failed to allocate block cache\n"); ++		goto failed_mount; ++	} ++ ++	for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++		msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; ++ ++	msblk->next_cache = 0; ++ ++	/* Allocate read_data block */ ++	msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ? ++					SQUASHFS_METADATA_SIZE : ++					sblk->block_size; ++ ++	if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) { ++		ERROR("Failed to allocate read_data block\n"); ++		goto failed_mount; ++	} ++ ++	/* Allocate read_page block */ ++	if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { ++		ERROR("Failed to allocate read_page block\n"); ++		goto failed_mount; ++	} ++ ++	/* Allocate uid and gid tables */ ++	if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * ++					sizeof(unsigned int), GFP_KERNEL))) { ++		ERROR("Failed to allocate uid/gid table\n"); ++		goto failed_mount; ++	} ++	msblk->guid = msblk->uid + sblk->no_uids; ++ ++	if (msblk->swap) { ++		unsigned int suid[sblk->no_uids + sblk->no_guids]; ++ ++		if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, ++					((sblk->no_uids + sblk->no_guids) * ++					 sizeof(unsigned int)) | ++					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++			ERROR("unable to read uid/gid table\n"); ++			goto failed_mount; ++		} ++ ++		SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids + ++			sblk->no_guids), (sizeof(unsigned int) * 8)); ++	} else ++		if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, ++					((sblk->no_uids + sblk->no_guids) * ++					 sizeof(unsigned int)) | ++					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++			ERROR("unable to read uid/gid table\n"); ++			goto failed_mount; ++		} ++ ++ ++	if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) ++		goto allocate_root; ++ ++	if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) * ++				SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { ++		ERROR("Failed to allocate fragment block cache\n"); ++		goto failed_mount; ++	} ++ ++	for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { ++		msblk->fragment[i].locked = 0; ++		msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++		msblk->fragment[i].data = NULL; ++	} ++ ++	msblk->next_fragment = 0; ++ ++	/* Allocate fragment index table */ ++	if (msblk->read_fragment_index_table(s) == 0) ++		goto failed_mount; ++ ++allocate_root: ++	if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL) ++		goto failed_mount; ++ ++	if ((s->s_root = d_alloc_root(root)) == NULL) { ++		ERROR("Root inode create failed\n"); ++		iput(root); ++		goto failed_mount; ++	} ++ ++	TRACE("Leaving squashfs_read_super\n"); ++	return 0; ++ ++failed_mount: ++	kfree(msblk->fragment_index); ++	kfree(msblk->fragment); ++	kfree(msblk->uid); ++	kfree(msblk->read_page); ++	kfree(msblk->read_data); ++	kfree(msblk->block_cache); ++	kfree(msblk->fragment_index_2); ++	kfree(s->s_fs_info); ++	s->s_fs_info = NULL; ++	return -EINVAL; ++ ++failure: ++	return -ENOMEM; ++} ++ ++ ++static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++	struct squashfs_sb_info *msblk = dentry->d_inode->i_sb->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++ ++	TRACE("Entered squashfs_statfs\n"); ++ ++	buf->f_type = SQUASHFS_MAGIC; ++	buf->f_bsize = sblk->block_size; ++	buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1; ++	buf->f_bfree = buf->f_bavail = 0; ++	buf->f_files = sblk->inodes; ++	buf->f_ffree = 0; ++	buf->f_namelen = SQUASHFS_NAME_LEN; ++ ++	return 0; ++} ++ ++ ++static int squashfs_symlink_readpage(struct file *file, struct page *page) ++{ ++	struct inode *inode = page->mapping->host; ++	int index = page->index << PAGE_CACHE_SHIFT, length, bytes; ++	long long block = SQUASHFS_I(inode)->start_block; ++	int offset = SQUASHFS_I(inode)->offset; ++	void *pageaddr = kmap(page); ++ ++	TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " ++				"%llx, offset %x\n", page->index, ++				SQUASHFS_I(inode)->start_block, ++				SQUASHFS_I(inode)->offset); ++ ++	for (length = 0; length < index; length += bytes) { ++		if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, ++				block, offset, PAGE_CACHE_SIZE, &block, ++				&offset))) { ++			ERROR("Unable to read symbolic link [%llx:%x]\n", block, ++					offset); ++			goto skip_read; ++		} ++	} ++ ++	if (length != index) { ++		ERROR("(squashfs_symlink_readpage) length != index\n"); ++		bytes = 0; ++		goto skip_read; ++	} ++ ++	bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : ++					i_size_read(inode) - length; ++ ++	if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, ++					offset, bytes, &block, &offset))) ++		ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); ++ ++skip_read: ++	memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++	kunmap(page); ++	SetPageUptodate(page); ++	unlock_page(page); ++ ++	return 0; ++} ++ ++ ++struct meta_index *locate_meta_index(struct inode *inode, int index, int offset) ++{ ++	struct meta_index *meta = NULL; ++	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++	int i; ++ ++	down(&msblk->meta_index_mutex); ++ ++	TRACE("locate_meta_index: index %d, offset %d\n", index, offset); ++ ++	if(msblk->meta_index == NULL) ++		goto not_allocated; ++ ++	for (i = 0; i < SQUASHFS_META_NUMBER; i ++) ++		if (msblk->meta_index[i].inode_number == inode->i_ino && ++				msblk->meta_index[i].offset >= offset && ++				msblk->meta_index[i].offset <= index && ++				msblk->meta_index[i].locked == 0) { ++			TRACE("locate_meta_index: entry %d, offset %d\n", i, ++					msblk->meta_index[i].offset); ++			meta = &msblk->meta_index[i]; ++			offset = meta->offset; ++		} ++ ++	if (meta) ++		meta->locked = 1; ++ ++not_allocated: ++	up(&msblk->meta_index_mutex); ++ ++	return meta; ++} ++ ++ ++struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) ++{ ++	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++	struct meta_index *meta = NULL; ++	int i; ++ ++	down(&msblk->meta_index_mutex); ++ ++	TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); ++ ++	if(msblk->meta_index == NULL) { ++		if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) * ++					SQUASHFS_META_NUMBER, GFP_KERNEL))) { ++			ERROR("Failed to allocate meta_index\n"); ++			goto failed; ++		} ++		for(i = 0; i < SQUASHFS_META_NUMBER; i++) { ++			msblk->meta_index[i].inode_number = 0; ++			msblk->meta_index[i].locked = 0; ++		} ++		msblk->next_meta_index = 0; ++	} ++ ++	for(i = SQUASHFS_META_NUMBER; i && ++			msblk->meta_index[msblk->next_meta_index].locked; i --) ++		msblk->next_meta_index = (msblk->next_meta_index + 1) % ++			SQUASHFS_META_NUMBER; ++ ++	if(i == 0) { ++		TRACE("empty_meta_index: failed!\n"); ++		goto failed; ++	} ++ ++	TRACE("empty_meta_index: returned meta entry %d, %p\n", ++			msblk->next_meta_index, ++			&msblk->meta_index[msblk->next_meta_index]); ++ ++	meta = &msblk->meta_index[msblk->next_meta_index]; ++	msblk->next_meta_index = (msblk->next_meta_index + 1) % ++			SQUASHFS_META_NUMBER; ++ ++	meta->inode_number = inode->i_ino; ++	meta->offset = offset; ++	meta->skip = skip; ++	meta->entries = 0; ++	meta->locked = 1; ++ ++failed: ++	up(&msblk->meta_index_mutex); ++	return meta; ++} ++ ++ ++void release_meta_index(struct inode *inode, struct meta_index *meta) ++{ ++	meta->locked = 0; ++} ++ ++ ++static int read_block_index(struct super_block *s, int blocks, char *block_list, ++		long long *start_block, int *offset) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	unsigned int *block_listp; ++	int block = 0; ++ ++	if (msblk->swap) { ++		char sblock_list[blocks << 2]; ++ ++		if (!squashfs_get_cached_block(s, sblock_list, *start_block, ++				*offset, blocks << 2, start_block, offset)) { ++			ERROR("Unable to read block list [%llx:%x]\n", ++				*start_block, *offset); ++			goto failure; ++		} ++		SQUASHFS_SWAP_INTS(((unsigned int *)block_list), ++				((unsigned int *)sblock_list), blocks); ++	} else ++		if (!squashfs_get_cached_block(s, block_list, *start_block, ++				*offset, blocks << 2, start_block, offset)) { ++			ERROR("Unable to read block list [%llx:%x]\n", ++				*start_block, *offset); ++			goto failure; ++		} ++ ++	for (block_listp = (unsigned int *) block_list; blocks; ++				block_listp++, blocks --) ++		block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); ++ ++	return block; ++ ++failure: ++	return -1; ++} ++ ++ ++#define SIZE 256 ++ ++static inline int calculate_skip(int blocks) { ++	int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); ++	return skip >= 7 ? 7 : skip + 1; ++} ++ ++ ++static int get_meta_index(struct inode *inode, int index, ++		long long *index_block, int *index_offset, ++		long long *data_block, char *block_list) ++{ ++	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); ++	int offset = 0; ++	struct meta_index *meta; ++	struct meta_entry *meta_entry; ++	long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; ++	int cur_offset = SQUASHFS_I(inode)->offset; ++	long long cur_data_block = SQUASHFS_I(inode)->start_block; ++	int i; ++ ++	index /= SQUASHFS_META_INDEXES * skip; ++ ++	while ( offset < index ) { ++		meta = locate_meta_index(inode, index, offset + 1); ++ ++		if (meta == NULL) { ++			if ((meta = empty_meta_index(inode, offset + 1, ++							skip)) == NULL) ++				goto all_done; ++		} else { ++			offset = index < meta->offset + meta->entries ? index : ++				meta->offset + meta->entries - 1; ++			meta_entry = &meta->meta_entry[offset - meta->offset]; ++			cur_index_block = meta_entry->index_block + sblk->inode_table_start; ++			cur_offset = meta_entry->offset; ++			cur_data_block = meta_entry->data_block; ++			TRACE("get_meta_index: offset %d, meta->offset %d, " ++				"meta->entries %d\n", offset, meta->offset, ++				meta->entries); ++			TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" ++				" data_block 0x%llx\n", cur_index_block, ++				cur_offset, cur_data_block); ++		} ++ ++		for (i = meta->offset + meta->entries; i <= index && ++				i < meta->offset + SQUASHFS_META_ENTRIES; i++) { ++			int blocks = skip * SQUASHFS_META_INDEXES; ++ ++			while (blocks) { ++				int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : ++					blocks; ++				int res = read_block_index(inode->i_sb, block, ++					block_list, &cur_index_block, ++					&cur_offset); ++ ++				if (res == -1) ++					goto failed; ++ ++				cur_data_block += res; ++				blocks -= block; ++			} ++ ++			meta_entry = &meta->meta_entry[i - meta->offset]; ++			meta_entry->index_block = cur_index_block - sblk->inode_table_start; ++			meta_entry->offset = cur_offset; ++			meta_entry->data_block = cur_data_block; ++			meta->entries ++; ++			offset ++; ++		} ++ ++		TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", ++				meta->offset, meta->entries); ++ ++		release_meta_index(inode, meta); ++	} ++ ++all_done: ++	*index_block = cur_index_block; ++	*index_offset = cur_offset; ++	*data_block = cur_data_block; ++ ++	return offset * SQUASHFS_META_INDEXES * skip; ++ ++failed: ++	release_meta_index(inode, meta); ++	return -1; ++} ++ ++ ++static long long read_blocklist(struct inode *inode, int index, ++				int readahead_blks, char *block_list, ++				unsigned short **block_p, unsigned int *bsize) ++{ ++	long long block_ptr; ++	int offset; ++	long long block; ++	int res = get_meta_index(inode, index, &block_ptr, &offset, &block, ++		block_list); ++ ++	TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" ++		       " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, ++		       block); ++ ++	if(res == -1) ++		goto failure; ++ ++	index -= res; ++ ++	while ( index ) { ++		int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; ++		int res = read_block_index(inode->i_sb, blocks, block_list, ++			&block_ptr, &offset); ++		if (res == -1) ++			goto failure; ++		block += res; ++		index -= blocks; ++	} ++ ++	if (read_block_index(inode->i_sb, 1, block_list, ++			&block_ptr, &offset) == -1) ++		goto failure; ++	*bsize = *((unsigned int *) block_list); ++ ++	return block; ++ ++failure: ++	return 0; ++} ++ ++ ++static int squashfs_readpage(struct file *file, struct page *page) ++{ ++	struct inode *inode = page->mapping->host; ++	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	unsigned char block_list[SIZE]; ++	long long block; ++	unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; ++	int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); ++ 	void *pageaddr; ++	struct squashfs_fragment_cache *fragment = NULL; ++	char *data_ptr = msblk->read_page; ++ ++	int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; ++	int start_index = page->index & ~mask; ++	int end_index = start_index | mask; ++ ++	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", ++					page->index, ++					SQUASHFS_I(inode)->start_block); ++ ++	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++					PAGE_CACHE_SHIFT)) ++		goto skip_read; ++ ++	if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++					|| index < (i_size_read(inode) >> ++					sblk->block_log)) { ++		if ((block = (msblk->read_blocklist)(inode, index, 1, ++					block_list, NULL, &bsize)) == 0) ++			goto skip_read; ++ ++		down(&msblk->read_page_mutex); ++ ++		if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, ++					block, bsize, NULL))) { ++			ERROR("Unable to read page, block %llx, size %x\n", block, ++					bsize); ++			up(&msblk->read_page_mutex); ++			goto skip_read; ++		} ++	} else { ++		if ((fragment = get_cached_fragment(inode->i_sb, ++					SQUASHFS_I(inode)-> ++					u.s1.fragment_start_block, ++					SQUASHFS_I(inode)->u.s1.fragment_size)) ++					== NULL) { ++			ERROR("Unable to read page, block %llx, size %x\n", ++					SQUASHFS_I(inode)-> ++					u.s1.fragment_start_block, ++					(int) SQUASHFS_I(inode)-> ++					u.s1.fragment_size); ++			goto skip_read; ++		} ++		bytes = SQUASHFS_I(inode)->u.s1.fragment_offset + ++					(i_size_read(inode) & (sblk->block_size ++					- 1)); ++		byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset; ++		data_ptr = fragment->data; ++	} ++ ++	for (i = start_index; i <= end_index && byte_offset < bytes; ++					i++, byte_offset += PAGE_CACHE_SIZE) { ++		struct page *push_page; ++		int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ? ++					PAGE_CACHE_SIZE : bytes - byte_offset; ++ ++		TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", ++					bytes, i, byte_offset, available_bytes); ++ ++		if (i == page->index)  { ++			pageaddr = kmap_atomic(page, KM_USER0); ++			memcpy(pageaddr, data_ptr + byte_offset, ++					available_bytes); ++			memset(pageaddr + available_bytes, 0, ++					PAGE_CACHE_SIZE - available_bytes); ++			kunmap_atomic(pageaddr, KM_USER0); ++			flush_dcache_page(page); ++			SetPageUptodate(page); ++			unlock_page(page); ++		} else if ((push_page = ++				grab_cache_page_nowait(page->mapping, i))) { ++ 			pageaddr = kmap_atomic(push_page, KM_USER0); ++ ++			memcpy(pageaddr, data_ptr + byte_offset, ++					available_bytes); ++			memset(pageaddr + available_bytes, 0, ++					PAGE_CACHE_SIZE - available_bytes); ++			kunmap_atomic(pageaddr, KM_USER0); ++			flush_dcache_page(push_page); ++			SetPageUptodate(push_page); ++			unlock_page(push_page); ++			page_cache_release(push_page); ++		} ++	} ++ ++	if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++					|| index < (i_size_read(inode) >> ++					sblk->block_log)) ++		up(&msblk->read_page_mutex); ++	else ++		release_cached_fragment(msblk, fragment); ++ ++	return 0; ++ ++skip_read: ++	pageaddr = kmap_atomic(page, KM_USER0); ++	memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++	kunmap_atomic(pageaddr, KM_USER0); ++	flush_dcache_page(page); ++	SetPageUptodate(page); ++	unlock_page(page); ++ ++	return 0; ++} ++ ++ ++static int squashfs_readpage4K(struct file *file, struct page *page) ++{ ++	struct inode *inode = page->mapping->host; ++	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	unsigned char block_list[SIZE]; ++	long long block; ++	unsigned int bsize, bytes = 0; ++ 	void *pageaddr; ++ ++	TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n", ++					page->index, ++					SQUASHFS_I(inode)->start_block); ++ ++	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++					PAGE_CACHE_SHIFT)) { ++		pageaddr = kmap_atomic(page, KM_USER0); ++		goto skip_read; ++	} ++ ++	if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++					|| page->index < (i_size_read(inode) >> ++					sblk->block_log)) { ++		block = (msblk->read_blocklist)(inode, page->index, 1, ++					block_list, NULL, &bsize); ++ ++		down(&msblk->read_page_mutex); ++		bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, ++					bsize, NULL); ++		pageaddr = kmap_atomic(page, KM_USER0); ++		if (bytes) ++			memcpy(pageaddr, msblk->read_page, bytes); ++		else ++			ERROR("Unable to read page, block %llx, size %x\n", ++					block, bsize); ++		up(&msblk->read_page_mutex); ++	} else { ++		struct squashfs_fragment_cache *fragment = ++			get_cached_fragment(inode->i_sb, ++					SQUASHFS_I(inode)-> ++					u.s1.fragment_start_block, ++					SQUASHFS_I(inode)-> u.s1.fragment_size); ++		pageaddr = kmap_atomic(page, KM_USER0); ++		if (fragment) { ++			bytes = i_size_read(inode) & (sblk->block_size - 1); ++			memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> ++					u.s1.fragment_offset, bytes); ++			release_cached_fragment(msblk, fragment); ++		} else ++			ERROR("Unable to read page, block %llx, size %x\n", ++					SQUASHFS_I(inode)-> ++					u.s1.fragment_start_block, (int) ++					SQUASHFS_I(inode)-> u.s1.fragment_size); ++	} ++ ++skip_read: ++	memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++	kunmap_atomic(pageaddr, KM_USER0); ++	flush_dcache_page(page); ++	SetPageUptodate(page); ++	unlock_page(page); ++ ++	return 0; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++				*next_block, unsigned int *next_offset, ++				long long index_start, ++				unsigned int index_offset, int i_count, ++				long long f_pos) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	int i, length = 0; ++	struct squashfs_dir_index index; ++ ++	TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++					i_count, (unsigned int) f_pos); ++ ++	f_pos =- 3; ++	if (f_pos == 0) ++		goto finish; ++ ++	for (i = 0; i < i_count; i++) { ++		if (msblk->swap) { ++			struct squashfs_dir_index sindex; ++			squashfs_get_cached_block(s, (char *) &sindex, ++					index_start, index_offset, ++					sizeof(sindex), &index_start, ++					&index_offset); ++			SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); ++		} else ++			squashfs_get_cached_block(s, (char *) &index, ++					index_start, index_offset, ++					sizeof(index), &index_start, ++					&index_offset); ++ ++		if (index.index > f_pos) ++			break; ++ ++		squashfs_get_cached_block(s, NULL, index_start, index_offset, ++					index.size + 1, &index_start, ++					&index_offset); ++ ++		length = index.index; ++		*next_block = index.start_block + sblk->directory_table_start; ++	} ++ ++	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++	return length + 3; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++				*next_block, unsigned int *next_offset, ++				long long index_start, ++				unsigned int index_offset, int i_count, ++				const char *name, int size) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	int i, length = 0; ++	char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1]; ++	struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer; ++	char str[SQUASHFS_NAME_LEN + 1]; ++ ++	TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++	strncpy(str, name, size); ++	str[size] = '\0'; ++ ++	for (i = 0; i < i_count; i++) { ++		if (msblk->swap) { ++			struct squashfs_dir_index sindex; ++			squashfs_get_cached_block(s, (char *) &sindex, ++					index_start, index_offset, ++					sizeof(sindex), &index_start, ++					&index_offset); ++			SQUASHFS_SWAP_DIR_INDEX(index, &sindex); ++		} else ++			squashfs_get_cached_block(s, (char *) index, ++					index_start, index_offset, ++					sizeof(struct squashfs_dir_index), ++					&index_start, &index_offset); ++ ++		squashfs_get_cached_block(s, index->name, index_start, ++					index_offset, index->size + 1, ++					&index_start, &index_offset); ++ ++		index->name[index->size + 1] = '\0'; ++ ++		if (strcmp(index->name, str) > 0) ++			break; ++ ++		length = index->index; ++		*next_block = index->start_block + sblk->directory_table_start; ++	} ++ ++	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++	return length + 3; ++} ++ ++ ++static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++	struct inode *i = file->f_dentry->d_inode; ++	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	long long next_block = SQUASHFS_I(i)->start_block + ++		sblk->directory_table_start; ++	int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, ++		dir_count; ++	struct squashfs_dir_header dirh; ++	char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; ++	struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; ++ ++	TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); ++ ++	while(file->f_pos < 3) { ++		char *name; ++		int size, i_ino; ++ ++		if(file->f_pos == 0) { ++			name = "."; ++			size = 1; ++			i_ino = i->i_ino; ++		} else { ++			name = ".."; ++			size = 2; ++			i_ino = SQUASHFS_I(i)->u.s2.parent_inode; ++		} ++		TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", ++				(unsigned int) dirent, name, size, (int) ++				file->f_pos, i_ino, ++				squashfs_filetype_table[1]); ++ ++		if (filldir(dirent, name, size, ++				file->f_pos, i_ino, ++				squashfs_filetype_table[1]) < 0) { ++				TRACE("Filldir returned less than 0\n"); ++				goto finish; ++		} ++		file->f_pos += size; ++		dirs_read++; ++	} ++ ++	length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++				SQUASHFS_I(i)->u.s2.directory_index_start, ++				SQUASHFS_I(i)->u.s2.directory_index_offset, ++				SQUASHFS_I(i)->u.s2.directory_index_count, ++				file->f_pos); ++ ++	while (length < i_size_read(i)) { ++		/* read directory header */ ++		if (msblk->swap) { ++			struct squashfs_dir_header sdirh; ++ ++			if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++					next_block, next_offset, sizeof(sdirh), ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += sizeof(sdirh); ++			SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++		} else { ++			if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++					next_block, next_offset, sizeof(dirh), ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += sizeof(dirh); ++		} ++ ++		dir_count = dirh.count + 1; ++		while (dir_count--) { ++			if (msblk->swap) { ++				struct squashfs_dir_entry sdire; ++				if (!squashfs_get_cached_block(i->i_sb, (char *) ++						&sdire, next_block, next_offset, ++						sizeof(sdire), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++				length += sizeof(sdire); ++				SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++			} else { ++				if (!squashfs_get_cached_block(i->i_sb, (char *) ++						dire, next_block, next_offset, ++						sizeof(*dire), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++				length += sizeof(*dire); ++			} ++ ++			if (!squashfs_get_cached_block(i->i_sb, dire->name, ++						next_block, next_offset, ++						dire->size + 1, &next_block, ++						&next_offset)) ++				goto failed_read; ++ ++			length += dire->size + 1; ++ ++			if (file->f_pos >= length) ++				continue; ++ ++			dire->name[dire->size + 1] = '\0'; ++ ++			TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", ++					(unsigned int) dirent, dire->name, ++					dire->size + 1, (int) file->f_pos, ++					dirh.start_block, dire->offset, ++					dirh.inode_number + dire->inode_number, ++					squashfs_filetype_table[dire->type]); ++ ++			if (filldir(dirent, dire->name, dire->size + 1, ++					file->f_pos, ++					dirh.inode_number + dire->inode_number, ++					squashfs_filetype_table[dire->type]) ++					< 0) { ++				TRACE("Filldir returned less than 0\n"); ++				goto finish; ++			} ++			file->f_pos = length; ++			dirs_read++; ++		} ++	} ++ ++finish: ++	return dirs_read; ++ ++failed_read: ++	ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++		next_offset); ++	return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, ++				struct nameidata *nd) ++{ ++	const unsigned char *name = dentry->d_name.name; ++	int len = dentry->d_name.len; ++	struct inode *inode = NULL; ++	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	long long next_block = SQUASHFS_I(i)->start_block + ++				sblk->directory_table_start; ++	int next_offset = SQUASHFS_I(i)->offset, length = 0, ++				dir_count; ++	struct squashfs_dir_header dirh; ++	char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN]; ++	struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; ++ ++	TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++	if (len > SQUASHFS_NAME_LEN) ++		goto exit_loop; ++ ++	length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++				SQUASHFS_I(i)->u.s2.directory_index_start, ++				SQUASHFS_I(i)->u.s2.directory_index_offset, ++				SQUASHFS_I(i)->u.s2.directory_index_count, name, ++				len); ++ ++	while (length < i_size_read(i)) { ++		/* read directory header */ ++		if (msblk->swap) { ++			struct squashfs_dir_header sdirh; ++			if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++					next_block, next_offset, sizeof(sdirh), ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += sizeof(sdirh); ++			SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++		} else { ++			if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++					next_block, next_offset, sizeof(dirh), ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += sizeof(dirh); ++		} ++ ++		dir_count = dirh.count + 1; ++		while (dir_count--) { ++			if (msblk->swap) { ++				struct squashfs_dir_entry sdire; ++				if (!squashfs_get_cached_block(i->i_sb, (char *) ++						&sdire, next_block,next_offset, ++						sizeof(sdire), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++				length += sizeof(sdire); ++				SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++			} else { ++				if (!squashfs_get_cached_block(i->i_sb, (char *) ++						dire, next_block,next_offset, ++						sizeof(*dire), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++				length += sizeof(*dire); ++			} ++ ++			if (!squashfs_get_cached_block(i->i_sb, dire->name, ++					next_block, next_offset, dire->size + 1, ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += dire->size + 1; ++ ++			if (name[0] < dire->name[0]) ++				goto exit_loop; ++ ++			if ((len == dire->size + 1) && !strncmp(name, ++						dire->name, len)) { ++				squashfs_inode_t ino = ++					SQUASHFS_MKINODE(dirh.start_block, ++					dire->offset); ++ ++				TRACE("calling squashfs_iget for directory " ++					"entry %s, inode %x:%x, %d\n", name, ++					dirh.start_block, dire->offset, ++					dirh.inode_number + dire->inode_number); ++ ++				inode = (msblk->iget)(i->i_sb, ino); ++ ++				goto exit_loop; ++			} ++		} ++	} ++ ++exit_loop: ++	d_add(dentry, inode); ++	return ERR_PTR(0); ++ ++failed_read: ++	ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++		next_offset); ++	goto exit_loop; ++} ++ ++ ++static void squashfs_put_super(struct super_block *s) ++{ ++	int i; ++ ++	if (s->s_fs_info) { ++		struct squashfs_sb_info *sbi = s->s_fs_info; ++		if (sbi->block_cache) ++			for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++				if (sbi->block_cache[i].block != ++							SQUASHFS_INVALID_BLK) ++					kfree(sbi->block_cache[i].data); ++		if (sbi->fragment) ++			for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) ++				SQUASHFS_FREE(sbi->fragment[i].data); ++		kfree(sbi->fragment); ++		kfree(sbi->block_cache); ++		kfree(sbi->read_data); ++		kfree(sbi->read_page); ++		kfree(sbi->uid); ++		kfree(sbi->fragment_index); ++		kfree(sbi->fragment_index_2); ++		kfree(sbi->meta_index); ++		kfree(s->s_fs_info); ++		s->s_fs_info = NULL; ++	} ++} ++ ++ ++static int squashfs_get_sb(struct file_system_type *fs_type, ++			int flags, const char *dev_name, void *data, ++			struct vfsmount *mnt) ++{ ++	return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, mnt); ++} ++ ++ ++static int __init init_squashfs_fs(void) ++{ ++	int err = init_inodecache(); ++	if (err) ++		goto out; ++ ++	printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " ++		"Phillip Lougher\n"); ++ ++	if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { ++		ERROR("Failed to allocate zlib workspace\n"); ++		destroy_inodecache(); ++		err = -ENOMEM; ++		goto out; ++	} ++ ++	if ((err = register_filesystem(&squashfs_fs_type))) { ++		vfree(stream.workspace); ++		destroy_inodecache(); ++	} ++ ++out: ++	return err; ++} ++ ++ ++static void __exit exit_squashfs_fs(void) ++{ ++	vfree(stream.workspace); ++	unregister_filesystem(&squashfs_fs_type); ++	destroy_inodecache(); ++} ++ ++ ++static struct kmem_cache * squashfs_inode_cachep; ++ ++ ++static struct inode *squashfs_alloc_inode(struct super_block *sb) ++{ ++	struct squashfs_inode_info *ei; ++	ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL); ++	if (!ei) ++		return NULL; ++	return &ei->vfs_inode; ++} ++ ++ ++static void squashfs_destroy_inode(struct inode *inode) ++{ ++	kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode)); ++} ++ ++ ++static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) ++{ ++	struct squashfs_inode_info *ei = foo; ++ ++	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); ++	if (squashfs_inode_cachep == NULL) ++		return -ENOMEM; ++	return 0; ++} ++ ++ ++static void destroy_inodecache(void) ++{ ++	kmem_cache_destroy(squashfs_inode_cachep); ++} ++ ++ ++module_init(init_squashfs_fs); ++module_exit(exit_squashfs_fs); ++MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem"); ++MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/fs/squashfs/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for the linux squashfs routines. ++# ++ ++obj-$(CONFIG_SQUASHFS) += squashfs.o ++squashfs-y += inode.o ++squashfs-y += squashfs2_0.o +--- /dev/null ++++ b/fs/squashfs/squashfs2_0.c +@@ -0,0 +1,758 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs2_0.c ++ */ ++ ++#include <linux/types.h> ++#include <linux/squashfs_fs.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/fs.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++#include <linux/squashfs_fs_sb.h> ++#include <linux/squashfs_fs_i.h> ++#include <linux/buffer_head.h> ++#include <linux/vfs.h> ++#include <linux/init.h> ++#include <linux/dcache.h> ++#include <linux/wait.h> ++#include <linux/zlib.h> ++#include <linux/blkdev.h> ++#include <linux/vmalloc.h> ++#include <asm/uaccess.h> ++#include <asm/semaphore.h> ++ ++#include "squashfs.h" ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir); ++static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *, ++				struct nameidata *); ++ ++static struct file_operations squashfs_dir_ops_2 = { ++	.read = generic_read_dir, ++	.readdir = squashfs_readdir_2 ++}; ++ ++static struct inode_operations squashfs_dir_inode_ops_2 = { ++	.lookup = squashfs_lookup_2 ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++	DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static int read_fragment_index_table_2(struct super_block *s) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++ ++	if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++					(sblk->fragments), GFP_KERNEL))) { ++		ERROR("Failed to allocate uid/gid table\n"); ++		return 0; ++	} ++ ++	if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) && ++					!squashfs_read_data(s, (char *) ++					msblk->fragment_index_2, ++					sblk->fragment_table_start, ++					SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++					(sblk->fragments) | ++					SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++		ERROR("unable to read fragment index table\n"); ++		return 0; ++	} ++ ++	if (msblk->swap) { ++		int i; ++		unsigned int fragment; ++ ++		for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments); ++									i++) { ++			SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment), ++						&msblk->fragment_index_2[i], 1); ++			msblk->fragment_index_2[i] = fragment; ++		} ++	} ++ ++	return 1; ++} ++ ++ ++static int get_fragment_location_2(struct super_block *s, unsigned int fragment, ++				long long *fragment_start_block, ++				unsigned int *fragment_size) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	long long start_block = ++		msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)]; ++	int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment); ++	struct squashfs_fragment_entry_2 fragment_entry; ++ ++	if (msblk->swap) { ++		struct squashfs_fragment_entry_2 sfragment_entry; ++ ++		if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++					start_block, offset, ++					sizeof(sfragment_entry), &start_block, ++					&offset)) ++			goto out; ++		SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry); ++	} else ++		if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++					start_block, offset, ++					sizeof(fragment_entry), &start_block, ++					&offset)) ++			goto out; ++ ++	*fragment_start_block = fragment_entry.start_block; ++	*fragment_size = fragment_entry.size; ++ ++	return 1; ++ ++out: ++	return 0; ++} ++ ++ ++static struct inode *squashfs_new_inode(struct super_block *s, ++		struct squashfs_base_inode_header_2 *inodeb, unsigned int ino) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	struct inode *i = new_inode(s); ++ ++	if (i) { ++		i->i_ino = ino; ++		i->i_mtime.tv_sec = sblk->mkfs_time; ++		i->i_atime.tv_sec = sblk->mkfs_time; ++		i->i_ctime.tv_sec = sblk->mkfs_time; ++		i->i_uid = msblk->uid[inodeb->uid]; ++		i->i_mode = inodeb->mode; ++		i->i_nlink = 1; ++		i->i_size = 0; ++		if (inodeb->guid == SQUASHFS_GUIDS) ++			i->i_gid = i->i_uid; ++		else ++			i->i_gid = msblk->guid[inodeb->guid]; ++	} ++ ++	return i; ++} ++ ++ ++static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode) ++{ ++	struct inode *i; ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	unsigned int block = SQUASHFS_INODE_BLK(inode) + ++		sblk->inode_table_start; ++	unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++	unsigned int ino = SQUASHFS_MK_VFS_INODE(block ++		- sblk->inode_table_start, offset); ++	long long next_block; ++	unsigned int next_offset; ++	union squashfs_inode_header_2 id, sid; ++	struct squashfs_base_inode_header_2 *inodeb = &id.base, ++					  *sinodeb = &sid.base; ++ ++	TRACE("Entered squashfs_iget\n"); ++ ++	if (msblk->swap) { ++		if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++					offset, sizeof(*sinodeb), &next_block, ++					&next_offset)) ++			goto failed_read; ++		SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb, ++					sizeof(*sinodeb)); ++	} else ++		if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++					offset, sizeof(*inodeb), &next_block, ++					&next_offset)) ++			goto failed_read; ++ ++	switch(inodeb->inode_type) { ++		case SQUASHFS_FILE_TYPE: { ++			struct squashfs_reg_inode_header_2 *inodep = &id.reg; ++			struct squashfs_reg_inode_header_2 *sinodep = &sid.reg; ++			long long frag_blk; ++			unsigned int frag_size; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			frag_blk = SQUASHFS_INVALID_BLK; ++			if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++					!get_fragment_location_2(s, ++					inodep->fragment, &frag_blk, &frag_size)) ++				goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++				goto failed_read1; ++ ++			i->i_size = inodep->file_size; ++			i->i_fop = &generic_ro_fops; ++			i->i_mode |= S_IFREG; ++			i->i_mtime.tv_sec = inodep->mtime; ++			i->i_atime.tv_sec = inodep->mtime; ++			i->i_ctime.tv_sec = inodep->mtime; ++			i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++			i->i_blksize = PAGE_CACHE_SIZE; ++			SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++			SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++			SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++			SQUASHFS_I(i)->start_block = inodep->start_block; ++			SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++			SQUASHFS_I(i)->offset = next_offset; ++			if (sblk->block_size > 4096) ++				i->i_data.a_ops = &squashfs_aops; ++			else ++				i->i_data.a_ops = &squashfs_aops_4K; ++ ++			TRACE("File inode %x:%x, start_block %x, " ++					"block_list_start %llx, offset %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					inodep->start_block, next_block, ++					next_offset); ++			break; ++		} ++		case SQUASHFS_DIR_TYPE: { ++			struct squashfs_dir_inode_header_2 *inodep = &id.dir; ++			struct squashfs_dir_inode_header_2 *sinodep = &sid.dir; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++				goto failed_read1; ++ ++			i->i_size = inodep->file_size; ++			i->i_op = &squashfs_dir_inode_ops_2; ++			i->i_fop = &squashfs_dir_ops_2; ++			i->i_mode |= S_IFDIR; ++			i->i_mtime.tv_sec = inodep->mtime; ++			i->i_atime.tv_sec = inodep->mtime; ++			i->i_ctime.tv_sec = inodep->mtime; ++			SQUASHFS_I(i)->start_block = inodep->start_block; ++			SQUASHFS_I(i)->offset = inodep->offset; ++			SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++			SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++			TRACE("Directory inode %x:%x, start_block %x, offset " ++					"%x\n", SQUASHFS_INODE_BLK(inode), ++					offset, inodep->start_block, ++					inodep->offset); ++			break; ++		} ++		case SQUASHFS_LDIR_TYPE: { ++			struct squashfs_ldir_inode_header_2 *inodep = &id.ldir; ++			struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep, ++						sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++				goto failed_read1; ++ ++			i->i_size = inodep->file_size; ++			i->i_op = &squashfs_dir_inode_ops_2; ++			i->i_fop = &squashfs_dir_ops_2; ++			i->i_mode |= S_IFDIR; ++			i->i_mtime.tv_sec = inodep->mtime; ++			i->i_atime.tv_sec = inodep->mtime; ++			i->i_ctime.tv_sec = inodep->mtime; ++			SQUASHFS_I(i)->start_block = inodep->start_block; ++			SQUASHFS_I(i)->offset = inodep->offset; ++			SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++			SQUASHFS_I(i)->u.s2.directory_index_offset = ++								next_offset; ++			SQUASHFS_I(i)->u.s2.directory_index_count = ++								inodep->i_count; ++			SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++			TRACE("Long directory inode %x:%x, start_block %x, " ++					"offset %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					inodep->start_block, inodep->offset); ++			break; ++		} ++		case SQUASHFS_SYMLINK_TYPE: { ++			struct squashfs_symlink_inode_header_2 *inodep = ++								&id.symlink; ++			struct squashfs_symlink_inode_header_2 *sinodep = ++								&sid.symlink; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, ++								sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++				goto failed_read1; ++ ++			i->i_size = inodep->symlink_size; ++			i->i_op = &page_symlink_inode_operations; ++			i->i_data.a_ops = &squashfs_symlink_aops; ++			i->i_mode |= S_IFLNK; ++			SQUASHFS_I(i)->start_block = next_block; ++			SQUASHFS_I(i)->offset = next_offset; ++ ++			TRACE("Symbolic link inode %x:%x, start_block %llx, " ++					"offset %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					next_block, next_offset); ++			break; ++		 } ++		 case SQUASHFS_BLKDEV_TYPE: ++		 case SQUASHFS_CHRDEV_TYPE: { ++			struct squashfs_dev_inode_header_2 *inodep = &id.dev; ++			struct squashfs_dev_inode_header_2 *sinodep = &sid.dev; ++ ++			if (msblk->swap) { ++				if (!squashfs_get_cached_block(s, (char *) ++						sinodep, block, offset, ++						sizeof(*sinodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++				SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep); ++			} else ++				if (!squashfs_get_cached_block(s, (char *) ++						inodep, block, offset, ++						sizeof(*inodep), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++			if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++				goto failed_read1; ++ ++			i->i_mode |= (inodeb->inode_type == ++					SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR : ++					S_IFBLK; ++			init_special_inode(i, i->i_mode, ++					old_decode_dev(inodep->rdev)); ++ ++			TRACE("Device inode %x:%x, rdev %x\n", ++					SQUASHFS_INODE_BLK(inode), offset, ++					inodep->rdev); ++			break; ++		 } ++		 case SQUASHFS_FIFO_TYPE: ++		 case SQUASHFS_SOCKET_TYPE: { ++			if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++				goto failed_read1; ++ ++			i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++							? S_IFIFO : S_IFSOCK; ++			init_special_inode(i, i->i_mode, 0); ++			break; ++		 } ++		 default: ++			ERROR("Unknown inode type %d in squashfs_iget!\n", ++					inodeb->inode_type); ++			goto failed_read1; ++	} ++ ++	insert_inode_hash(i); ++	return i; ++ ++failed_read: ++	ERROR("Unable to read inode [%x:%x]\n", block, offset); ++ ++failed_read1: ++	return NULL; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++				*next_block, unsigned int *next_offset, ++				long long index_start, ++				unsigned int index_offset, int i_count, ++				long long f_pos) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	int i, length = 0; ++	struct squashfs_dir_index_2 index; ++ ++	TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++					i_count, (unsigned int) f_pos); ++ ++	if (f_pos == 0) ++		goto finish; ++ ++	for (i = 0; i < i_count; i++) { ++		if (msblk->swap) { ++			struct squashfs_dir_index_2 sindex; ++			squashfs_get_cached_block(s, (char *) &sindex, ++					index_start, index_offset, ++					sizeof(sindex), &index_start, ++					&index_offset); ++			SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex); ++		} else ++			squashfs_get_cached_block(s, (char *) &index, ++					index_start, index_offset, ++					sizeof(index), &index_start, ++					&index_offset); ++ ++		if (index.index > f_pos) ++			break; ++ ++		squashfs_get_cached_block(s, NULL, index_start, index_offset, ++					index.size + 1, &index_start, ++					&index_offset); ++ ++		length = index.index; ++		*next_block = index.start_block + sblk->directory_table_start; ++	} ++ ++	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++	return length; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++				*next_block, unsigned int *next_offset, ++				long long index_start, ++				unsigned int index_offset, int i_count, ++				const char *name, int size) ++{ ++	struct squashfs_sb_info *msblk = s->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	int i, length = 0; ++	char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1]; ++	struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer; ++	char str[SQUASHFS_NAME_LEN + 1]; ++ ++	TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++	strncpy(str, name, size); ++	str[size] = '\0'; ++ ++	for (i = 0; i < i_count; i++) { ++		if (msblk->swap) { ++			struct squashfs_dir_index_2 sindex; ++			squashfs_get_cached_block(s, (char *) &sindex, ++					index_start, index_offset, ++					sizeof(sindex), &index_start, ++					&index_offset); ++			SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex); ++		} else ++			squashfs_get_cached_block(s, (char *) index, ++					index_start, index_offset, ++					sizeof(struct squashfs_dir_index_2), ++					&index_start, &index_offset); ++ ++		squashfs_get_cached_block(s, index->name, index_start, ++					index_offset, index->size + 1, ++					&index_start, &index_offset); ++ ++		index->name[index->size + 1] = '\0'; ++ ++		if (strcmp(index->name, str) > 0) ++			break; ++ ++		length = index->index; ++		*next_block = index->start_block + sblk->directory_table_start; ++	} ++ ++	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++	return length; ++} ++ ++ ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir) ++{ ++	struct inode *i = file->f_dentry->d_inode; ++	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	long long next_block = SQUASHFS_I(i)->start_block + ++		sblk->directory_table_start; ++	int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, ++		dir_count; ++	struct squashfs_dir_header_2 dirh; ++	char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]; ++	struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; ++ ++	TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset); ++ ++	length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++				SQUASHFS_I(i)->u.s2.directory_index_start, ++				SQUASHFS_I(i)->u.s2.directory_index_offset, ++				SQUASHFS_I(i)->u.s2.directory_index_count, ++				file->f_pos); ++ ++	while (length < i_size_read(i)) { ++		/* read directory header */ ++		if (msblk->swap) { ++			struct squashfs_dir_header_2 sdirh; ++ ++			if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++					next_block, next_offset, sizeof(sdirh), ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += sizeof(sdirh); ++			SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++		} else { ++			if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++					next_block, next_offset, sizeof(dirh), ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += sizeof(dirh); ++		} ++ ++		dir_count = dirh.count + 1; ++		while (dir_count--) { ++			if (msblk->swap) { ++				struct squashfs_dir_entry_2 sdire; ++				if (!squashfs_get_cached_block(i->i_sb, (char *) ++						&sdire, next_block, next_offset, ++						sizeof(sdire), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++				length += sizeof(sdire); ++				SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++			} else { ++				if (!squashfs_get_cached_block(i->i_sb, (char *) ++						dire, next_block, next_offset, ++						sizeof(*dire), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++				length += sizeof(*dire); ++			} ++ ++			if (!squashfs_get_cached_block(i->i_sb, dire->name, ++						next_block, next_offset, ++						dire->size + 1, &next_block, ++						&next_offset)) ++				goto failed_read; ++ ++			length += dire->size + 1; ++ ++			if (file->f_pos >= length) ++				continue; ++ ++			dire->name[dire->size + 1] = '\0'; ++ ++			TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", ++					(unsigned int) dirent, dire->name, ++					dire->size + 1, (int) file->f_pos, ++					dirh.start_block, dire->offset, ++					squashfs_filetype_table[dire->type]); ++ ++			if (filldir(dirent, dire->name, dire->size + 1, ++					file->f_pos, SQUASHFS_MK_VFS_INODE( ++					dirh.start_block, dire->offset), ++					squashfs_filetype_table[dire->type]) ++					< 0) { ++				TRACE("Filldir returned less than 0\n"); ++				goto finish; ++			} ++			file->f_pos = length; ++			dirs_read++; ++		} ++	} ++ ++finish: ++	return dirs_read; ++ ++failed_read: ++	ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++		next_offset); ++	return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry, ++				struct nameidata *nd) ++{ ++	const unsigned char *name = dentry->d_name.name; ++	int len = dentry->d_name.len; ++	struct inode *inode = NULL; ++	struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++	struct squashfs_super_block *sblk = &msblk->sblk; ++	long long next_block = SQUASHFS_I(i)->start_block + ++				sblk->directory_table_start; ++	int next_offset = SQUASHFS_I(i)->offset, length = 0, ++				dir_count; ++	struct squashfs_dir_header_2 dirh; ++	char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN]; ++	struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; ++	int sorted = sblk->s_major == 2 && sblk->s_minor >= 1; ++ ++	TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++	if (len > SQUASHFS_NAME_LEN) ++		goto exit_loop; ++ ++	length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++				SQUASHFS_I(i)->u.s2.directory_index_start, ++				SQUASHFS_I(i)->u.s2.directory_index_offset, ++				SQUASHFS_I(i)->u.s2.directory_index_count, name, ++				len); ++ ++	while (length < i_size_read(i)) { ++		/* read directory header */ ++		if (msblk->swap) { ++			struct squashfs_dir_header_2 sdirh; ++			if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++					next_block, next_offset, sizeof(sdirh), ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += sizeof(sdirh); ++			SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++		} else { ++			if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++					next_block, next_offset, sizeof(dirh), ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += sizeof(dirh); ++		} ++ ++		dir_count = dirh.count + 1; ++		while (dir_count--) { ++			if (msblk->swap) { ++				struct squashfs_dir_entry_2 sdire; ++				if (!squashfs_get_cached_block(i->i_sb, (char *) ++						&sdire, next_block,next_offset, ++						sizeof(sdire), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++				length += sizeof(sdire); ++				SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++			} else { ++				if (!squashfs_get_cached_block(i->i_sb, (char *) ++						dire, next_block,next_offset, ++						sizeof(*dire), &next_block, ++						&next_offset)) ++					goto failed_read; ++ ++				length += sizeof(*dire); ++			} ++ ++			if (!squashfs_get_cached_block(i->i_sb, dire->name, ++					next_block, next_offset, dire->size + 1, ++					&next_block, &next_offset)) ++				goto failed_read; ++ ++			length += dire->size + 1; ++ ++			if (sorted && name[0] < dire->name[0]) ++				goto exit_loop; ++ ++			if ((len == dire->size + 1) && !strncmp(name, ++						dire->name, len)) { ++				squashfs_inode_t ino = ++					SQUASHFS_MKINODE(dirh.start_block, ++					dire->offset); ++ ++				TRACE("calling squashfs_iget for directory " ++					"entry %s, inode %x:%x, %lld\n", name, ++					dirh.start_block, dire->offset, ino); ++ ++				inode = (msblk->iget)(i->i_sb, ino); ++ ++				goto exit_loop; ++			} ++		} ++	} ++ ++exit_loop: ++	d_add(dentry, inode); ++	return ERR_PTR(0); ++ ++failed_read: ++	ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++		next_offset); ++	goto exit_loop; ++} ++ ++ ++int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++	struct squashfs_super_block *sblk = &msblk->sblk; ++ ++	msblk->iget = squashfs_iget_2; ++	msblk->read_fragment_index_table = read_fragment_index_table_2; ++ ++	sblk->bytes_used = sblk->bytes_used_2; ++	sblk->uid_start = sblk->uid_start_2; ++	sblk->guid_start = sblk->guid_start_2; ++	sblk->inode_table_start = sblk->inode_table_start_2; ++	sblk->directory_table_start = sblk->directory_table_start_2; ++	sblk->fragment_table_start = sblk->fragment_table_start_2; ++ ++	return 1; ++} +--- /dev/null ++++ b/fs/squashfs/squashfs.h +@@ -0,0 +1,86 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs.h ++ */ ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#endif ++ ++#ifdef SQUASHFS_TRACE ++#define TRACE(s, args...)	printk(KERN_NOTICE "SQUASHFS: "s, ## args) ++#else ++#define TRACE(s, args...)	{} ++#endif ++ ++#define ERROR(s, args...)	printk(KERN_ERR "SQUASHFS error: "s, ## args) ++ ++#define SERROR(s, args...)	do { \ ++				if (!silent) \ ++				printk(KERN_ERR "SQUASHFS error: "s, ## args);\ ++				} while(0) ++ ++#define WARNING(s, args...)	printk(KERN_WARNING "SQUASHFS: "s, ## args) ++ ++static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode) ++{ ++	return list_entry(inode, struct squashfs_inode_info, vfs_inode); ++} ++ ++#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY) ++#define SQSH_EXTERN ++extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++				long long index, unsigned int length, ++				long long *next_index); ++extern int squashfs_get_cached_block(struct super_block *s, char *buffer, ++				long long block, unsigned int offset, ++				int length, long long *next_block, ++				unsigned int *next_offset); ++extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++					squashfs_fragment_cache *fragment); ++extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++					*s, long long start_block, ++					int length); ++extern struct address_space_operations squashfs_symlink_aops; ++extern struct address_space_operations squashfs_aops; ++extern struct address_space_operations squashfs_aops_4K; ++extern struct inode_operations squashfs_dir_inode_ops; ++#else ++#define SQSH_EXTERN static ++#endif ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk) ++{ ++	return 0; ++} ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++	return 0; ++} ++#endif +--- a/include/linux/magic.h ++++ b/include/linux/magic.h +@@ -35,6 +35,9 @@ + #define REISER2FS_SUPER_MAGIC_STRING	"ReIsEr2Fs" + #define REISER2FS_JR_SUPER_MAGIC_STRING	"ReIsEr3Fs" +  ++#define SQUASHFS_MAGIC			0x73717368 ++#define SQUASHFS_MAGIC_SWAP		0x68737173 ++ + #define SMB_SUPER_MAGIC		0x517B + #define USBDEVICE_SUPER_MAGIC	0x9fa2 + #define CGROUP_SUPER_MAGIC	0x27e0eb +--- /dev/null ++++ b/include/linux/squashfs_fs.h +@@ -0,0 +1,911 @@ ++#ifndef SQUASHFS_FS ++#define SQUASHFS_FS ++ ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs.h ++ */ ++ ++#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#define CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#endif ++ ++#ifdef	CONFIG_SQUASHFS_VMALLOC ++#define SQUASHFS_ALLOC(a)		vmalloc(a) ++#define SQUASHFS_FREE(a)		vfree(a) ++#else ++#define SQUASHFS_ALLOC(a)		kmalloc(a, GFP_KERNEL) ++#define SQUASHFS_FREE(a)		kfree(a) ++#endif ++#define SQUASHFS_CACHED_FRAGMENTS	CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE ++#define SQUASHFS_MAJOR			3 ++#define SQUASHFS_MINOR			0 ++#define SQUASHFS_START			0 ++ ++/* size of metadata (inode and directory) blocks */ ++#define SQUASHFS_METADATA_SIZE		8192 ++#define SQUASHFS_METADATA_LOG		13 ++ ++/* default size of data blocks */ ++#define SQUASHFS_FILE_SIZE		65536 ++#define SQUASHFS_FILE_LOG		16 ++ ++#define SQUASHFS_FILE_MAX_SIZE		65536 ++ ++/* Max number of uids and gids */ ++#define SQUASHFS_UIDS			256 ++#define SQUASHFS_GUIDS			255 ++ ++/* Max length of filename (not 255) */ ++#define SQUASHFS_NAME_LEN		256 ++ ++#define SQUASHFS_INVALID		((long long) 0xffffffffffff) ++#define SQUASHFS_INVALID_FRAG		((unsigned int) 0xffffffff) ++#define SQUASHFS_INVALID_BLK		((long long) -1) ++#define SQUASHFS_USED_BLK		((long long) -2) ++ ++/* Filesystem flags */ ++#define SQUASHFS_NOI			0 ++#define SQUASHFS_NOD			1 ++#define SQUASHFS_CHECK			2 ++#define SQUASHFS_NOF			3 ++#define SQUASHFS_NO_FRAG		4 ++#define SQUASHFS_ALWAYS_FRAG		5 ++#define SQUASHFS_DUPLICATE		6 ++ ++#define SQUASHFS_BIT(flag, bit)		((flag >> bit) & 1) ++ ++#define SQUASHFS_UNCOMPRESSED_INODES(flags)	SQUASHFS_BIT(flags, \ ++						SQUASHFS_NOI) ++ ++#define SQUASHFS_UNCOMPRESSED_DATA(flags)	SQUASHFS_BIT(flags, \ ++						SQUASHFS_NOD) ++ ++#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags)	SQUASHFS_BIT(flags, \ ++						SQUASHFS_NOF) ++ ++#define SQUASHFS_NO_FRAGMENTS(flags)		SQUASHFS_BIT(flags, \ ++						SQUASHFS_NO_FRAG) ++ ++#define SQUASHFS_ALWAYS_FRAGMENTS(flags)	SQUASHFS_BIT(flags, \ ++						SQUASHFS_ALWAYS_FRAG) ++ ++#define SQUASHFS_DUPLICATES(flags)		SQUASHFS_BIT(flags, \ ++						SQUASHFS_DUPLICATE) ++ ++#define SQUASHFS_CHECK_DATA(flags)		SQUASHFS_BIT(flags, \ ++						SQUASHFS_CHECK) ++ ++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \ ++		duplicate_checking)	(noi | (nod << 1) | (check_data << 2) \ ++		| (nof << 3) | (no_frag << 4) | (always_frag << 5) | \ ++		(duplicate_checking << 6)) ++ ++/* Max number of types and file types */ ++#define SQUASHFS_DIR_TYPE		1 ++#define SQUASHFS_FILE_TYPE		2 ++#define SQUASHFS_SYMLINK_TYPE		3 ++#define SQUASHFS_BLKDEV_TYPE		4 ++#define SQUASHFS_CHRDEV_TYPE		5 ++#define SQUASHFS_FIFO_TYPE		6 ++#define SQUASHFS_SOCKET_TYPE		7 ++#define SQUASHFS_LDIR_TYPE		8 ++#define SQUASHFS_LREG_TYPE		9 ++ ++/* 1.0 filesystem type definitions */ ++#define SQUASHFS_TYPES			5 ++#define SQUASHFS_IPC_TYPE		0 ++ ++/* Flag whether block is compressed or uncompressed, bit is set if block is ++ * uncompressed */ ++#define SQUASHFS_COMPRESSED_BIT		(1 << 15) ++ ++#define SQUASHFS_COMPRESSED_SIZE(B)	(((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ ++		(B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT) ++ ++#define SQUASHFS_COMPRESSED(B)		(!((B) & SQUASHFS_COMPRESSED_BIT)) ++ ++#define SQUASHFS_COMPRESSED_BIT_BLOCK		(1 << 24) ++ ++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)	(((B) & \ ++	~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \ ++	~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK) ++ ++#define SQUASHFS_COMPRESSED_BLOCK(B)	(!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) ++ ++/* ++ * Inode number ops.  Inodes consist of a compressed block number, and an ++ * uncompressed  offset within that block ++ */ ++#define SQUASHFS_INODE_BLK(a)		((unsigned int) ((a) >> 16)) ++ ++#define SQUASHFS_INODE_OFFSET(a)	((unsigned int) ((a) & 0xffff)) ++ ++#define SQUASHFS_MKINODE(A, B)		((squashfs_inode_t)(((squashfs_inode_t) (A)\ ++					<< 16) + (B))) ++ ++/* Compute 32 bit VFS inode number from squashfs inode number */ ++#define SQUASHFS_MK_VFS_INODE(a, b)	((unsigned int) (((a) << 8) + \ ++					((b) >> 2) + 1)) ++/* XXX */ ++ ++/* Translate between VFS mode and squashfs mode */ ++#define SQUASHFS_MODE(a)		((a) & 0xfff) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES(A)	(A * sizeof(struct squashfs_fragment_entry)) ++ ++#define SQUASHFS_FRAGMENT_INDEX(A)	(SQUASHFS_FRAGMENT_BYTES(A) / \ ++					SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)	(SQUASHFS_FRAGMENT_BYTES(A) % \ ++						SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES(A)	((SQUASHFS_FRAGMENT_BYTES(A) + \ ++					SQUASHFS_METADATA_SIZE - 1) / \ ++					SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)	(SQUASHFS_FRAGMENT_INDEXES(A) *\ ++						sizeof(long long)) ++ ++/* cached data constants for filesystem */ ++#define SQUASHFS_CACHED_BLKS		8 ++ ++#define SQUASHFS_MAX_FILE_SIZE_LOG	64 ++ ++#define SQUASHFS_MAX_FILE_SIZE		((long long) 1 << \ ++					(SQUASHFS_MAX_FILE_SIZE_LOG - 2)) ++ ++#define SQUASHFS_MARKER_BYTE		0xff ++ ++/* meta index cache */ ++#define SQUASHFS_META_INDEXES	(SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) ++#define SQUASHFS_META_ENTRIES	31 ++#define SQUASHFS_META_NUMBER	8 ++#define SQUASHFS_SLOTS		4 ++ ++#include <linux/magic.h> ++ ++struct meta_entry { ++	long long		data_block; ++	unsigned int		index_block; ++	unsigned short		offset; ++	unsigned short		pad; ++}; ++ ++struct meta_index { ++	unsigned int		inode_number; ++	unsigned int		offset; ++	unsigned short		entries; ++	unsigned short		skip; ++	unsigned short		locked; ++	unsigned short		pad; ++	struct meta_entry	meta_entry[SQUASHFS_META_ENTRIES]; ++}; ++ ++ ++/* ++ * definitions for structures on disk ++ */ ++ ++typedef long long		squashfs_block_t; ++typedef long long		squashfs_inode_t; ++ ++struct squashfs_super_block { ++	unsigned int		s_magic; ++	unsigned int		inodes; ++	unsigned int		bytes_used_2; ++	unsigned int		uid_start_2; ++	unsigned int		guid_start_2; ++	unsigned int		inode_table_start_2; ++	unsigned int		directory_table_start_2; ++	unsigned int		s_major:16; ++	unsigned int		s_minor:16; ++	unsigned int		block_size_1:16; ++	unsigned int		block_log:16; ++	unsigned int		flags:8; ++	unsigned int		no_uids:8; ++	unsigned int		no_guids:8; ++	unsigned int		mkfs_time /* time of filesystem creation */; ++	squashfs_inode_t	root_inode; ++	unsigned int		block_size; ++	unsigned int		fragments; ++	unsigned int		fragment_table_start_2; ++	long long		bytes_used; ++	long long		uid_start; ++	long long		guid_start; ++	long long		inode_table_start; ++	long long		directory_table_start; ++	long long		fragment_table_start; ++	long long		unused; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_index { ++	unsigned int		index; ++	unsigned int		start_block; ++	unsigned char		size; ++	unsigned char		name[0]; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_BASE_INODE_HEADER		\ ++	unsigned int		inode_type:4;	\ ++	unsigned int		mode:12;	\ ++	unsigned int		uid:8;		\ ++	unsigned int		guid:8;		\ ++	unsigned int		mtime;		\ ++	unsigned int 		inode_number; ++ ++struct squashfs_base_inode_header { ++	SQUASHFS_BASE_INODE_HEADER; ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header { ++	SQUASHFS_BASE_INODE_HEADER; ++	unsigned int		nlink; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header { ++	SQUASHFS_BASE_INODE_HEADER; ++	unsigned int		nlink; ++	unsigned short		rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header { ++	SQUASHFS_BASE_INODE_HEADER; ++	unsigned int		nlink; ++	unsigned short		symlink_size; ++	char			symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header { ++	SQUASHFS_BASE_INODE_HEADER; ++	squashfs_block_t	start_block; ++	unsigned int		fragment; ++	unsigned int		offset; ++	unsigned int		file_size; ++	unsigned short		block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_lreg_inode_header { ++	SQUASHFS_BASE_INODE_HEADER; ++	unsigned int		nlink; ++	squashfs_block_t	start_block; ++	unsigned int		fragment; ++	unsigned int		offset; ++	long long		file_size; ++	unsigned short		block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header { ++	SQUASHFS_BASE_INODE_HEADER; ++	unsigned int		nlink; ++	unsigned int		file_size:19; ++	unsigned int		offset:13; ++	unsigned int		start_block; ++	unsigned int		parent_inode; ++} __attribute__  ((packed)); ++ ++struct squashfs_ldir_inode_header { ++	SQUASHFS_BASE_INODE_HEADER; ++	unsigned int		nlink; ++	unsigned int		file_size:27; ++	unsigned int		offset:13; ++	unsigned int		start_block; ++	unsigned int		i_count:16; ++	unsigned int		parent_inode; ++	struct squashfs_dir_index	index[0]; ++} __attribute__  ((packed)); ++ ++union squashfs_inode_header { ++	struct squashfs_base_inode_header	base; ++	struct squashfs_dev_inode_header	dev; ++	struct squashfs_symlink_inode_header	symlink; ++	struct squashfs_reg_inode_header	reg; ++	struct squashfs_lreg_inode_header	lreg; ++	struct squashfs_dir_inode_header	dir; ++	struct squashfs_ldir_inode_header	ldir; ++	struct squashfs_ipc_inode_header	ipc; ++}; ++ ++struct squashfs_dir_entry { ++	unsigned int		offset:13; ++	unsigned int		type:3; ++	unsigned int		size:8; ++	int			inode_number:16; ++	char			name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_header { ++	unsigned int		count:8; ++	unsigned int		start_block; ++	unsigned int		inode_number; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry { ++	long long		start_block; ++	unsigned int		size; ++	unsigned int		unused; ++} __attribute__ ((packed)); ++ ++extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen); ++extern int squashfs_uncompress_init(void); ++extern int squashfs_uncompress_exit(void); ++ ++/* ++ * macros to convert each packed bitfield structure from little endian to big ++ * endian and vice versa.  These are needed when creating or using a filesystem ++ * on a machine with different byte ordering to the target architecture. ++ * ++ */ ++ ++#define SQUASHFS_SWAP_START \ ++	int bits;\ ++	int b_pos;\ ++	unsigned long long val;\ ++	unsigned char *s;\ ++	unsigned char *d; ++ ++#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\ ++	SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ ++	SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ ++	SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ ++	SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ ++	SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ ++	SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ ++	SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ ++	SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ ++	SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ ++	SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ ++	SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ ++	SQUASHFS_SWAP((s)->flags, d, 288, 8);\ ++	SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ ++	SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ ++	SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ ++	SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ ++	SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ ++	SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ ++	SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ ++	SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ ++	SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ ++	SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ ++	SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ ++	SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ ++	SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ ++	SQUASHFS_SWAP((s)->unused, d, 888, 64);\ ++} ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++	SQUASHFS_MEMSET(s, d, n);\ ++	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++	SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++	SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++	SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++	SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++	SQUASHFS_SWAP((s)->inode_number, d, 64, 32); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++			sizeof(struct squashfs_ipc_inode_header))\ ++	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++			sizeof(struct squashfs_dev_inode_header)); \ ++	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++	SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++			sizeof(struct squashfs_symlink_inode_header));\ ++	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++	SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++			sizeof(struct squashfs_reg_inode_header));\ ++	SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ ++	SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ ++	SQUASHFS_SWAP((s)->offset, d, 192, 32);\ ++	SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++			sizeof(struct squashfs_lreg_inode_header));\ ++	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++	SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ ++	SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ ++	SQUASHFS_SWAP((s)->offset, d, 224, 32);\ ++	SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++			sizeof(struct squashfs_dir_inode_header));\ ++	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++	SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ ++	SQUASHFS_SWAP((s)->offset, d, 147, 13);\ ++	SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ ++	SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++			sizeof(struct squashfs_ldir_inode_header));\ ++	SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++	SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ ++	SQUASHFS_SWAP((s)->offset, d, 155, 13);\ ++	SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ ++	SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ ++	SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\ ++	SQUASHFS_SWAP((s)->index, d, 0, 32);\ ++	SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ ++	SQUASHFS_SWAP((s)->size, d, 64, 8);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\ ++	SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++	SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ ++	SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\ ++	SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++	SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++	SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++	SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\ ++	SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ ++	SQUASHFS_SWAP((s)->size, d, 64, 32);\ ++} ++ ++#define SQUASHFS_SWAP_SHORTS(s, d, n) {\ ++	int entry;\ ++	int bit_position;\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, n * 2);\ ++	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++			16)\ ++		SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ ++} ++ ++#define SQUASHFS_SWAP_INTS(s, d, n) {\ ++	int entry;\ ++	int bit_position;\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, n * 4);\ ++	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++			32)\ ++		SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\ ++	int entry;\ ++	int bit_position;\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, n * 8);\ ++	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++			64)\ ++		SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ ++	int entry;\ ++	int bit_position;\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, n * bits / 8);\ ++	for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++			bits)\ ++		SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++ ++struct squashfs_base_inode_header_1 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:4; /* index into uid table */ ++	unsigned int		guid:4; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_1 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:4; /* index into uid table */ ++	unsigned int		guid:4; /* index into guid table */ ++	unsigned int		type:4; ++	unsigned int		offset:4; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_1 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:4; /* index into uid table */ ++	unsigned int		guid:4; /* index into guid table */ ++	unsigned short		rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_1 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:4; /* index into uid table */ ++	unsigned int		guid:4; /* index into guid table */ ++	unsigned short		symlink_size; ++	char			symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_1 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:4; /* index into uid table */ ++	unsigned int		guid:4; /* index into guid table */ ++	unsigned int		mtime; ++	unsigned int		start_block; ++	unsigned int		file_size:32; ++	unsigned short		block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_1 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:4; /* index into uid table */ ++	unsigned int		guid:4; /* index into guid table */ ++	unsigned int		file_size:19; ++	unsigned int		offset:13; ++	unsigned int		mtime; ++	unsigned int		start_block:24; ++} __attribute__  ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ ++	SQUASHFS_MEMSET(s, d, n);\ ++	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++	SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++	SQUASHFS_SWAP((s)->uid, d, 16, 4);\ ++	SQUASHFS_SWAP((s)->guid, d, 20, 4); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++			sizeof(struct squashfs_ipc_inode_header_1));\ ++	SQUASHFS_SWAP((s)->type, d, 24, 4);\ ++	SQUASHFS_SWAP((s)->offset, d, 28, 4);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++			sizeof(struct squashfs_dev_inode_header_1));\ ++	SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++			sizeof(struct squashfs_symlink_inode_header_1));\ ++	SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++			sizeof(struct squashfs_reg_inode_header_1));\ ++	SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ ++	SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ ++	SQUASHFS_SWAP((s)->file_size, d, 88, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++			sizeof(struct squashfs_dir_inode_header_1));\ ++	SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ ++	SQUASHFS_SWAP((s)->offset, d, 43, 13);\ ++	SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ ++	SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ ++} ++ ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++ ++struct squashfs_dir_index_2 { ++	unsigned int		index:27; ++	unsigned int		start_block:29; ++	unsigned char		size; ++	unsigned char		name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_base_inode_header_2 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:8; /* index into uid table */ ++	unsigned int		guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_2 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:8; /* index into uid table */ ++	unsigned int		guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_2 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:8; /* index into uid table */ ++	unsigned int		guid:8; /* index into guid table */ ++	unsigned short		rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_2 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:8; /* index into uid table */ ++	unsigned int		guid:8; /* index into guid table */ ++	unsigned short		symlink_size; ++	char			symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_2 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:8; /* index into uid table */ ++	unsigned int		guid:8; /* index into guid table */ ++	unsigned int		mtime; ++	unsigned int		start_block; ++	unsigned int		fragment; ++	unsigned int		offset; ++	unsigned int		file_size:32; ++	unsigned short		block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_2 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:8; /* index into uid table */ ++	unsigned int		guid:8; /* index into guid table */ ++	unsigned int		file_size:19; ++	unsigned int		offset:13; ++	unsigned int		mtime; ++	unsigned int		start_block:24; ++} __attribute__  ((packed)); ++ ++struct squashfs_ldir_inode_header_2 { ++	unsigned int		inode_type:4; ++	unsigned int		mode:12; /* protection */ ++	unsigned int		uid:8; /* index into uid table */ ++	unsigned int		guid:8; /* index into guid table */ ++	unsigned int		file_size:27; ++	unsigned int		offset:13; ++	unsigned int		mtime; ++	unsigned int		start_block:24; ++	unsigned int		i_count:16; ++	struct squashfs_dir_index_2	index[0]; ++} __attribute__  ((packed)); ++ ++union squashfs_inode_header_2 { ++	struct squashfs_base_inode_header_2	base; ++	struct squashfs_dev_inode_header_2	dev; ++	struct squashfs_symlink_inode_header_2	symlink; ++	struct squashfs_reg_inode_header_2	reg; ++	struct squashfs_dir_inode_header_2	dir; ++	struct squashfs_ldir_inode_header_2	ldir; ++	struct squashfs_ipc_inode_header_2	ipc; ++}; ++ ++struct squashfs_dir_header_2 { ++	unsigned int		count:8; ++	unsigned int		start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_entry_2 { ++	unsigned int		offset:13; ++	unsigned int		type:3; ++	unsigned int		size:8; ++	char			name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry_2 { ++	unsigned int		start_block; ++	unsigned int		size; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++	SQUASHFS_MEMSET(s, d, n);\ ++	SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++	SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++	SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++	SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ ++	SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++			sizeof(struct squashfs_dev_inode_header_2)); \ ++	SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++			sizeof(struct squashfs_symlink_inode_header_2));\ ++	SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++			sizeof(struct squashfs_reg_inode_header_2));\ ++	SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++	SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ ++	SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ ++	SQUASHFS_SWAP((s)->offset, d, 128, 32);\ ++	SQUASHFS_SWAP((s)->file_size, d, 160, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++			sizeof(struct squashfs_dir_inode_header_2));\ ++	SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ ++	SQUASHFS_SWAP((s)->offset, d, 51, 13);\ ++	SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ ++	SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++			sizeof(struct squashfs_ldir_inode_header_2));\ ++	SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ ++	SQUASHFS_SWAP((s)->offset, d, 59, 13);\ ++	SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ ++	SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ ++	SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ ++	SQUASHFS_SWAP((s)->index, d, 0, 27);\ ++	SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ ++	SQUASHFS_SWAP((s)->size, d, 56, 8);\ ++} ++#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ ++	SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++	SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ ++	SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++	SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++	SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ ++	SQUASHFS_SWAP_START\ ++	SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ ++	SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ ++	SQUASHFS_SWAP((s)->size, d, 32, 32);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES_2(A)	(A * sizeof(struct squashfs_fragment_entry_2)) ++ ++#define SQUASHFS_FRAGMENT_INDEX_2(A)	(SQUASHFS_FRAGMENT_BYTES_2(A) / \ ++					SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A)	(SQUASHFS_FRAGMENT_BYTES_2(A) % \ ++						SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES_2(A)	((SQUASHFS_FRAGMENT_BYTES_2(A) + \ ++					SQUASHFS_METADATA_SIZE - 1) / \ ++					SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A)	(SQUASHFS_FRAGMENT_INDEXES_2(A) *\ ++						sizeof(int)) ++ ++#endif ++ ++#ifdef __KERNEL__ ++ ++/* ++ * macros used to swap each structure entry, taking into account ++ * bitfields and different bitfield placing conventions on differing ++ * architectures ++ */ ++ ++#include <asm/byteorder.h> ++ ++#ifdef __BIG_ENDIAN ++	/* convert from little endian to big endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++		tbits, b_pos) ++#else ++	/* convert from big endian to little endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++		tbits, 64 - tbits - b_pos) ++#endif ++ ++#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ ++	b_pos = pos % 8;\ ++	val = 0;\ ++	s = (unsigned char *)p + (pos / 8);\ ++	d = ((unsigned char *) &val) + 7;\ ++	for(bits = 0; bits < (tbits + b_pos); bits += 8) \ ++		*d-- = *s++;\ ++	value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ ++} ++ ++#define SQUASHFS_MEMSET(s, d, n)	memset(s, 0, n); ++ ++#endif ++#endif +--- /dev/null ++++ b/include/linux/squashfs_fs_i.h +@@ -0,0 +1,45 @@ ++#ifndef SQUASHFS_FS_I ++#define SQUASHFS_FS_I ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_i.h ++ */ ++ ++struct squashfs_inode_info { ++	long long	start_block; ++	unsigned int	offset; ++	union { ++		struct { ++			long long	fragment_start_block; ++			unsigned int	fragment_size; ++			unsigned int	fragment_offset; ++			long long	block_list_start; ++		} s1; ++		struct { ++			long long	directory_index_start; ++			unsigned int	directory_index_offset; ++			unsigned int	directory_index_count; ++			unsigned int	parent_inode; ++		} s2; ++	} u; ++	struct inode	vfs_inode; ++}; ++#endif +--- /dev/null ++++ b/include/linux/squashfs_fs_sb.h +@@ -0,0 +1,74 @@ ++#ifndef SQUASHFS_FS_SB ++#define SQUASHFS_FS_SB ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_sb.h ++ */ ++ ++#include <linux/squashfs_fs.h> ++ ++struct squashfs_cache { ++	long long	block; ++	int		length; ++	long long	next_index; ++	char		*data; ++}; ++ ++struct squashfs_fragment_cache { ++	long long	block; ++	int		length; ++	unsigned int	locked; ++	char		*data; ++}; ++ ++struct squashfs_sb_info { ++	struct squashfs_super_block	sblk; ++	int			devblksize; ++	int			devblksize_log2; ++	int			swap; ++	struct squashfs_cache	*block_cache; ++	struct squashfs_fragment_cache	*fragment; ++	int			next_cache; ++	int			next_fragment; ++	int			next_meta_index; ++	unsigned int		*uid; ++	unsigned int		*guid; ++	long long		*fragment_index; ++	unsigned int		*fragment_index_2; ++	unsigned int		read_size; ++	char			*read_data; ++	char			*read_page; ++	struct semaphore	read_data_mutex; ++	struct semaphore	read_page_mutex; ++	struct semaphore	block_cache_mutex; ++	struct semaphore	fragment_mutex; ++	struct semaphore	meta_index_mutex; ++	wait_queue_head_t	waitq; ++	wait_queue_head_t	fragment_wait_queue; ++	struct meta_index	*meta_index; ++	struct inode		*(*iget)(struct super_block *s,  squashfs_inode_t \ ++				inode); ++	long long		(*read_blocklist)(struct inode *inode, int \ ++				index, int readahead_blks, char *block_list, \ ++				unsigned short **block_p, unsigned int *bsize); ++	int			(*read_fragment_index_table)(struct super_block *s); ++}; ++#endif +--- a/init/do_mounts_rd.c ++++ b/init/do_mounts_rd.c +@@ -5,6 +5,7 @@ + #include <linux/ext2_fs.h> + #include <linux/romfs_fs.h> + #include <linux/cramfs_fs.h> ++#include <linux/squashfs_fs.h> + #include <linux/initrd.h> + #include <linux/string.h> +  +@@ -39,6 +40,7 @@ +  * numbers could not be found. +  * +  * We currently check for the following magic numbers: ++ *      squashfs +  * 	minix +  * 	ext2 +  *	romfs +@@ -53,6 +55,7 @@ + 	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 @@ + 	ext2sb = (struct ext2_super_block *) buf; + 	romfsb = (struct romfs_super_block *) buf; + 	cramfsb = (struct cramfs_super *) buf; ++	squashfsb = (struct squashfs_super_block *) buf; + 	memset(buf, 0xe5, size); +  + 	/* +@@ -101,6 +105,15 @@ + 		goto done; + 	} +  ++	/* squashfs is at block zero too */ ++	if (squashfsb->s_magic == SQUASHFS_MAGIC) { ++		printk(KERN_NOTICE ++		       "RAMDISK: squashfs filesystem found at block %d\n", ++		       start_block); ++		nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; ++		goto done; ++	} ++ + 	/* + 	 * Read block 1 to test for minix and ext2 superblock + 	 */  | 
