Re: [PATCH v5 3/3] fs: add support for SquashFS 4.0

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue,  1 Mar 2016 12:10:04 +0100
yegorslists@xxxxxxxxxxxxxx wrote:

> From: Yegor Yefremov <yegorslists@xxxxxxxxxxxxxx>
> 
> The driver was imported from Linux 4.4.
> 
> Current implementation supports only XZ decompressor.
> 
> Signed-off-by: Yegor Yefremov <yegorslists@xxxxxxxxxxxxxx>

Tested-by: Antony Pavlov <antonynpavlov@xxxxxxxxx>

> ---
> Changes:
> 	v5: - simplify squashfs_read
> 
>         v4: - add promt to mount examples
>             - rework squashfs_read to enable non linear reading
>             - embed super_block into squashfs_priv
>             - check malloc' return value
> 
>         v3: - rework inode searching
>             - eliminate memleaks in directory search
> 
>         v2: - fix checkpatch errors
>             - remove spinlock/wait code from cache implementation
>             - fix directory listing
>             - add documentation entry about SquashFS
>             - fix compiling without XZ enabled
>             - fix some memory leaks
> 
>  Documentation/filesystems/squashfs.rst |  15 ++
>  fs/Kconfig                             |   1 +
>  fs/Makefile                            |   1 +
>  fs/squashfs/Kconfig                    |  32 +++
>  fs/squashfs/Makefile                   |  13 +
>  fs/squashfs/block.c                    | 212 +++++++++++++++
>  fs/squashfs/cache.c                    | 393 ++++++++++++++++++++++++++++
>  fs/squashfs/decompressor.c             | 140 ++++++++++
>  fs/squashfs/decompressor.h             |  57 +++++
>  fs/squashfs/decompressor_single.c      |  83 ++++++
>  fs/squashfs/file.c                     | 445 ++++++++++++++++++++++++++++++++
>  fs/squashfs/file_cache.c               |  35 +++
>  fs/squashfs/fragment.c                 |  94 +++++++
>  fs/squashfs/id.c                       |  98 +++++++
>  fs/squashfs/inode.c                    | 402 +++++++++++++++++++++++++++++
>  fs/squashfs/namei.c                    | 346 +++++++++++++++++++++++++
>  fs/squashfs/page_actor.c               |  99 +++++++
>  fs/squashfs/page_actor.h               |  53 ++++
>  fs/squashfs/squashfs.c                 | 368 ++++++++++++++++++++++++++
>  fs/squashfs/squashfs.h                 | 142 +++++++++++
>  fs/squashfs/squashfs_fs.h              | 453 +++++++++++++++++++++++++++++++++
>  fs/squashfs/squashfs_fs_i.h            |  52 ++++
>  fs/squashfs/squashfs_fs_sb.h           |  82 ++++++
>  fs/squashfs/super.c                    | 344 +++++++++++++++++++++++++
>  fs/squashfs/xz_wrapper.c               | 190 ++++++++++++++
>  25 files changed, 4150 insertions(+)
>  create mode 100644 Documentation/filesystems/squashfs.rst
>  create mode 100644 fs/squashfs/Kconfig
>  create mode 100644 fs/squashfs/Makefile
>  create mode 100644 fs/squashfs/block.c
>  create mode 100644 fs/squashfs/cache.c
>  create mode 100644 fs/squashfs/decompressor.c
>  create mode 100644 fs/squashfs/decompressor.h
>  create mode 100644 fs/squashfs/decompressor_single.c
>  create mode 100644 fs/squashfs/file.c
>  create mode 100644 fs/squashfs/file_cache.c
>  create mode 100644 fs/squashfs/fragment.c
>  create mode 100644 fs/squashfs/id.c
>  create mode 100644 fs/squashfs/inode.c
>  create mode 100644 fs/squashfs/namei.c
>  create mode 100644 fs/squashfs/page_actor.c
>  create mode 100644 fs/squashfs/page_actor.h
>  create mode 100644 fs/squashfs/squashfs.c
>  create mode 100644 fs/squashfs/squashfs.h
>  create mode 100644 fs/squashfs/squashfs_fs.h
>  create mode 100644 fs/squashfs/squashfs_fs_i.h
>  create mode 100644 fs/squashfs/squashfs_fs_sb.h
>  create mode 100644 fs/squashfs/super.c
>  create mode 100644 fs/squashfs/xz_wrapper.c
> 
> diff --git a/Documentation/filesystems/squashfs.rst b/Documentation/filesystems/squashfs.rst
> new file mode 100644
> index 0000000..88c97eb
> --- /dev/null
> +++ b/Documentation/filesystems/squashfs.rst
> @@ -0,0 +1,15 @@
> +.. index:: squashfs (filesystem)
> +
> +SquashFS filesystem
> +===================
> +
> +SquashFS is a highly compressed read-only filesystem for Linux.
> +It uses zlib, lzo or xz compression to compress both files, inodes
> +and directories. A SquashFS filesystem can be mounted using the
> +:ref:`command_mount` command::
> +
> +  barebox:/ mkdir /mnt
> +  barebox:/ mount -t squashfs /dev/spiflash.FileSystem /mnt
> +  barebox:/ ls /mnt
> +  zImage barebox.bin
> +  barebox:/ umount /mnt
> diff --git a/fs/Kconfig b/fs/Kconfig
> index 56fe41a..c7c42ac 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -90,6 +90,7 @@ config FS_SMHFS
>  	  Barebox
>  
>  source fs/pstore/Kconfig
> +source fs/squashfs/Kconfig
>  
>  config FS_RATP
>  	bool
> diff --git a/fs/Makefile b/fs/Makefile
> index 7896e38..f2bb702 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -15,4 +15,5 @@ obj-$(CONFIG_FS_EFI)	 += efi.o
>  obj-$(CONFIG_FS_EFIVARFS) += efivarfs.o
>  obj-$(CONFIG_FS_SMHFS) += smhfs.o
>  obj-$(CONFIG_FS_PSTORE) += pstore/
> +obj-$(CONFIG_FS_SQUASHFS) += squashfs/
>  obj-$(CONFIG_FS_RATP)	+= ratpfs.o
> diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
> new file mode 100644
> index 0000000..dc25d93
> --- /dev/null
> +++ b/fs/squashfs/Kconfig
> @@ -0,0 +1,32 @@
> +menuconfig FS_SQUASHFS
> +	bool
> +	prompt "squashfs support"
> +	help
> +	  Saying Y here includes support for SquashFS 4.0 (a Compressed
> +	  Read-Only File System).  Squashfs is a highly compressed read-only
> +	  filesystem for Linux.  It uses zlib, lzo or xz compression to
> +	  compress both files, inodes and directories.  Inodes in the system
> +	  are very small and all blocks are packed to minimise data overhead.
> +	  Block sizes greater than 4K are supported up to a maximum of 1 Mbytes
> +	  (default block size 128K).  SquashFS 4.0 supports 64 bit filesystems
> +	  and files (larger than 4GB), full uid/gid information, hard links and
> +	  timestamps.
> +
> +	  Squashfs is intended for general read-only filesystem use, for
> +	  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 tools are available from http://squashfs.sourceforge.net.
> +
> +config SQUASHFS_XZ
> +	bool "Include support for XZ compressed file systems"
> +	default y
> +	depends on FS_SQUASHFS
> +	select XZ_DECOMPRESS
> +	help
> +	  Saying Y here includes support for reading Squashfs file systems
> +	  compressed with XZ compression.  XZ gives better compression than
> +	  the default zlib compression, at the expense of greater CPU and
> +	  memory overhead.
> +
> +	  XZ is not the standard compression used in Squashfs and so most
> +	  file systems will be readable without selecting this option.
> diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
> new file mode 100644
> index 0000000..c0d024c
> --- /dev/null
> +++ b/fs/squashfs/Makefile
> @@ -0,0 +1,13 @@
> +obj-y	+= squashfs.o
> +obj-y	+= block.o
> +obj-y	+= cache.o
> +obj-y	+= decompressor.o
> +obj-y	+= decompressor_single.o
> +obj-y	+= file.o
> +obj-y	+= file_cache.o
> +obj-y	+= fragment.o
> +obj-y	+= id.o
> +obj-y	+= inode.o
> +obj-y	+= namei.o
> +obj-y	+= super.o
> +obj-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o
> diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
> new file mode 100644
> index 0000000..3e2b9a5
> --- /dev/null
> +++ b/fs/squashfs/block.c
> @@ -0,0 +1,212 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * block.c
> + */
> +
> +/*
> + * This file implements the low-level routines to read and decompress
> + * datablocks and metadata blocks.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/string.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs.h"
> +#include "decompressor.h"
> +#include "page_actor.h"
> +
> +/*
> + * Read the metadata block length, this is stored in the first two
> + * bytes of the metadata block.
> + */
> +static char *get_block_length(struct super_block *sb,
> +			u64 *cur_index, int *offset, int *length)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	char *buf;
> +
> +	buf = squashfs_devread(msblk,
> +			 *cur_index * msblk->devblksize,
> +			 msblk->devblksize);
> +	if (buf == NULL)
> +		return NULL;
> +
> +	if (msblk->devblksize - *offset == 1) {
> +		*length = (unsigned char) buf[*offset];
> +		free(buf);
> +		buf = squashfs_devread(msblk,
> +				 ++(*cur_index) * msblk->devblksize,
> +				 msblk->devblksize);
> +		if (buf == NULL)
> +			return NULL;
> +		*length |= (unsigned char) buf[0] << 8;
> +		*offset = 1;
> +	} else {
> +		*length = (unsigned char) buf[*offset] |
> +			(unsigned char) buf[*offset + 1] << 8;
> +		*offset += 2;
> +
> +		if (*offset == msblk->devblksize) {
> +			free(buf);
> +			buf = squashfs_devread(msblk,
> +					 ++(*cur_index) * msblk->devblksize,
> +					 msblk->devblksize);
> +			if (buf == NULL)
> +				return NULL;
> +			*offset = 0;
> +		}
> +	}
> +
> +	return buf;
> +}
> +
> +
> +/*
> + * Read and decompress a metadata block or datablock.  Length is non-zero
> + * if a datablock is being read (the size is stored elsewhere in the
> + * filesystem), otherwise the length is obtained from the first two bytes of
> + * the metadata block.  A bit in the length field indicates if the block
> + * is stored uncompressed in the filesystem (usually because compression
> + * generated a larger block - this does occasionally happen with compression
> + * algorithms).
> + */
> +int squashfs_read_data(struct super_block *sb, u64 index, int length,
> +		u64 *next_index, struct squashfs_page_actor *output)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	char **buf;
> +	int offset = index & ((1 << msblk->devblksize_log2) - 1);
> +	u64 cur_index = index >> msblk->devblksize_log2;
> +	int bytes, compressed, b = 0, k = 0, avail;
> +
> +	buf = calloc(((output->length + msblk->devblksize - 1)
> +			>> msblk->devblksize_log2) + 1, sizeof(*buf));
> +	if (buf == NULL)
> +		return -ENOMEM;
> +
> +	if (length) {
> +		/*
> +		 * Datablock.
> +		 */
> +		bytes = -offset;
> +		compressed = SQUASHFS_COMPRESSED_BLOCK(length);
> +		length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
> +		if (next_index)
> +			*next_index = index + length;
> +
> +		TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
> +			index, compressed ? "" : "un", length, output->length);
> +
> +		if (length < 0 || length > output->length ||
> +				(index + length) > msblk->bytes_used) {
> +			goto read_failure;
> +		}
> +
> +		for (b = 0; bytes < length; b++, cur_index++) {
> +			buf[b] = squashfs_devread(msblk,
> +					 cur_index * msblk->devblksize,
> +					 msblk->devblksize);
> +			if (buf[b] == NULL)
> +				goto block_release;
> +
> +			bytes += msblk->devblksize;
> +		}
> +
> +	} else {
> +		/*
> +		 * Metadata block.
> +		 */
> +		if ((index + 2) > msblk->bytes_used)
> +			goto read_failure;
> +
> +		buf[0] = get_block_length(sb, &cur_index, &offset, &length);
> +		if (buf[0] == NULL)
> +			goto read_failure;
> +		b = 1;
> +
> +		bytes = msblk->devblksize - offset;
> +		compressed = SQUASHFS_COMPRESSED(length);
> +		length = SQUASHFS_COMPRESSED_SIZE(length);
> +		if (next_index)
> +			*next_index = index + length + 2;
> +
> +		TRACE("Block Meta @ 0x%llx, %scompressed size %d\n", index,
> +				compressed ? "" : "un", length);
> +
> +		if (length < 0 || length > output->length ||
> +					(index + length) > msblk->bytes_used)
> +			goto block_release;
> +
> +		for (; bytes < length; b++) {
> +			buf[b] = squashfs_devread(msblk,
> +					 ++cur_index * msblk->devblksize,
> +					 msblk->devblksize);
> +			if (buf[b] == NULL)
> +				goto block_release;
> +			bytes += msblk->devblksize;
> +		}
> +	}
> +
> +	if (compressed) {
> +		length = squashfs_decompress(msblk, buf, b, offset, length,
> +			output);
> +		if (length < 0)
> +			goto read_failure;
> +	} else {
> +		/*
> +		 * Block is uncompressed.
> +		 */
> +		int pg_offset = 0;
> +		void *data = squashfs_first_page(output);
> +
> +		for (bytes = length; k < b; k++) {
> +			int in = min(bytes, msblk->devblksize - offset);
> +			bytes -= in;
> +			while (in) {
> +				if (pg_offset == PAGE_CACHE_SIZE) {
> +					data = squashfs_next_page(output);
> +					pg_offset = 0;
> +				}
> +				avail = min_t(int, in, PAGE_CACHE_SIZE -
> +						pg_offset);
> +				memcpy(data + pg_offset, buf[k] + offset,
> +						avail);
> +				in -= avail;
> +				pg_offset += avail;
> +				offset += avail;
> +			}
> +			offset = 0;
> +			kfree(buf[k]);
> +		}
> +		squashfs_finish_page(output);
> +	}
> +
> +	kfree(buf);
> +	return length;
> +
> +block_release:
> +	for (; k < b; k++)
> +		kfree(buf[k]);
> +
> +read_failure:
> +	ERROR("squashfs_read_data failed to read block 0x%llx\n",
> +					(unsigned long long) index);
> +	kfree(buf);
> +	return -EIO;
> +}
> diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
> new file mode 100644
> index 0000000..766bc99
> --- /dev/null
> +++ b/fs/squashfs/cache.c
> @@ -0,0 +1,393 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * cache.c
> + */
> +
> +/*
> + * Blocks in Squashfs are compressed.  To avoid repeatedly decompressing
> + * recently accessed data Squashfs uses two small metadata and fragment caches.
> + *
> + * This file implements a generic cache implementation used for both caches,
> + * plus functions layered ontop of the generic cache implementation to
> + * access the metadata and fragment caches.
> + *
> + * To avoid out of memory and fragmentation issues with vmalloc the cache
> + * uses sequences of kmalloced PAGE_CACHE_SIZE buffers.
> + *
> + * It should be noted that the cache is not used for file datablocks, these
> + * are decompressed and cached in the page-cache in the normal way.  The
> + * cache is only used to temporarily cache fragment and metadata blocks
> + * which have been read as as a result of a metadata (i.e. inode or
> + * directory) or fragment access.  Because metadata and fragments are packed
> + * together into blocks (to gain greater compression) the read of a particular
> + * piece of metadata or fragment will retrieve other metadata/fragments which
> + * have been packed with it, these because of locality-of-reference may be read
> + * in the near future. Temporarily caching them ensures they are available for
> + * near future access without requiring an additional read and decompress.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/pagemap.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs.h"
> +#include "page_actor.h"
> +
> +/*
> + * Look-up block in cache, and increment usage count.  If not in cache, read
> + * and decompress it from disk.
> + */
> +struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
> +	struct squashfs_cache *cache, u64 block, int length)
> +{
> +	int i, n;
> +	struct squashfs_cache_entry *entry;
> +
> +	while (1) {
> +		for (i = cache->curr_blk, n = 0; n < cache->entries; n++) {
> +			if (cache->entry[i].block == block) {
> +				cache->curr_blk = i;
> +				break;
> +			}
> +			i = (i + 1) % cache->entries;
> +		}
> +
> +		if (n == cache->entries) {
> +
> +			/*
> +			 * At least one unused cache entry.  A simple
> +			 * round-robin strategy is used to choose the entry to
> +			 * be evicted from the cache.
> +			 */
> +			i = cache->next_blk;
> +			for (n = 0; n < cache->entries; n++) {
> +				if (cache->entry[i].refcount == 0)
> +					break;
> +				i = (i + 1) % cache->entries;
> +			}
> +
> +			cache->next_blk = (i + 1) % cache->entries;
> +			entry = &cache->entry[i];
> +
> +			/*
> +			 * Initialise chosen cache entry, and fill it in from
> +			 * disk.
> +			 */
> +			cache->unused--;
> +			entry->block = block;
> +			entry->refcount = 1;
> +			entry->pending = 1;
> +			entry->error = 0;
> +
> +			entry->length = squashfs_read_data(sb, block, length,
> +				&entry->next_index, entry->actor);
> +
> +			if (entry->length < 0)
> +				entry->error = entry->length;
> +
> +			entry->pending = 0;
> +
> +			goto out;
> +		}
> +
> +		/*
> +		 * Block already in cache.  Increment refcount so it doesn't
> +		 * get reused until we're finished with it, if it was
> +		 * previously unused there's one less cache entry available
> +		 * for reuse.
> +		 */
> +		entry = &cache->entry[i];
> +		if (entry->refcount == 0)
> +			cache->unused--;
> +		entry->refcount++;
> +
> +		goto out;
> +	}
> +
> +out:
> +	TRACE("Got %s %d, start block %lld, refcount %d, error %d\n",
> +		cache->name, i, entry->block, entry->refcount, entry->error);
> +
> +	if (entry->error)
> +		ERROR("Unable to read %s cache entry [%llx]\n", cache->name,
> +							block);
> +	return entry;
> +}
> +
> +
> +/*
> + * Release cache entry, once usage count is zero it can be reused.
> + */
> +void squashfs_cache_put(struct squashfs_cache_entry *entry)
> +{
> +	struct squashfs_cache *cache = entry->cache;
> +
> +	entry->refcount--;
> +	if (entry->refcount == 0)
> +		cache->unused++;
> +}
> +
> +/*
> + * Delete cache reclaiming all kmalloced buffers.
> + */
> +void squashfs_cache_delete(struct squashfs_cache *cache)
> +{
> +	int i, j;
> +
> +	if (cache == NULL)
> +		return;
> +
> +	for (i = 0; i < cache->entries; i++) {
> +		if (cache->entry[i].data) {
> +			for (j = 0; j < cache->pages; j++)
> +				kfree(cache->entry[i].data[j]);
> +			kfree(cache->entry[i].data);
> +		}
> +		kfree(cache->entry[i].actor);
> +	}
> +
> +	kfree(cache->entry);
> +	kfree(cache);
> +}
> +
> +
> +/*
> + * Initialise cache allocating the specified number of entries, each of
> + * size block_size.  To avoid vmalloc fragmentation issues each entry
> + * is allocated as a sequence of kmalloced PAGE_CACHE_SIZE buffers.
> + */
> +struct squashfs_cache *squashfs_cache_init(char *name, int entries,
> +	int block_size)
> +{
> +	int i, j;
> +	struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);
> +
> +	if (cache == NULL) {
> +		ERROR("Failed to allocate %s cache\n", name);
> +		return NULL;
> +	}
> +
> +	cache->entry = calloc(entries, sizeof(*(cache->entry)));
> +	if (cache->entry == NULL) {
> +		ERROR("Failed to allocate %s cache\n", name);
> +		goto cleanup;
> +	}
> +
> +	cache->curr_blk = 0;
> +	cache->next_blk = 0;
> +	cache->unused = entries;
> +	cache->entries = entries;
> +	cache->block_size = block_size;
> +	cache->pages = block_size >> PAGE_CACHE_SHIFT;
> +	cache->pages = cache->pages ? cache->pages : 1;
> +	cache->name = name;
> +
> +	for (i = 0; i < entries; i++) {
> +		struct squashfs_cache_entry *entry = &cache->entry[i];
> +
> +		entry->cache = cache;
> +		entry->block = SQUASHFS_INVALID_BLK;
> +		entry->data = calloc(cache->pages, sizeof(void *));
> +		if (entry->data == NULL) {
> +			ERROR("Failed to allocate %s cache entry\n", name);
> +			goto cleanup;
> +		}
> +
> +		for (j = 0; j < cache->pages; j++) {
> +			entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
> +			if (entry->data[j] == NULL) {
> +				ERROR("Failed to allocate %s buffer\n", name);
> +				goto cleanup;
> +			}
> +		}
> +
> +		entry->actor = squashfs_page_actor_init(entry->data,
> +						cache->pages, 0);
> +		if (entry->actor == NULL) {
> +			ERROR("Failed to allocate %s cache entry\n", name);
> +			goto cleanup;
> +		}
> +	}
> +
> +	return cache;
> +
> +cleanup:
> +	squashfs_cache_delete(cache);
> +	return NULL;
> +}
> +
> +
> +/*
> + * Copy up to length bytes from cache entry to buffer starting at offset bytes
> + * into the cache entry.  If there's not length bytes then copy the number of
> + * bytes available.  In all cases return the number of bytes copied.
> + */
> +int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
> +		int offset, int length)
> +{
> +	int remaining = length;
> +
> +	if (length == 0)
> +		return 0;
> +	else if (buffer == NULL)
> +		return min(length, entry->length - offset);
> +
> +	while (offset < entry->length) {
> +		void *buff = entry->data[offset / PAGE_CACHE_SIZE]
> +				+ (offset % PAGE_CACHE_SIZE);
> +		int bytes = min_t(int, entry->length - offset,
> +				PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE));
> +
> +		if (bytes >= remaining) {
> +			memcpy(buffer, buff, remaining);
> +			remaining = 0;
> +			break;
> +		}
> +
> +		memcpy(buffer, buff, bytes);
> +		buffer += bytes;
> +		remaining -= bytes;
> +		offset += bytes;
> +	}
> +
> +	return length - remaining;
> +}
> +
> +
> +/*
> + * Read length bytes from metadata position <block, offset> (block is the
> + * start of the compressed block on disk, and offset is the offset into
> + * the block once decompressed).  Data is packed into consecutive blocks,
> + * and length bytes may require reading more than one block.
> + */
> +int squashfs_read_metadata(struct super_block *sb, void *buffer,
> +		u64 *block, int *offset, int length)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	int bytes, res = length;
> +	struct squashfs_cache_entry *entry;
> +
> +	TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset);
> +
> +	while (length) {
> +		entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0);
> +		if (entry->error) {
> +			TRACE("entry->error\n");
> +			res = entry->error;
> +			goto error;
> +		} else if (*offset >= entry->length) {
> +			TRACE("entry->length\n");
> +			res = -EIO;
> +			goto error;
> +		}
> +
> +		bytes = squashfs_copy_data(buffer, entry, *offset, length);
> +		if (buffer)
> +			buffer += bytes;
> +		length -= bytes;
> +		*offset += bytes;
> +
> +		if (*offset == entry->length) {
> +			*block = entry->next_index;
> +			*offset = 0;
> +		}
> +
> +		squashfs_cache_put(entry);
> +	}
> +
> +	return res;
> +
> +error:
> +	squashfs_cache_put(entry);
> +	return res;
> +}
> +
> +
> +/*
> + * Look-up in the fragmment cache the fragment located at <start_block> in the
> + * filesystem.  If necessary read and decompress it from disk.
> + */
> +struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *sb,
> +				u64 start_block, int length)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +
> +	return squashfs_cache_get(sb, msblk->fragment_cache, start_block,
> +		length);
> +}
> +
> +
> +/*
> + * Read and decompress the datablock located at <start_block> in the
> + * filesystem.  The cache is used here to avoid duplicating locking and
> + * read/decompress code.
> + */
> +struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,
> +				u64 start_block, int length)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +
> +	return squashfs_cache_get(sb, msblk->read_page, start_block, length);
> +}
> +
> +/*
> + * Read a filesystem table (uncompressed sequence of bytes) from disk
> + */
> +void *squashfs_read_table(struct super_block *sb, u64 block, int length)
> +{
> +	int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
> +	int i, res;
> +	void *table, *buffer, **data;
> +	struct squashfs_page_actor *actor;
> +
> +	table = buffer = kmalloc(length, GFP_KERNEL);
> +	if (table == NULL)
> +		return ERR_PTR(-ENOMEM);
> +
> +	data = calloc(pages, sizeof(void *));
> +	if (data == NULL) {
> +		res = -ENOMEM;
> +		goto failed;
> +	}
> +
> +	actor = squashfs_page_actor_init(data, pages, length);
> +	if (actor == NULL) {
> +		res = -ENOMEM;
> +		goto failed2;
> +	}
> +
> +	for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
> +		data[i] = buffer;
> +
> +	res = squashfs_read_data(sb, block, length |
> +		SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
> +
> +	kfree(data);
> +	kfree(actor);
> +
> +	if (res < 0)
> +		goto failed;
> +
> +	return table;
> +
> +failed2:
> +	kfree(data);
> +failed:
> +	kfree(table);
> +	return ERR_PTR(res);
> +}
> diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
> new file mode 100644
> index 0000000..558457b
> --- /dev/null
> +++ b/fs/squashfs/decompressor.c
> @@ -0,0 +1,140 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * decompressor.c
> + */
> +
> +#include <linux/types.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "decompressor.h"
> +#include "squashfs.h"
> +#include "page_actor.h"
> +
> +/*
> + * This file (and decompressor.h) implements a decompressor framework for
> + * Squashfs, allowing multiple decompressors to be easily supported
> + */
> +
> +static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = {
> +	NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0
> +};
> +
> +#ifndef CONFIG_SQUASHFS_LZ4
> +static const struct squashfs_decompressor squashfs_lz4_comp_ops = {
> +	NULL, NULL, NULL, NULL, LZ4_COMPRESSION, "lz4", 0
> +};
> +#endif
> +
> +#ifndef CONFIG_SQUASHFS_LZO
> +static const struct squashfs_decompressor squashfs_lzo_comp_ops = {
> +	NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0
> +};
> +#endif
> +
> +#ifndef CONFIG_SQUASHFS_XZ
> +static const struct squashfs_decompressor squashfs_xz_comp_ops = {
> +	NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0
> +};
> +#endif
> +
> +#ifndef CONFIG_SQUASHFS_ZLIB
> +static const struct squashfs_decompressor squashfs_zlib_comp_ops = {
> +	NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0
> +};
> +#endif
> +
> +static const struct squashfs_decompressor squashfs_unknown_comp_ops = {
> +	NULL, NULL, NULL, NULL, 0, "unknown", 0
> +};
> +
> +static const struct squashfs_decompressor *decompressor[] = {
> +	&squashfs_zlib_comp_ops,
> +	&squashfs_lz4_comp_ops,
> +	&squashfs_lzo_comp_ops,
> +	&squashfs_xz_comp_ops,
> +	&squashfs_lzma_unsupported_comp_ops,
> +	&squashfs_unknown_comp_ops
> +};
> +
> +
> +const struct squashfs_decompressor *squashfs_lookup_decompressor(int id)
> +{
> +	int i;
> +
> +	for (i = 0; decompressor[i]->id; i++)
> +		if (id == decompressor[i]->id)
> +			break;
> +
> +	return decompressor[i];
> +}
> +
> +
> +static void *get_comp_opts(struct super_block *sb, unsigned short flags)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	void *buffer = NULL, *comp_opts;
> +	struct squashfs_page_actor *actor = NULL;
> +	int length = 0;
> +
> +	/*
> +	 * Read decompressor specific options from file system if present
> +	 */
> +	if (SQUASHFS_COMP_OPTS(flags)) {
> +		buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
> +		if (buffer == NULL) {
> +			comp_opts = ERR_PTR(-ENOMEM);
> +			goto out;
> +		}
> +
> +		actor = squashfs_page_actor_init(&buffer, 1, 0);
> +		if (actor == NULL) {
> +			comp_opts = ERR_PTR(-ENOMEM);
> +			goto out;
> +		}
> +		length = squashfs_read_data(sb,
> +			sizeof(struct squashfs_super_block), 0, NULL, actor);
> +
> +		if (length < 0) {
> +			comp_opts = ERR_PTR(length);
> +			goto out;
> +		}
> +	}
> +
> +	comp_opts = squashfs_comp_opts(msblk, buffer, length);
> +
> +out:
> +	kfree(actor);
> +	kfree(buffer);
> +	return comp_opts;
> +}
> +
> +
> +void *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	void *stream, *comp_opts = get_comp_opts(sb, flags);
> +
> +	if (IS_ERR(comp_opts))
> +		return comp_opts;
> +
> +	stream = squashfs_decompressor_create(msblk, comp_opts);
> +	if (IS_ERR(stream))
> +		kfree(comp_opts);
> +
> +	return stream;
> +}
> diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h
> new file mode 100644
> index 0000000..b097677
> --- /dev/null
> +++ b/fs/squashfs/decompressor.h
> @@ -0,0 +1,57 @@
> +#ifndef DECOMPRESSOR_H
> +#define DECOMPRESSOR_H
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * decompressor.h
> + */
> +
> +struct squashfs_decompressor {
> +	void	*(*init)(struct squashfs_sb_info *, void *);
> +	void	*(*comp_opts)(struct squashfs_sb_info *, void *, int);
> +	void	(*free)(void *);
> +	int	(*decompress)(struct squashfs_sb_info *, void *,
> +		char **, int, int, int,
> +		struct squashfs_page_actor *);
> +	int	id;
> +	char	*name;
> +	int	supported;
> +};
> +
> +static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk,
> +							void *buff, int length)
> +{
> +	return msblk->decompressor->comp_opts ?
> +		msblk->decompressor->comp_opts(msblk, buff, length) : NULL;
> +}
> +
> +#ifdef CONFIG_SQUASHFS_XZ
> +extern const struct squashfs_decompressor squashfs_xz_comp_ops;
> +#endif
> +
> +#ifdef CONFIG_SQUASHFS_LZ4
> +extern const struct squashfs_decompressor squashfs_lz4_comp_ops;
> +#endif
> +
> +#ifdef CONFIG_SQUASHFS_LZO
> +extern const struct squashfs_decompressor squashfs_lzo_comp_ops;
> +#endif
> +
> +#ifdef CONFIG_SQUASHFS_ZLIB
> +extern const struct squashfs_decompressor squashfs_zlib_comp_ops;
> +#endif
> +
> +#endif
> diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c
> new file mode 100644
> index 0000000..046834c
> --- /dev/null
> +++ b/fs/squashfs/decompressor_single.c
> @@ -0,0 +1,83 @@
> +/*
> + * Copyright (c) 2013
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include <linux/types.h>
> +#include <malloc.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "decompressor.h"
> +#include "squashfs.h"
> +
> +/*
> + * This file implements single-threaded decompression in the
> + * decompressor framework
> + */
> +
> +struct squashfs_stream {
> +	void		*stream;
> +	struct mutex	mutex;
> +};
> +
> +void *squashfs_decompressor_create(struct squashfs_sb_info *msblk,
> +						void *comp_opts)
> +{
> +	struct squashfs_stream *stream;
> +	int err = -ENOMEM;
> +
> +	stream = kmalloc(sizeof(*stream), GFP_KERNEL);
> +	if (stream == NULL)
> +		goto out;
> +
> +	stream->stream = msblk->decompressor->init(msblk, comp_opts);
> +	if (IS_ERR(stream->stream)) {
> +		err = PTR_ERR(stream->stream);
> +		goto out;
> +	}
> +
> +	kfree(comp_opts);
> +	mutex_init(&stream->mutex);
> +	return stream;
> +
> +out:
> +	kfree(stream);
> +	return ERR_PTR(err);
> +}
> +
> +void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk)
> +{
> +	struct squashfs_stream *stream = msblk->stream;
> +
> +	if (stream) {
> +		msblk->decompressor->free(stream->stream);
> +		kfree(stream);
> +	}
> +}
> +
> +int squashfs_decompress(struct squashfs_sb_info *msblk, char **bh,
> +	int b, int offset, int length, struct squashfs_page_actor *output)
> +{
> +	int res;
> +	struct squashfs_stream *stream = msblk->stream;
> +
> +	mutex_lock(&stream->mutex);
> +	res = msblk->decompressor->decompress(msblk, stream->stream, bh, b,
> +		offset, length, output);
> +	mutex_unlock(&stream->mutex);
> +
> +	if (res < 0)
> +		ERROR("%s decompression failed, data probably corrupt\n",
> +			msblk->decompressor->name);
> +
> +	return res;
> +}
> +
> +int squashfs_max_decompressors(void)
> +{
> +	return 1;
> +}
> diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
> new file mode 100644
> index 0000000..0806f90
> --- /dev/null
> +++ b/fs/squashfs/file.c
> @@ -0,0 +1,445 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * file.c
> + */
> +
> +/*
> + * This file contains code for handling regular files.  A regular file
> + * consists of a sequence of contiguous compressed blocks, and/or a
> + * compressed fragment block (tail-end packed block).   The compressed size
> + * of each datablock is stored in a block list contained within the
> + * file inode (itself stored in one or more compressed metadata blocks).
> + *
> + * To speed up access to datablocks when reading 'large' files (256 Mbytes or
> + * larger), the code implements an index cache that caches the mapping from
> + * block index to datablock location on disk.
> + *
> + * The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
> + * retaining a simple and space-efficient block list on disk.  The cache
> + * is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
> + * Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
> + * The index cache is designed to be memory efficient, and by default uses
> + * 16 KiB.
> + */
> +
> +#include <malloc.h>
> +#include <linux/fs.h>
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/pagemap.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs_fs_i.h"
> +#include "squashfs.h"
> +
> +/*
> + * Locate cache slot in range [offset, index] for specified inode.  If
> + * there's more than one return the slot closest to index.
> + */
> +static struct meta_index *locate_meta_index(struct inode *inode, int offset,
> +				int index)
> +{
> +	struct meta_index *meta = NULL;
> +	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
> +	int i;
> +
> +	if (msblk->meta_index == NULL)
> +		goto not_allocated;
> +
> +	for (i = 0; i < SQUASHFS_META_SLOTS; 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:
> +	return meta;
> +}
> +
> +
> +/*
> + * Find and initialise an empty cache slot for index offset.
> + */
> +static 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;
> +
> +	TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
> +
> +	if (msblk->meta_index == NULL) {
> +		/*
> +		 * First time cache index has been used, allocate and
> +		 * initialise.  The cache index could be allocated at
> +		 * mount time but doing it here means it is allocated only
> +		 * if a 'large' file is read.
> +		 */
> +		msblk->meta_index = calloc(SQUASHFS_META_SLOTS,
> +			sizeof(*(msblk->meta_index)));
> +		if (msblk->meta_index == NULL) {
> +			ERROR("Failed to allocate meta_index\n");
> +			goto failed;
> +		}
> +		for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
> +			msblk->meta_index[i].inode_number = 0;
> +			msblk->meta_index[i].locked = 0;
> +		}
> +		msblk->next_meta_index = 0;
> +	}
> +
> +	for (i = SQUASHFS_META_SLOTS; i &&
> +			msblk->meta_index[msblk->next_meta_index].locked; i--)
> +		msblk->next_meta_index = (msblk->next_meta_index + 1) %
> +			SQUASHFS_META_SLOTS;
> +
> +	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_SLOTS;
> +
> +	meta->inode_number = inode->i_ino;
> +	meta->offset = offset;
> +	meta->skip = skip;
> +	meta->entries = 0;
> +	meta->locked = 1;
> +
> +failed:
> +	return meta;
> +}
> +
> +/*
> + * Read the next n blocks from the block list, starting from
> + * metadata block <start_block, offset>.
> + */
> +static long long read_indexes(struct super_block *sb, int n,
> +				u64 *start_block, int *offset)
> +{
> +	int err, i;
> +	long long block = 0;
> +	__le32 *blist = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
> +
> +	if (blist == NULL) {
> +		ERROR("read_indexes: Failed to allocate block_list\n");
> +		return -ENOMEM;
> +	}
> +
> +	while (n) {
> +		int blocks = min_t(int, n, PAGE_CACHE_SIZE >> 2);
> +
> +		err = squashfs_read_metadata(sb, blist, start_block,
> +				offset, blocks << 2);
> +		if (err < 0) {
> +			ERROR("read_indexes: reading block [%llx:%x]\n",
> +				*start_block, *offset);
> +			goto failure;
> +		}
> +
> +		for (i = 0; i < blocks; i++) {
> +			int size = le32_to_cpu(blist[i]);
> +			block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
> +		}
> +		n -= blocks;
> +	}
> +
> +	kfree(blist);
> +	return block;
> +
> +failure:
> +	kfree(blist);
> +	return err;
> +}
> +
> +
> +/*
> + * Each cache index slot has SQUASHFS_META_ENTRIES, each of which
> + * can cache one index -> datablock/blocklist-block mapping.  We wish
> + * to distribute these over the length of the file, entry[0] maps index x,
> + * entry[1] maps index x + skip, entry[2] maps index x + 2 * skip, and so on.
> + * The larger the file, the greater the skip factor.  The skip factor is
> + * limited to the size of the metadata cache (SQUASHFS_CACHED_BLKS) to ensure
> + * the number of metadata blocks that need to be read fits into the cache.
> + * If the skip factor is limited in this way then the file will use multiple
> + * slots.
> + */
> +static inline int calculate_skip(int blocks)
> +{
> +	int skip = blocks / ((SQUASHFS_META_ENTRIES + 1)
> +		 * SQUASHFS_META_INDEXES);
> +	return min(SQUASHFS_CACHED_BLKS - 1, skip + 1);
> +}
> +
> +
> +/*
> + * Search and grow the index cache for the specified inode, returning the
> + * on-disk locations of the datablock and block list metadata block
> + * <index_block, index_offset> for index (scaled to nearest cache index).
> + */
> +static int fill_meta_index(struct inode *inode, int index,
> +		u64 *index_block, int *index_offset, u64 *data_block)
> +{
> +	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
> +	int skip = calculate_skip(i_size_read(inode) >> msblk->block_log);
> +	int offset = 0;
> +	struct meta_index *meta;
> +	struct meta_entry *meta_entry;
> +	u64 cur_index_block = squashfs_i(inode)->block_list_start;
> +	int cur_offset = squashfs_i(inode)->offset;
> +	u64 cur_data_block = squashfs_i(inode)->start;
> +	int err, i;
> +
> +	/*
> +	 * Scale index to cache index (cache slot entry)
> +	 */
> +	index /= SQUASHFS_META_INDEXES * skip;
> +
> +	while (offset < index) {
> +		meta = locate_meta_index(inode, offset + 1, index);
> +
> +		if (meta == NULL) {
> +			meta = empty_meta_index(inode, offset + 1, skip);
> +			if (meta == 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 +
> +				msblk->inode_table;
> +			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);
> +		}
> +
> +		/*
> +		 * If necessary grow cache slot by reading block list.  Cache
> +		 * slot is extended up to index or to the end of the slot, in
> +		 * which case further slots will be used.
> +		 */
> +		for (i = meta->offset + meta->entries; i <= index &&
> +				i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
> +			int blocks = skip * SQUASHFS_META_INDEXES;
> +			long long res = read_indexes(inode->i_sb, blocks,
> +					&cur_index_block, &cur_offset);
> +
> +			if (res < 0) {
> +				if (meta->entries == 0)
> +					/*
> +					 * Don't leave an empty slot on read
> +					 * error allocated to this inode...
> +					 */
> +					meta->inode_number = 0;
> +				err = res;
> +				goto failed;
> +			}
> +
> +			cur_data_block += res;
> +			meta_entry = &meta->meta_entry[i - meta->offset];
> +			meta_entry->index_block = cur_index_block -
> +				msblk->inode_table;
> +			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);
> +
> +	}
> +
> +all_done:
> +	*index_block = cur_index_block;
> +	*index_offset = cur_offset;
> +	*data_block = cur_data_block;
> +
> +	/*
> +	 * Scale cache index (cache slot entry) to index
> +	 */
> +	return offset * SQUASHFS_META_INDEXES * skip;
> +
> +failed:
> +	return err;
> +}
> +
> +
> +/*
> + * Get the on-disk location and compressed size of the datablock
> + * specified by index.  Fill_meta_index() does most of the work.
> + */
> +static int read_blocklist(struct inode *inode, int index, u64 *block)
> +{
> +	u64 start;
> +	long long blks;
> +	int offset;
> +	__le32 size;
> +	int res = fill_meta_index(inode, index, &start, &offset, block);
> +
> +	TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset"
> +		       " 0x%x, block 0x%llx\n", res, index, start, offset,
> +			*block);
> +
> +	if (res < 0)
> +		return res;
> +
> +	/*
> +	 * res contains the index of the mapping returned by fill_meta_index(),
> +	 * this will likely be less than the desired index (because the
> +	 * meta_index cache works at a higher granularity).  Read any
> +	 * extra block indexes needed.
> +	 */
> +	if (res < index) {
> +		blks = read_indexes(inode->i_sb, index - res, &start, &offset);
> +		if (blks < 0)
> +			return (int) blks;
> +		*block += blks;
> +	}
> +
> +	/*
> +	 * Read length of block specified by index.
> +	 */
> +	res = squashfs_read_metadata(inode->i_sb, &size, &start, &offset,
> +			sizeof(size));
> +	if (res < 0)
> +		return res;
> +	return le32_to_cpu(size);
> +}
> +
> +/* Copy data into page cache  */
> +void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer,
> +	int bytes, int offset)
> +{
> +	int i;
> +	struct squashfs_page *sq_page = squashfs_page(page);
> +
> +	/*
> +	 * Loop copying datablock into pages.  As the datablock likely covers
> +	 * many PAGE_CACHE_SIZE pages (default block size is 128 KiB) explicitly
> +	 * grab the pages from the page cache, except for the page that we've
> +	 * been called to fill.
> +	 */
> +	for (i = 0; i < 32 && bytes > 0; i++,
> +			bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
> +		int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0;
> +
> +		TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
> +
> +		squashfs_copy_data(sq_page->buf[i], buffer, offset, avail);
> +		memset(sq_page->buf[i] + avail, 0, PAGE_CACHE_SIZE - avail);
> +		sq_page->idx++;
> +	}
> +}
> +
> +/* Read datablock stored packed inside a fragment (tail-end packed block) */
> +static int squashfs_readpage_fragment(struct page *page)
> +{
> +	struct inode *inode = page->inode;
> +	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
> +	struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb,
> +		squashfs_i(inode)->fragment_block,
> +		squashfs_i(inode)->fragment_size);
> +	int res = buffer->error;
> +
> +	TRACE("squashfs_readpage_fragment: frag size: %d\n",
> +		squashfs_i(inode)->fragment_size);
> +	if (res)
> +		ERROR("Unable to read page, block %llx, size %x\n",
> +			squashfs_i(inode)->fragment_block,
> +			squashfs_i(inode)->fragment_size);
> +	else
> +		squashfs_copy_cache(page, buffer, i_size_read(inode) &
> +			(msblk->block_size - 1),
> +			squashfs_i(inode)->fragment_offset);
> +
> +	squashfs_cache_put(buffer);
> +	return res;
> +}
> +
> +static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
> +{
> +	struct inode *inode = page->inode;
> +	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
> +	int bytes = index == file_end ?
> +			(i_size_read(inode) & (msblk->block_size - 1)) :
> +			 msblk->block_size;
> +
> +	squashfs_copy_cache(page, NULL, bytes, 0);
> +
> +	return 0;
> +}
> +
> +int squashfs_readpage(struct file *file, struct page *page)
> +{
> +	struct inode *inode = page->inode;
> +	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
> +	int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
> +	int file_end = i_size_read(inode) >> msblk->block_log;
> +	int res;
> +
> +	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
> +				page->index, squashfs_i(inode)->start);
> +
> +	if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
> +					PAGE_CACHE_SHIFT))
> +		goto out;
> +
> +	if (index < file_end || squashfs_i(inode)->fragment_block ==
> +					SQUASHFS_INVALID_BLK) {
> +		u64 block = 0;
> +		int bsize = read_blocklist(inode, index, &block);
> +		if (bsize < 0)
> +			goto out;
> +
> +		if (bsize == 0)
> +			res = squashfs_readpage_sparse(page, index, file_end);
> +		else
> +			res = squashfs_readpage_block(page, block, bsize);
> +	} else
> +		res = squashfs_readpage_fragment(page);
> +
> +	if (!res)
> +		return 0;
> +
> +out:
> +	memset(page->addr, 0, PAGE_CACHE_SIZE);
> +
> +	return 0;
> +}
> diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c
> new file mode 100644
> index 0000000..9443e45
> --- /dev/null
> +++ b/fs/squashfs/file_cache.c
> @@ -0,0 +1,35 @@
> +/*
> + * Copyright (c) 2013
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include <linux/fs.h>
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/pagemap.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs_fs_i.h"
> +#include "squashfs.h"
> +
> +/* Read separately compressed datablock and memcopy into page cache */
> +int squashfs_readpage_block(struct page *page, u64 block, int bsize)
> +{
> +	struct inode *i = page->inode;
> +	struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
> +		block, bsize);
> +	int res = buffer->error;
> +
> +	if (res)
> +		ERROR("Unable to read page, block %llx, size %x\n", block,
> +			bsize);
> +	else
> +		squashfs_copy_cache(page, buffer, buffer->length, 0);
> +
> +	squashfs_cache_put(buffer);
> +	return res;
> +}
> diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c
> new file mode 100644
> index 0000000..2b99ff5
> --- /dev/null
> +++ b/fs/squashfs/fragment.c
> @@ -0,0 +1,94 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * fragment.c
> + */
> +
> +/*
> + * This file implements code to handle compressed fragments (tail-end packed
> + * datablocks).
> + *
> + * Regular files contain a fragment index which is mapped to a fragment
> + * location on disk and compressed size using a fragment lookup table.
> + * Like everything in Squashfs this fragment lookup table is itself stored
> + * compressed into metadata blocks.  A second index table is used to locate
> + * these.  This second index table for speed of access (and because it
> + * is small) is read at mount time and cached in memory.
> + */
> +
> +#include <malloc.h>
> +#include <linux/fs.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs.h"
> +
> +/*
> + * Look-up fragment using the fragment index table.  Return the on disk
> + * location of the fragment and its compressed size
> + */
> +int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment,
> +				u64 *fragment_block)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	int block = SQUASHFS_FRAGMENT_INDEX(fragment);
> +	int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
> +	u64 start_block = le64_to_cpu(msblk->fragment_index[block]);
> +	struct squashfs_fragment_entry fragment_entry;
> +	int size;
> +
> +	size = squashfs_read_metadata(sb, &fragment_entry, &start_block,
> +					&offset, sizeof(fragment_entry));
> +	if (size < 0)
> +		return size;
> +
> +	*fragment_block = le64_to_cpu(fragment_entry.start_block);
> +	size = le32_to_cpu(fragment_entry.size);
> +
> +	return size;
> +}
> +
> +
> +/*
> + * Read the uncompressed fragment lookup table indexes off disk into memory
> + */
> +__le64 *squashfs_read_fragment_index_table(struct super_block *sb,
> +	u64 fragment_table_start, u64 next_table, unsigned int fragments)
> +{
> +	unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments);
> +	__le64 *table;
> +
> +	/*
> +	 * Sanity check, length bytes should not extend into the next table -
> +	 * this check also traps instances where fragment_table_start is
> +	 * incorrectly larger than the next table start
> +	 */
> +	if (fragment_table_start + length > next_table)
> +		return ERR_PTR(-EINVAL);
> +
> +	table = squashfs_read_table(sb, fragment_table_start, length);
> +
> +	/*
> +	 * table[0] points to the first fragment table metadata block, this
> +	 * should be less than fragment_table_start
> +	 */
> +	if (!IS_ERR(table) && le64_to_cpu(table[0]) >= fragment_table_start) {
> +		kfree(table);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return table;
> +}
> diff --git a/fs/squashfs/id.c b/fs/squashfs/id.c
> new file mode 100644
> index 0000000..5db0f73
> --- /dev/null
> +++ b/fs/squashfs/id.c
> @@ -0,0 +1,98 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * id.c
> + */
> +
> +/*
> + * This file implements code to handle uids and gids.
> + *
> + * For space efficiency regular files store uid and gid indexes, which are
> + * converted to 32-bit uids/gids using an id look up table.  This table is
> + * stored compressed into metadata blocks.  A second index table is used to
> + * locate these.  This second index table for speed of access (and because it
> + * is small) is read at mount time and cached in memory.
> + */
> +
> +#include <linux/barebox-wrapper.h>
> +#include <linux/fs.h>
> +#include <malloc.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs.h"
> +
> +/*
> + * Map uid/gid index into real 32-bit uid/gid using the id look up table
> + */
> +int squashfs_get_id(struct super_block *sb, unsigned int index,
> +					unsigned int *id)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	int block = SQUASHFS_ID_BLOCK(index);
> +	int offset = SQUASHFS_ID_BLOCK_OFFSET(index);
> +	u64 start_block = le64_to_cpu(msblk->id_table[block]);
> +	__le32 disk_id;
> +	int err;
> +
> +	err = squashfs_read_metadata(sb, &disk_id, &start_block, &offset,
> +							sizeof(disk_id));
> +	if (err < 0)
> +		return err;
> +
> +	*id = le32_to_cpu(disk_id);
> +	return 0;
> +}
> +
> +
> +/*
> + * Read uncompressed id lookup table indexes from disk into memory
> + */
> +__le64 *squashfs_read_id_index_table(struct super_block *sb,
> +		u64 id_table_start, u64 next_table, unsigned short no_ids)
> +{
> +	unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids);
> +	__le64 *table;
> +
> +	TRACE("In read_id_index_table, length %d\n", length);
> +
> +	/* Sanity check values */
> +
> +	/* there should always be at least one id */
> +	if (no_ids == 0)
> +		return ERR_PTR(-EINVAL);
> +
> +	/*
> +	 * length bytes should not extend into the next table - this check
> +	 * also traps instances where id_table_start is incorrectly larger
> +	 * than the next table start
> +	 */
> +	if (id_table_start + length > next_table)
> +		return ERR_PTR(-EINVAL);
> +
> +	table = squashfs_read_table(sb, id_table_start, length);
> +
> +	/*
> +	 * table[0] points to the first id lookup table metadata block, this
> +	 * should be less than id_table_start
> +	 */
> +	if (!IS_ERR(table) && le64_to_cpu(table[0]) >= id_table_start) {
> +		kfree(table);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return table;
> +}
> diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
> new file mode 100644
> index 0000000..749fc7e
> --- /dev/null
> +++ b/fs/squashfs/inode.c
> @@ -0,0 +1,402 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * inode.c
> + */
> +
> +/*
> + * This file implements code to create and read inodes from disk.
> + *
> + * Inodes in Squashfs are identified by a 48-bit inode which encodes the
> + * location of the compressed metadata block containing the inode, and the byte
> + * offset into that block where the inode is placed (<block, offset>).
> + *
> + * To maximise compression there are different inodes for each file type
> + * (regular file, directory, device, etc.), the inode contents and length
> + * varying with the type.
> + *
> + * To further maximise compression, two types of regular file inode and
> + * directory inode are defined: inodes optimised for frequently occurring
> + * regular files and directories, and extended types where extra
> + * information has to be stored.
> + */
> +
> +#include <malloc.h>
> +#include <linux/fs.h>
> +#include <linux/stat.h>
> +#include <linux/pagemap.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs_fs_i.h"
> +#include "squashfs.h"
> +
> +struct inode *iget_locked_squashfs(struct super_block *sb, unsigned long ino)
> +{
> +	struct inode *inode;
> +	struct squashfs_inode_info *ei;
> +
> +	ei = malloc(sizeof(struct squashfs_inode_info));
> +	inode = &ei->vfs_inode;
> +	if (inode) {
> +		inode->i_ino = ino;
> +		inode->i_sb = sb;
> +		inode->i_state = I_SYNC | I_NEW;
> +	}
> +
> +	return inode;
> +}
> +
> +/*
> + * Initialise VFS inode with the base inode information common to all
> + * Squashfs inode types.  Sqsh_ino contains the unswapped base inode
> + * off disk.
> + */
> +static int squashfs_new_inode(struct super_block *sb, struct inode *inode,
> +				struct squashfs_base_inode *sqsh_ino)
> +{
> +	uid_t i_uid;
> +	gid_t i_gid;
> +	int err;
> +
> +	err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->uid), &i_uid);
> +	if (err)
> +		return err;
> +
> +	err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->guid), &i_gid);
> +	if (err)
> +		return err;
> +
> +	inode->i_ino = le32_to_cpu(sqsh_ino->inode_number);
> +	inode->i_mtime.tv_sec = le32_to_cpu(sqsh_ino->mtime);
> +	inode->i_atime.tv_sec = inode->i_mtime.tv_sec;
> +	inode->i_ctime.tv_sec = inode->i_mtime.tv_sec;
> +	inode->i_mode = le16_to_cpu(sqsh_ino->mode);
> +	inode->i_size = 0;
> +
> +	return err;
> +}
> +
> +
> +struct inode *squashfs_iget(struct super_block *sb, long long ino,
> +				unsigned int ino_number)
> +{
> +	struct inode *inode = iget_locked_squashfs(sb, ino_number);
> +	int err;
> +
> +	TRACE("Entered squashfs_iget\n");
> +
> +	if (!inode)
> +		return ERR_PTR(-ENOMEM);
> +	if (!(inode->i_state & I_NEW))
> +		return inode;
> +
> +	err = squashfs_read_inode(inode, ino);
> +	if (err)
> +		return ERR_PTR(err);
> +
> +	return inode;
> +}
> +
> +
> +/*
> + * Initialise VFS inode by reading inode from inode table (compressed
> + * metadata).  The format and amount of data read depends on type.
> + */
> +int squashfs_read_inode(struct inode *inode, long long ino)
> +{
> +	struct super_block *sb = inode->i_sb;
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	u64 block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
> +	int err, type, offset = SQUASHFS_INODE_OFFSET(ino);
> +	union squashfs_inode squashfs_ino;
> +	struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
> +	int xattr_id = SQUASHFS_INVALID_XATTR;
> +
> +	TRACE("Entered squashfs_read_inode: %d\n", ino);
> +
> +	/*
> +	 * Read inode base common to all inode types.
> +	 */
> +	err = squashfs_read_metadata(sb, sqshb_ino, &block,
> +				&offset, sizeof(*sqshb_ino));
> +	if (err < 0)
> +		goto failed_read;
> +
> +	err = squashfs_new_inode(sb, inode, sqshb_ino);
> +	if (err)
> +		goto failed_read;
> +
> +	block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
> +	offset = SQUASHFS_INODE_OFFSET(ino);
> +
> +	type = le16_to_cpu(sqshb_ino->inode_type);
> +	switch (type) {
> +	case SQUASHFS_REG_TYPE: {
> +		unsigned int frag_offset, frag;
> +		int frag_size;
> +		u64 frag_blk;
> +		struct squashfs_reg_inode *sqsh_ino = &squashfs_ino.reg;
> +
> +		TRACE("Type: SQUASHFS_REG_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +							sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		frag = le32_to_cpu(sqsh_ino->fragment);
> +		if (frag != SQUASHFS_INVALID_FRAG) {
> +			frag_offset = le32_to_cpu(sqsh_ino->offset);
> +			frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
> +			if (frag_size < 0) {
> +				err = frag_size;
> +				goto failed_read;
> +			}
> +		} else {
> +			frag_blk = SQUASHFS_INVALID_BLK;
> +			frag_size = 0;
> +			frag_offset = 0;
> +		}
> +
> +		inode->i_size = le32_to_cpu(sqsh_ino->file_size);
> +		inode->i_mode |= S_IFREG;
> +		inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
> +		squashfs_i(inode)->fragment_block = frag_blk;
> +		squashfs_i(inode)->fragment_size = frag_size;
> +		squashfs_i(inode)->fragment_offset = frag_offset;
> +		squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
> +		squashfs_i(inode)->block_list_start = block;
> +		squashfs_i(inode)->offset = offset;
> +
> +		TRACE("File inode %x:%x, start_block %llx, block_list_start "
> +			"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
> +			offset, squashfs_i(inode)->start, block, offset);
> +		break;
> +	}
> +	case SQUASHFS_LREG_TYPE: {
> +		unsigned int frag_offset, frag;
> +		int frag_size;
> +		u64 frag_blk;
> +		struct squashfs_lreg_inode *sqsh_ino = &squashfs_ino.lreg;
> +
> +		TRACE("Type: SQUASHFS_LREG_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +							sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		frag = le32_to_cpu(sqsh_ino->fragment);
> +		if (frag != SQUASHFS_INVALID_FRAG) {
> +			frag_offset = le32_to_cpu(sqsh_ino->offset);
> +			frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
> +			if (frag_size < 0) {
> +				err = frag_size;
> +				goto failed_read;
> +			}
> +		} else {
> +			frag_blk = SQUASHFS_INVALID_BLK;
> +			frag_size = 0;
> +			frag_offset = 0;
> +		}
> +
> +		xattr_id = le32_to_cpu(sqsh_ino->xattr);
> +		inode->i_size = le64_to_cpu(sqsh_ino->file_size);
> +		inode->i_mode |= S_IFREG;
> +		inode->i_blocks = (inode->i_size -
> +				le64_to_cpu(sqsh_ino->sparse) + 511) >> 9;
> +
> +		squashfs_i(inode)->fragment_block = frag_blk;
> +		squashfs_i(inode)->fragment_size = frag_size;
> +		squashfs_i(inode)->fragment_offset = frag_offset;
> +		squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block);
> +		squashfs_i(inode)->block_list_start = block;
> +		squashfs_i(inode)->offset = offset;
> +
> +		TRACE("File inode %x:%x, start_block %llx, block_list_start "
> +			"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
> +			offset, squashfs_i(inode)->start, block, offset);
> +		break;
> +	}
> +	case SQUASHFS_DIR_TYPE: {
> +		struct squashfs_dir_inode *sqsh_ino = &squashfs_ino.dir;
> +
> +		TRACE("Type: SQUASHFS_DIR_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +				sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		inode->i_size = le16_to_cpu(sqsh_ino->file_size);
> +		inode->i_mode |= S_IFDIR;
> +		squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
> +		squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
> +		squashfs_i(inode)->dir_idx_cnt = 0;
> +		squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
> +
> +		TRACE("Directory inode %x:%x, start_block %llx, offset %x\n",
> +				SQUASHFS_INODE_BLK(ino), offset,
> +				squashfs_i(inode)->start,
> +				le16_to_cpu(sqsh_ino->offset));
> +		break;
> +	}
> +	case SQUASHFS_LDIR_TYPE: {
> +		struct squashfs_ldir_inode *sqsh_ino = &squashfs_ino.ldir;
> +
> +		TRACE("Type: SQUASHFS_LDIR_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +				sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		xattr_id = le32_to_cpu(sqsh_ino->xattr);
> +		inode->i_size = le32_to_cpu(sqsh_ino->file_size);
> +		inode->i_mode |= S_IFDIR;
> +		squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
> +		squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
> +		squashfs_i(inode)->dir_idx_start = block;
> +		squashfs_i(inode)->dir_idx_offset = offset;
> +		squashfs_i(inode)->dir_idx_cnt = le16_to_cpu(sqsh_ino->i_count);
> +		squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
> +
> +		TRACE("Long directory inode %x:%x, start_block %llx, offset "
> +				"%x\n", SQUASHFS_INODE_BLK(ino), offset,
> +				squashfs_i(inode)->start,
> +				le16_to_cpu(sqsh_ino->offset));
> +		break;
> +	}
> +	case SQUASHFS_SYMLINK_TYPE:
> +	case SQUASHFS_LSYMLINK_TYPE: {
> +		struct squashfs_symlink_inode *sqsh_ino = &squashfs_ino.symlink;
> +
> +		TRACE("Type: SQUASHFS_SYMLINK_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +				sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
> +		inode->i_mode |= S_IFLNK;
> +		squashfs_i(inode)->start = block;
> +		squashfs_i(inode)->offset = offset;
> +
> +		if (type == SQUASHFS_LSYMLINK_TYPE) {
> +			__le32 xattr;
> +
> +			err = squashfs_read_metadata(sb, NULL, &block,
> +						&offset, inode->i_size);
> +			if (err < 0)
> +				goto failed_read;
> +			err = squashfs_read_metadata(sb, &xattr, &block,
> +						&offset, sizeof(xattr));
> +			if (err < 0)
> +				goto failed_read;
> +			xattr_id = le32_to_cpu(xattr);
> +		}
> +
> +		TRACE("Symbolic link inode %x:%x, start_block %llx, offset "
> +				"%x\n", SQUASHFS_INODE_BLK(ino), offset,
> +				block, offset);
> +		break;
> +	}
> +	case SQUASHFS_BLKDEV_TYPE:
> +	case SQUASHFS_CHRDEV_TYPE: {
> +		struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev;
> +		unsigned int rdev;
> +
> +		TRACE("Type: SQUASHFS_BLLDEV_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +				sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		if (type == SQUASHFS_CHRDEV_TYPE)
> +			inode->i_mode |= S_IFCHR;
> +		else
> +			inode->i_mode |= S_IFBLK;
> +		rdev = le32_to_cpu(sqsh_ino->rdev);
> +
> +		TRACE("Device inode %x:%x, rdev %x\n",
> +				SQUASHFS_INODE_BLK(ino), offset, rdev);
> +		break;
> +	}
> +	case SQUASHFS_LBLKDEV_TYPE:
> +	case SQUASHFS_LCHRDEV_TYPE: {
> +		struct squashfs_ldev_inode *sqsh_ino = &squashfs_ino.ldev;
> +		unsigned int rdev;
> +
> +		TRACE("Type: SQUASHFS_LBLLDEV_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +				sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		if (type == SQUASHFS_LCHRDEV_TYPE)
> +			inode->i_mode |= S_IFCHR;
> +		else
> +			inode->i_mode |= S_IFBLK;
> +		xattr_id = le32_to_cpu(sqsh_ino->xattr);
> +		rdev = le32_to_cpu(sqsh_ino->rdev);
> +
> +		TRACE("Device inode %x:%x, rdev %x\n",
> +				SQUASHFS_INODE_BLK(ino), offset, rdev);
> +		break;
> +	}
> +	case SQUASHFS_FIFO_TYPE:
> +	case SQUASHFS_SOCKET_TYPE: {
> +		struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;
> +
> +		TRACE("Type: SQUASHFS_FIFO_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +				sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		if (type == SQUASHFS_FIFO_TYPE)
> +			inode->i_mode |= S_IFIFO;
> +		else
> +			inode->i_mode |= S_IFSOCK;
> +		break;
> +	}
> +	case SQUASHFS_LFIFO_TYPE:
> +	case SQUASHFS_LSOCKET_TYPE: {
> +		struct squashfs_lipc_inode *sqsh_ino = &squashfs_ino.lipc;
> +
> +		TRACE("Type: SQUASHFS_LFIFO_TYPE\n");
> +		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
> +				sizeof(*sqsh_ino));
> +		if (err < 0)
> +			goto failed_read;
> +
> +		if (type == SQUASHFS_LFIFO_TYPE)
> +			inode->i_mode |= S_IFIFO;
> +		else
> +			inode->i_mode |= S_IFSOCK;
> +		xattr_id = le32_to_cpu(sqsh_ino->xattr);
> +		break;
> +	}
> +	default:
> +		ERROR("Unknown inode type %d in squashfs_iget!\n", type);
> +		return -EINVAL;
> +	}
> +
> +	squashfs_i(inode)->xattr_count = 0;
> +
> +	return 0;
> +
> +failed_read:
> +	ERROR("Unable to read inode 0x%llx\n", ino);
> +	return err;
> +}
> diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c
> new file mode 100644
> index 0000000..482fda5
> --- /dev/null
> +++ b/fs/squashfs/namei.c
> @@ -0,0 +1,346 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * namei.c
> + */
> +
> +/*
> + * This file implements code to do filename lookup in directories.
> + *
> + * Like inodes, directories are packed into compressed metadata blocks, stored
> + * in a directory table.  Directories are accessed using the start address of
> + * the metablock containing the directory and the offset into the
> + * decompressed block (<block, offset>).
> + *
> + * Directories are organised in a slightly complex way, and are not simply
> + * a list of file names.  The organisation takes advantage of the
> + * fact that (in most cases) the inodes of the files will be in the same
> + * compressed metadata block, and therefore, can share the start block.
> + * Directories are therefore organised in a two level list, a directory
> + * header containing the shared start block value, and a sequence of directory
> + * entries, each of which share the shared start block.  A new directory header
> + * is written once/if the inode start block changes.  The directory
> + * header/directory entry list is repeated as many times as necessary.
> + *
> + * Directories are sorted, and can contain a directory index to speed up
> + * file lookup.  Directory indexes store one entry per metablock, each entry
> + * storing the index/filename mapping to the first directory header
> + * in each metadata block.  Directories are sorted in alphabetical order,
> + * and at lookup the index is scanned linearly looking for the first filename
> + * alphabetically larger than the filename being looked up.  At this point the
> + * location of the metadata block the filename is in has been found.
> + * The general idea of the index is ensure only one metadata block needs to be
> + * decompressed to do a lookup irrespective of the length of the directory.
> + * This scheme has the advantage that it doesn't require extra memory overhead
> + * and doesn't require much extra storage on disk.
> + */
> +
> +#include <linux/fs.h>
> +#include <malloc.h>
> +#include <linux/string.h>
> +#include <linux/dcache.h>
> +#include <common.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs_fs_i.h"
> +#include "squashfs.h"
> +
> +/*
> + * Lookup name in the directory index, returning the location of the metadata
> + * block containing it, and the directory index this represents.
> + *
> + * If we get an error reading the index then return the part of the index
> + * (if any) we have managed to read - the index isn't essential, just
> + * quicker.
> + */
> +static int get_dir_index_using_name(struct super_block *sb,
> +			u64 *next_block, int *next_offset, u64 index_start,
> +			int index_offset, int i_count, const char *name,
> +			int len)
> +{
> +	struct squashfs_sb_info *msblk = sb->s_fs_info;
> +	int i, length = 0, err;
> +	unsigned int size;
> +	struct squashfs_dir_index *index;
> +	char *str;
> +
> +	TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
> +
> +	index = kmalloc(sizeof(*index) + SQUASHFS_NAME_LEN * 2 + 2, GFP_KERNEL);
> +	if (index == NULL) {
> +		ERROR("Failed to allocate squashfs_dir_index\n");
> +		goto out;
> +	}
> +
> +	str = &index->name[SQUASHFS_NAME_LEN + 1];
> +	strncpy(str, name, len);
> +	str[len] = '\0';
> +
> +	for (i = 0; i < i_count; i++) {
> +		err = squashfs_read_metadata(sb, index, &index_start,
> +					&index_offset, sizeof(*index));
> +		if (err < 0)
> +			break;
> +
> +
> +		size = le32_to_cpu(index->size) + 1;
> +		if (size > SQUASHFS_NAME_LEN)
> +			break;
> +
> +		err = squashfs_read_metadata(sb, index->name, &index_start,
> +					&index_offset, size);
> +		if (err < 0)
> +			break;
> +
> +		index->name[size] = '\0';
> +
> +		if (strcmp(index->name, str) > 0)
> +			break;
> +
> +		length = le32_to_cpu(index->index);
> +		*next_block = le32_to_cpu(index->start_block) +
> +					msblk->directory_table;
> +	}
> +
> +	*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
> +	kfree(index);
> +
> +out:
> +	/*
> +	 * Return index (f_pos) of the looked up metadata block.  Translate
> +	 * from internal f_pos to external f_pos which is offset by 3 because
> +	 * we invent "." and ".." entries which are not actually stored in the
> +	 * directory.
> +	 */
> +	return length + 3;
> +}
> +
> +
> +struct inode *squashfs_lookup(struct inode *dir, const char *cur_name,
> +				 unsigned int flags)
> +{
> +	const unsigned char *name = cur_name;
> +	int len = strlen(cur_name);
> +	struct inode *inode = NULL;
> +	struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info;
> +	struct squashfs_dir_header dirh;
> +	struct squashfs_dir_entry *dire;
> +	u64 block = squashfs_i(dir)->start + msblk->directory_table;
> +	int offset = squashfs_i(dir)->offset;
> +	int err, length;
> +	unsigned int dir_count, size;
> +
> +	TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset);
> +	dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
> +	if (dire == NULL) {
> +		ERROR("Failed to allocate squashfs_dir_entry\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	if (len > SQUASHFS_NAME_LEN) {
> +		err = -ENAMETOOLONG;
> +		goto failed;
> +	}
> +
> +	length = get_dir_index_using_name(dir->i_sb, &block, &offset,
> +				squashfs_i(dir)->dir_idx_start,
> +				squashfs_i(dir)->dir_idx_offset,
> +				squashfs_i(dir)->dir_idx_cnt, name, len);
> +
> +	while (length < i_size_read(dir)) {
> +		/*
> +		 * Read directory header.
> +		 */
> +		err = squashfs_read_metadata(dir->i_sb, &dirh, &block,
> +				&offset, sizeof(dirh));
> +		if (err < 0)
> +			goto read_failure;
> +
> +		length += sizeof(dirh);
> +
> +		dir_count = le32_to_cpu(dirh.count) + 1;
> +
> +		if (dir_count > SQUASHFS_DIR_COUNT)
> +			goto data_error;
> +
> +		while (dir_count--) {
> +			/*
> +			 * Read directory entry.
> +			 */
> +			err = squashfs_read_metadata(dir->i_sb, dire, &block,
> +					&offset, sizeof(*dire));
> +			if (err < 0)
> +				goto read_failure;
> +
> +			size = le16_to_cpu(dire->size) + 1;
> +
> +			/* size should never be larger than SQUASHFS_NAME_LEN */
> +			if (size > SQUASHFS_NAME_LEN)
> +				goto data_error;
> +
> +			err = squashfs_read_metadata(dir->i_sb, dire->name,
> +					&block, &offset, size);
> +			if (err < 0)
> +				goto read_failure;
> +
> +			length += sizeof(*dire) + size;
> +			dire->name[len] = '\0';
> +			if (name[0] < dire->name[0])
> +				goto exit_lookup;
> +
> +			if (len == size && !strncmp(name, dire->name, len)) {
> +				unsigned int blk, off, ino_num;
> +				long long ino;
> +				blk = le32_to_cpu(dirh.start_block);
> +				off = le16_to_cpu(dire->offset);
> +				ino_num = le32_to_cpu(dirh.inode_number) +
> +					(short) le16_to_cpu(dire->inode_number);
> +				ino = SQUASHFS_MKINODE(blk, off);
> +
> +				TRACE("calling squashfs_iget for directory "
> +					"entry %s, inode  %x:%x, %d\n", name,
> +					blk, off, ino_num);
> +
> +				inode = squashfs_iget(dir->i_sb, ino, ino_num);
> +				goto exit_lookup;
> +			}
> +		}
> +	}
> +
> +exit_lookup:
> +	kfree(dire);
> +	return inode;
> +
> +data_error:
> +	err = -EIO;
> +
> +read_failure:
> +	ERROR("Unable to read directory block [%llx:%x]\n",
> +		squashfs_i(dir)->start + msblk->directory_table,
> +		squashfs_i(dir)->offset);
> +failed:
> +	kfree(dire);
> +	return ERR_PTR(err);
> +}
> +
> +int squashfs_lookup_next(struct inode *dir, char *root_name,
> +				 char *cur_name)
> +{
> +	const unsigned char *name = root_name;
> +	int len = strlen(root_name);
> +	struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info;
> +	struct squashfs_dir_header dirh;
> +	struct squashfs_dir_entry *dire;
> +	u64 block = squashfs_i(dir)->start + msblk->directory_table;
> +	int offset = squashfs_i(dir)->offset;
> +	int err, length;
> +	unsigned int dir_count, size;
> +	int name_found = 0, real_name_found = 0;
> +
> +	TRACE("Entered squashfs_lookup_next [%llx:%x]\n", block, offset);
> +
> +	dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
> +	if (dire == NULL) {
> +		ERROR("Failed to allocate squashfs_dir_entry\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (len > SQUASHFS_NAME_LEN) {
> +		err = -ENAMETOOLONG;
> +		goto failed;
> +	}
> +
> +	length = get_dir_index_using_name(dir->i_sb, &block, &offset,
> +				squashfs_i(dir)->dir_idx_start,
> +				squashfs_i(dir)->dir_idx_offset,
> +				squashfs_i(dir)->dir_idx_cnt, name, len);
> +
> +	while (length < i_size_read(dir)) {
> +		/*
> +		 * Read directory header.
> +		 */
> +		err = squashfs_read_metadata(dir->i_sb, &dirh, &block,
> +				&offset, sizeof(dirh));
> +		if (err < 0)
> +			goto read_failure;
> +
> +		length += sizeof(dirh);
> +
> +		dir_count = le32_to_cpu(dirh.count) + 1;
> +
> +		if (dir_count > SQUASHFS_DIR_COUNT)
> +			goto data_error;
> +
> +		while (dir_count--) {
> +			/*
> +			 * Read directory entry.
> +			 */
> +			err = squashfs_read_metadata(dir->i_sb, dire, &block,
> +					&offset, sizeof(*dire));
> +			if (err < 0)
> +				goto read_failure;
> +
> +			size = le16_to_cpu(dire->size) + 1;
> +
> +			/* size should never be larger than SQUASHFS_NAME_LEN */
> +			if (size > SQUASHFS_NAME_LEN)
> +				goto data_error;
> +
> +			err = squashfs_read_metadata(dir->i_sb, dire->name,
> +					&block, &offset, size);
> +			if (err < 0)
> +				goto read_failure;
> +
> +			length += sizeof(*dire) + size;
> +			dire->name[size] = '\0';
> +
> +			if (cur_name[0] == '/')
> +				name_found = 1;
> +
> +			if (!strcmp(cur_name, name))
> +				name_found = 1;
> +
> +			if (cur_name[0] != '/'
> +			    && strlen(cur_name) == size
> +			    && !strncmp(cur_name, dire->name, size)) {
> +				name_found = 1;
> +				continue;
> +			}
> +
> +			if (name_found) {
> +				sprintf(cur_name, "%s", dire->name);
> +				real_name_found = 1;
> +				goto exit_lookup;
> +			}
> +		}
> +	}
> +
> +exit_lookup:
> +	kfree(dire);
> +	return real_name_found ? 0 : 1;
> +
> +data_error:
> +	err = -EIO;
> +
> +read_failure:
> +	ERROR("Unable to read directory block [%llx:%x]\n",
> +		squashfs_i(dir)->start + msblk->directory_table,
> +		squashfs_i(dir)->offset);
> +failed:
> +	kfree(dire);
> +	return 1;
> +}
> diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c
> new file mode 100644
> index 0000000..4eb7308
> --- /dev/null
> +++ b/fs/squashfs/page_actor.c
> @@ -0,0 +1,99 @@
> +/*
> + * Copyright (c) 2013
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/pagemap.h>
> +#include "page_actor.h"
> +
> +/*
> + * This file contains implementations of page_actor for decompressing into
> + * an intermediate buffer, and for decompressing directly into the
> + * page cache.
> + *
> + * Calling code should avoid sleeping between calls to squashfs_first_page()
> + * and squashfs_finish_page().
> + */
> +
> +/* Implementation of page_actor for decompressing into intermediate buffer */
> +static void *cache_first_page(struct squashfs_page_actor *actor)
> +{
> +	actor->next_page = 1;
> +	return actor->buffer[0];
> +}
> +
> +static void *cache_next_page(struct squashfs_page_actor *actor)
> +{
> +	if (actor->next_page == actor->pages)
> +		return NULL;
> +
> +	return actor->buffer[actor->next_page++];
> +}
> +
> +static void cache_finish_page(struct squashfs_page_actor *actor)
> +{
> +	/* empty */
> +}
> +
> +struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
> +	int pages, int length)
> +{
> +	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
> +
> +	if (actor == NULL)
> +		return NULL;
> +
> +	actor->length = length ? : pages * PAGE_CACHE_SIZE;
> +	actor->buffer = buffer;
> +	actor->pages = pages;
> +	actor->next_page = 0;
> +	actor->squashfs_first_page = cache_first_page;
> +	actor->squashfs_next_page = cache_next_page;
> +	actor->squashfs_finish_page = cache_finish_page;
> +	return actor;
> +}
> +
> +/* Implementation of page_actor for decompressing directly into page cache. */
> +static void *direct_first_page(struct squashfs_page_actor *actor)
> +{
> +	actor->next_page = 1;
> +	return actor->pageaddr = kmap_atomic(actor->page[0]);
> +}
> +
> +static void *direct_next_page(struct squashfs_page_actor *actor)
> +{
> +	if (actor->pageaddr)
> +		kunmap_atomic(actor->pageaddr);
> +
> +	return actor->pageaddr = actor->next_page == actor->pages ? NULL :
> +		kmap_atomic(actor->page[actor->next_page++]);
> +}
> +
> +static void direct_finish_page(struct squashfs_page_actor *actor)
> +{
> +	if (actor->pageaddr)
> +		kunmap_atomic(actor->pageaddr);
> +}
> +
> +struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
> +	int pages, int length)
> +{
> +	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
> +
> +	if (actor == NULL)
> +		return NULL;
> +
> +	actor->length = length ? : pages * PAGE_CACHE_SIZE;
> +	actor->page = page;
> +	actor->pages = pages;
> +	actor->next_page = 0;
> +	actor->pageaddr = NULL;
> +	actor->squashfs_first_page = direct_first_page;
> +	actor->squashfs_next_page = direct_next_page;
> +	actor->squashfs_finish_page = direct_finish_page;
> +	return actor;
> +}
> diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
> new file mode 100644
> index 0000000..c07f05f
> --- /dev/null
> +++ b/fs/squashfs/page_actor.h
> @@ -0,0 +1,53 @@
> +#ifndef PAGE_ACTOR_H
> +#define PAGE_ACTOR_H
> +/*
> + * Copyright (c) 2013
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2. See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include <malloc.h>
> +#include <linux/barebox-wrapper.h>
> +#include <linux/pagemap.h>
> +
> +struct squashfs_page_actor {
> +	void	**page;
> +	int	pages;
> +	int	length;
> +	int	next_page;
> +};
> +
> +static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
> +	int pages, int length)
> +{
> +	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
> +
> +	if (actor == NULL)
> +		return NULL;
> +
> +	actor->length = length ? : pages * PAGE_CACHE_SIZE;
> +	actor->page = page;
> +	actor->pages = pages;
> +	actor->next_page = 0;
> +	return actor;
> +}
> +
> +static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
> +{
> +	actor->next_page = 1;
> +	return actor->page[0];
> +}
> +
> +static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
> +{
> +	return actor->next_page == actor->pages ? NULL :
> +		actor->page[actor->next_page++];
> +}
> +
> +static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
> +{
> +	/* empty */
> +}
> +#endif
> diff --git a/fs/squashfs/squashfs.c b/fs/squashfs/squashfs.c
> new file mode 100644
> index 0000000..d00dee6
> --- /dev/null
> +++ b/fs/squashfs/squashfs.c
> @@ -0,0 +1,368 @@
> +#include <common.h>
> +#include <malloc.h>
> +#include <driver.h>
> +#include <init.h>
> +#include <errno.h>
> +#include <fs.h>
> +#include <xfuncs.h>
> +
> +#include <linux/fs.h>
> +#include <linux/stat.h>
> +#include <linux/pagemap.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs_fs_i.h"
> +#include "squashfs.h"
> +
> +char *squashfs_devread(struct squashfs_sb_info *fs, int byte_offset,
> +		int byte_len)
> +{
> +	ssize_t size;
> +	char *buf;
> +
> +	buf = malloc(byte_len);
> +	if (buf == NULL)
> +		return NULL;
> +
> +	size = cdev_read(fs->cdev, buf, byte_len, byte_offset, 0);
> +	if (size < 0) {
> +		dev_err(fs->dev, "read error: %s\n",
> +				strerror(-size));
> +		return NULL;
> +	}
> +
> +	return buf;
> +}
> +
> +static struct inode *duplicate_inode(struct inode *inode)
> +{
> +	struct squashfs_inode_info *ei;
> +	ei = malloc(sizeof(struct squashfs_inode_info));
> +	if (ei == NULL) {
> +		ERROR("Error allocating memory for inode\n");
> +		return NULL;
> +	}
> +	memcpy(ei, squashfs_i(inode),
> +		sizeof(struct squashfs_inode_info));
> +
> +	return &ei->vfs_inode;
> +}
> +
> +static struct inode *squashfs_findfile(struct super_block *sb,
> +		const char *filename, char *buf)
> +{
> +	char *next;
> +	char fpath[128];
> +	char *name = fpath;
> +	struct inode *inode;
> +	struct inode *t_inode = NULL;
> +
> +	strcpy(fpath, filename);
> +
> +	/* Remove all leading slashes */
> +	while (*name == '/')
> +		name++;
> +
> +	inode = duplicate_inode(sb->s_root->d_inode);
> +
> +	/*
> +	 * Handle root-directory ('/')
> +	 */
> +	if (!name || *name == '\0')
> +		return inode;
> +
> +	for (;;) {
> +		/* Extract the actual part from the pathname.  */
> +		next = strchr(name, '/');
> +		if (next) {
> +			/* Remove all leading slashes.  */
> +			while (*next == '/')
> +				*(next++) = '\0';
> +		}
> +
> +		t_inode = squashfs_lookup(inode, name, 0);
> +		if (t_inode == NULL)
> +			break;
> +
> +		/*
> +		 * Check if directory with this name exists
> +		 */
> +
> +		/* Found the node!  */
> +		if (!next || *next == '\0') {
> +			if (buf != NULL)
> +				sprintf(buf, "%s", name);
> +
> +			free(squashfs_i(inode));
> +			return t_inode;
> +		}
> +
> +		name = next;
> +
> +		free(squashfs_i(inode));
> +		inode = t_inode;
> +	}
> +
> +	free(squashfs_i(inode));
> +	return NULL;
> +}
> +
> +static int squashfs_probe(struct device_d *dev)
> +{
> +	struct fs_device_d *fsdev;
> +	struct squashfs_priv *priv;
> +	int ret;
> +
> +	fsdev = dev_to_fs_device(dev);
> +
> +	priv = xzalloc(sizeof(struct squashfs_priv));
> +	dev->priv = priv;
> +
> +	ret = fsdev_open_cdev(fsdev);
> +	if (ret)
> +		goto err_out;
> +
> +
> +	ret = squashfs_mount(fsdev, 0);
> +	if (ret) {
> +		dev_err(dev, "no valid squashfs found\n");
> +		goto err_out;
> +	}
> +
> +	return 0;
> +
> +err_out:
> +	free(priv);
> +
> +	return ret;
> +}
> +
> +static void squashfs_remove(struct device_d *dev)
> +{
> +	struct squashfs_priv *priv = dev->priv;
> +
> +	squashfs_put_super(&priv->sb);
> +	free(priv);
> +}
> +
> +static int squashfs_open(struct device_d *dev, FILE *file, const char *filename)
> +{
> +	struct squashfs_priv *priv = dev->priv;
> +	struct inode *inode;
> +	struct squashfs_page *page;
> +	int i;
> +
> +	inode = squashfs_findfile(&priv->sb, filename, NULL);
> +	if (!inode)
> +		return -ENOENT;
> +
> +	page = malloc(sizeof(struct squashfs_page));
> +	page->buf = calloc(32, sizeof(*page->buf));
> +	for (i = 0; i < 32; i++) {
> +		page->buf[i] = malloc(PAGE_CACHE_SIZE);
> +		if (page->buf[i] == NULL) {
> +			dev_err(dev, "error allocation read buffer\n");
> +			goto error;
> +		}
> +	}
> +
> +	page->data_block = 0;
> +	page->idx = 0;
> +	page->real_page.inode = inode;
> +	file->size = inode->i_size;
> +	file->priv = page;
> +
> +	return 0;
> +
> +error:
> +	for (; i > 0; --i)
> +		free(page->buf[i]);
> +
> +	free(page->buf);
> +	free(page);
> +
> +	return -ENOMEM;
> +}
> +
> +static int squashfs_close(struct device_d *dev, FILE *f)
> +{
> +	struct squashfs_page *page = f->priv;
> +	int i;
> +
> +	for (i = 0; i < 32; i++)
> +		free(page->buf[i]);
> +
> +	free(page->buf);
> +	free(squashfs_i(page->real_page.inode));
> +	free(page);
> +
> +	return 0;
> +}
> +
> +static int squashfs_read_buf(struct squashfs_page *page, int pos, void **buf)
> +{
> +	unsigned int data_block = pos / (32 * PAGE_CACHE_SIZE);
> +	unsigned int data_block_pos = pos % (32 * PAGE_CACHE_SIZE);
> +	unsigned int idx = data_block_pos / PAGE_CACHE_SIZE;
> +
> +	if (data_block != page->data_block || page->idx == 0) {
> +		page->idx = 0;
> +		page->real_page.index = data_block * 32;
> +		squashfs_readpage(NULL, &page->real_page);
> +		page->data_block = data_block;
> +	}
> +
> +	*buf = page->buf[idx];
> +
> +	return 0;
> +}
> +
> +static int squashfs_read(struct device_d *_dev, FILE *f, void *buf,
> +		size_t insize)
> +{
> +	unsigned int size = insize;
> +	unsigned int pos = f->pos;
> +	unsigned int ofs;
> +	unsigned int now;
> +	void *pagebuf;
> +	struct squashfs_page *page = f->priv;
> +
> +	/* Read till end of current buffer page */
> +	ofs = pos % PAGE_CACHE_SIZE;
> +	if (ofs) {
> +		squashfs_read_buf(page, pos, &pagebuf);
> +
> +		now = min(size, PAGE_CACHE_SIZE - ofs);
> +		memcpy(buf, pagebuf + ofs, now);
> +
> +		size -= now;
> +		pos += now;
> +		buf += now;
> +	}
> +
> +	/* Do full buffer pages */
> +	while (size >= PAGE_CACHE_SIZE) {
> +		squashfs_read_buf(page, pos, &pagebuf);
> +
> +		memcpy(buf, pagebuf, PAGE_CACHE_SIZE);
> +		size -= PAGE_CACHE_SIZE;
> +		pos += PAGE_CACHE_SIZE;
> +		buf += PAGE_CACHE_SIZE;
> +	}
> +
> +	/* And the rest */
> +	if (size) {
> +		squashfs_read_buf(page, pos, &pagebuf);
> +		memcpy(buf, pagebuf, size);
> +		size  = 0;
> +	}
> +
> +	return insize;
> +}
> +
> +static loff_t squashfs_lseek(struct device_d *dev, FILE *f, loff_t pos)
> +{
> +	f->pos = pos;
> +
> +	return pos;
> +}
> +
> +struct squashfs_dir {
> +	struct file file;
> +	struct dentry dentry;
> +	struct dentry root_dentry;
> +	struct inode inode;
> +	struct qstr nm;
> +	DIR dir;
> +	char d_name[256];
> +	char root_d_name[256];
> +};
> +
> +static DIR *squashfs_opendir(struct device_d *dev, const char *pathname)
> +{
> +	struct squashfs_priv *priv = dev->priv;
> +	struct inode *inode;
> +	struct squashfs_dir *dir;
> +	char buf[256];
> +
> +	inode = squashfs_findfile(&priv->sb, pathname, buf);
> +	if (!inode)
> +		return NULL;
> +
> +	dir = xzalloc(sizeof(struct squashfs_dir));
> +	dir->dir.priv = dir;
> +
> +	dir->root_dentry.d_inode = inode;
> +
> +	sprintf(dir->d_name, "%s", buf);
> +	sprintf(dir->root_d_name, "%s", buf);
> +
> +	return &dir->dir;
> +}
> +
> +static struct dirent *squashfs_readdir(struct device_d *dev, DIR *_dir)
> +{
> +	struct squashfs_dir *dir = _dir->priv;
> +	struct dentry *root_dentry = &dir->root_dentry;
> +
> +	if (squashfs_lookup_next(root_dentry->d_inode,
> +				 dir->root_d_name,
> +				 dir->d_name))
> +		return NULL;
> +
> +	strcpy(_dir->d.d_name, dir->d_name);
> +
> +	return &_dir->d;
> +}
> +
> +static int squashfs_closedir(struct device_d *dev, DIR *_dir)
> +{
> +	struct squashfs_dir *dir = _dir->priv;
> +
> +	free(squashfs_i(dir->root_dentry.d_inode));
> +	free(dir);
> +
> +	return 0;
> +}
> +
> +static int squashfs_stat(struct device_d *dev, const char *filename,
> +		struct stat *s)
> +{
> +	struct squashfs_priv *priv = dev->priv;
> +	struct inode *inode;
> +
> +	inode = squashfs_findfile(&priv->sb, filename, NULL);
> +	if (!inode)
> +		return -ENOENT;
> +
> +	s->st_size = inode->i_size;
> +	s->st_mode = inode->i_mode;
> +
> +	free(squashfs_i(inode));
> +
> +	return 0;
> +}
> +
> +static struct fs_driver_d squashfs_driver = {
> +	.open		= squashfs_open,
> +	.close		= squashfs_close,
> +	.read		= squashfs_read,
> +	.lseek		= squashfs_lseek,
> +	.opendir	= squashfs_opendir,
> +	.readdir	= squashfs_readdir,
> +	.closedir	= squashfs_closedir,
> +	.stat		= squashfs_stat,
> +	.drv = {
> +		.probe = squashfs_probe,
> +		.remove = squashfs_remove,
> +		.name = "squashfs",
> +	}
> +};
> +
> +static int squashfs_init(void)
> +{
> +	return register_fs_driver(&squashfs_driver);
> +}
> +
> +device_initcall(squashfs_init);
> diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
> new file mode 100644
> index 0000000..9ad6534
> --- /dev/null
> +++ b/fs/squashfs/squashfs.h
> @@ -0,0 +1,142 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * squashfs.h
> + */
> +
> +#include <printk.h>
> +#include <fs.h>
> +#include <linux/fs.h>
> +#include <linux/kernel.h>
> +
> +#define DEBUG
> +#define pgoff_t		unsigned long
> +
> +struct squashfs_priv {
> +	struct super_block sb;
> +};
> +
> +/*
> + * We "simulate" the Linux page struct much simpler here
> + */
> +struct page {
> +	pgoff_t index;
> +	void *addr;
> +	struct inode *inode;
> +};
> +
> +
> +struct squashfs_page {
> +	struct page real_page;
> +	char **buf;
> +	int idx;
> +	int data_block;
> +};
> +
> +static inline struct squashfs_page *squashfs_page(struct page *page)
> +{
> +	return container_of(page, struct squashfs_page, real_page);
> +}
> +#define TRACE(s, args...)	pr_debug("SQUASHFS: "s, ## args)
> +
> +#define ERROR(s, args...)	pr_err("SQUASHFS error: "s, ## args)
> +
> +#define WARNING(s, args...)	pr_warn("SQUASHFS: "s, ## args)
> +
> +struct inode *iget_locked_squashfs(struct super_block *sb, unsigned long ino);
> +char *squashfs_devread(struct squashfs_sb_info *fs, int byte_offset,
> +		int byte_len);
> +extern int squashfs_mount(struct fs_device_d *fsdev,
> +		int silent);
> +extern void squashfs_put_super(struct super_block *sb);
> +
> +/* block.c */
> +extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
> +				struct squashfs_page_actor *);
> +
> +/* cache.c */
> +extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
> +extern void squashfs_cache_delete(struct squashfs_cache *);
> +extern struct squashfs_cache_entry *squashfs_cache_get(struct super_block *,
> +				struct squashfs_cache *, u64, int);
> +extern void squashfs_cache_put(struct squashfs_cache_entry *);
> +extern int squashfs_copy_data(void *, struct squashfs_cache_entry *, int, int);
> +extern int squashfs_read_metadata(struct super_block *, void *, u64 *,
> +				int *, int);
> +extern struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *,
> +				u64, int);
> +extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *,
> +				u64, int);
> +extern void *squashfs_read_table(struct super_block *, u64, int);
> +
> +/* decompressor.c */
> +extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
> +extern void *squashfs_decompressor_setup(struct super_block *, unsigned short);
> +
> +/* decompressor_xxx.c */
> +extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *);
> +extern void squashfs_decompressor_destroy(struct squashfs_sb_info *);
> +extern int squashfs_decompress(struct squashfs_sb_info *, char **,
> +	int, int, int, struct squashfs_page_actor *);
> +extern int squashfs_max_decompressors(void);
> +
> +/* fragment.c */
> +extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *);
> +extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
> +				u64, u64, unsigned int);
> +/* file.c */
> +void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
> +				int);
> +extern int squashfs_readpage(struct file *file, struct page *page);
> +
> +/* file_xxx.c */
> +extern int squashfs_readpage_block(struct page *, u64, int);
> +
> +/* id.c */
> +extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
> +extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64,
> +				unsigned short);
> +
> +/* inode.c */
> +extern struct inode *squashfs_iget(struct super_block *, long long,
> +				unsigned int);
> +extern int squashfs_read_inode(struct inode *, long long);
> +
> +/*
> + * Inodes, files,  decompressor and xattr operations
> + */
> +
> +/* dir.c */
> +extern const struct file_operations squashfs_dir_ops;
> +
> +/* export.c */
> +extern const struct export_operations squashfs_export_ops;
> +
> +/* file.c */
> +extern const struct address_space_operations squashfs_aops;
> +
> +/* inode.c */
> +extern const struct inode_operations squashfs_inode_ops;
> +
> +/* namei.c */
> +extern struct inode *squashfs_lookup(struct inode *dir, const char *cur_name,
> +				 unsigned int flags);
> +extern int squashfs_lookup_next(struct inode *dir,
> +		char *root_name, char *cur_name);
> +
> +/* symlink.c */
> +extern const struct address_space_operations squashfs_symlink_aops;
> +extern const struct inode_operations squashfs_symlink_inode_ops;
> diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h
> new file mode 100644
> index 0000000..56e7285
> --- /dev/null
> +++ b/fs/squashfs/squashfs_fs.h
> @@ -0,0 +1,453 @@
> +#ifndef SQUASHFS_FS
> +#define SQUASHFS_FS
> +/*
> + * Squashfs
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * squashfs_fs.h
> + */
> +
> +#define SQUASHFS_CACHED_FRAGMENTS	3
> +#define SQUASHFS_MAJOR			4
> +#define SQUASHFS_MINOR			0
> +#define SQUASHFS_START			0
> +
> +/* size of metadata (inode and directory) blocks */
> +#define SQUASHFS_METADATA_SIZE		8192
> +
> +/* default size of block device I/O */
> +#ifdef CONFIG_SQUASHFS_4K_DEVBLK_SIZE
> +#define SQUASHFS_DEVBLK_SIZE 4096
> +#else
> +#define SQUASHFS_DEVBLK_SIZE 1024
> +#endif
> +
> +#define SQUASHFS_FILE_MAX_SIZE		1048576
> +#define SQUASHFS_FILE_MAX_LOG		20
> +
> +/* Max length of filename (not 255) */
> +#define SQUASHFS_NAME_LEN		256
> +
> +/* Max value for directory header count*/
> +#define SQUASHFS_DIR_COUNT		256
> +
> +#define SQUASHFS_INVALID_FRAG		(0xffffffffU)
> +#define SQUASHFS_INVALID_XATTR		(0xffffffffU)
> +#define SQUASHFS_INVALID_BLK		(-1LL)
> +
> +/* Filesystem flags */
> +#define SQUASHFS_NOI			0
> +#define SQUASHFS_NOD			1
> +#define SQUASHFS_NOF			3
> +#define SQUASHFS_NO_FRAG		4
> +#define SQUASHFS_ALWAYS_FRAG		5
> +#define SQUASHFS_DUPLICATE		6
> +#define SQUASHFS_EXPORT			7
> +#define SQUASHFS_COMP_OPT		10
> +
> +#define SQUASHFS_BIT(flag, bit)		((flag >> bit) & 1)
> +
> +#define SQUASHFS_UNCOMPRESSED_INODES(flags)	SQUASHFS_BIT(flags, \
> +						SQUASHFS_NOI)
> +
> +#define SQUASHFS_UNCOMPRESSED_DATA(flags)	SQUASHFS_BIT(flags, \
> +						SQUASHFS_NOD)
> +
> +#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags)	SQUASHFS_BIT(flags, \
> +						SQUASHFS_NOF)
> +
> +#define SQUASHFS_NO_FRAGMENTS(flags)		SQUASHFS_BIT(flags, \
> +						SQUASHFS_NO_FRAG)
> +
> +#define SQUASHFS_ALWAYS_FRAGMENTS(flags)	SQUASHFS_BIT(flags, \
> +						SQUASHFS_ALWAYS_FRAG)
> +
> +#define SQUASHFS_DUPLICATES(flags)		SQUASHFS_BIT(flags, \
> +						SQUASHFS_DUPLICATE)
> +
> +#define SQUASHFS_EXPORTABLE(flags)		SQUASHFS_BIT(flags, \
> +						SQUASHFS_EXPORT)
> +
> +#define SQUASHFS_COMP_OPTS(flags)		SQUASHFS_BIT(flags, \
> +						SQUASHFS_COMP_OPT)
> +
> +/* Inode types including extended types */
> +#define SQUASHFS_DIR_TYPE		1
> +#define SQUASHFS_REG_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
> +#define SQUASHFS_LSYMLINK_TYPE		10
> +#define SQUASHFS_LBLKDEV_TYPE		11
> +#define SQUASHFS_LCHRDEV_TYPE		12
> +#define SQUASHFS_LFIFO_TYPE		13
> +#define SQUASHFS_LSOCKET_TYPE		14
> +
> +/* Max type value stored in directory entry */
> +#define SQUASHFS_MAX_DIR_TYPE		7
> +
> +/* Xattr types */
> +#define SQUASHFS_XATTR_USER             0
> +#define SQUASHFS_XATTR_TRUSTED          1
> +#define SQUASHFS_XATTR_SECURITY         2
> +#define SQUASHFS_XATTR_VALUE_OOL        256
> +#define SQUASHFS_XATTR_PREFIX_MASK      0xff
> +
> +/* 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)
> +
> +#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)		((long long)(((long long) (A)\
> +					<< 16) + (B)))
> +
> +/* 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(u64))
> +
> +/* inode lookup table defines */
> +#define SQUASHFS_LOOKUP_BYTES(A)	((A) * sizeof(u64))
> +
> +#define SQUASHFS_LOOKUP_BLOCK(A)	(SQUASHFS_LOOKUP_BYTES(A) / \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A)	(SQUASHFS_LOOKUP_BYTES(A) % \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_LOOKUP_BLOCKS(A)	((SQUASHFS_LOOKUP_BYTES(A) + \
> +					SQUASHFS_METADATA_SIZE - 1) / \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_LOOKUP_BLOCK_BYTES(A)	(SQUASHFS_LOOKUP_BLOCKS(A) *\
> +					sizeof(u64))
> +
> +/* uid/gid lookup table defines */
> +#define SQUASHFS_ID_BYTES(A)		((A) * sizeof(unsigned int))
> +
> +#define SQUASHFS_ID_BLOCK(A)		(SQUASHFS_ID_BYTES(A) / \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_ID_BLOCK_OFFSET(A)	(SQUASHFS_ID_BYTES(A) % \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_ID_BLOCKS(A)		((SQUASHFS_ID_BYTES(A) + \
> +					SQUASHFS_METADATA_SIZE - 1) / \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_ID_BLOCK_BYTES(A)	(SQUASHFS_ID_BLOCKS(A) *\
> +					sizeof(u64))
> +/* xattr id lookup table defines */
> +#define SQUASHFS_XATTR_BYTES(A)		((A) * sizeof(struct squashfs_xattr_id))
> +
> +#define SQUASHFS_XATTR_BLOCK(A)		(SQUASHFS_XATTR_BYTES(A) / \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_XATTR_BLOCK_OFFSET(A)	(SQUASHFS_XATTR_BYTES(A) % \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_XATTR_BLOCKS(A)	((SQUASHFS_XATTR_BYTES(A) + \
> +					SQUASHFS_METADATA_SIZE - 1) / \
> +					SQUASHFS_METADATA_SIZE)
> +
> +#define SQUASHFS_XATTR_BLOCK_BYTES(A)	(SQUASHFS_XATTR_BLOCKS(A) *\
> +					sizeof(u64))
> +#define SQUASHFS_XATTR_BLK(A)		((unsigned int) ((A) >> 16))
> +
> +#define SQUASHFS_XATTR_OFFSET(A)	((unsigned int) ((A) & 0xffff))
> +
> +/* cached data constants for filesystem */
> +#define SQUASHFS_CACHED_BLKS		8
> +
> +/* meta index cache */
> +#define SQUASHFS_META_INDEXES	(SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
> +#define SQUASHFS_META_ENTRIES	127
> +#define SQUASHFS_META_SLOTS	8
> +
> +struct meta_entry {
> +	u64			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
> + */
> +#define ZLIB_COMPRESSION	1
> +#define LZMA_COMPRESSION	2
> +#define LZO_COMPRESSION		3
> +#define XZ_COMPRESSION		4
> +#define LZ4_COMPRESSION		5
> +
> +struct squashfs_super_block {
> +	__le32			s_magic;
> +	__le32			inodes;
> +	__le32			mkfs_time;
> +	__le32			block_size;
> +	__le32			fragments;
> +	__le16			compression;
> +	__le16			block_log;
> +	__le16			flags;
> +	__le16			no_ids;
> +	__le16			s_major;
> +	__le16			s_minor;
> +	__le64			root_inode;
> +	__le64			bytes_used;
> +	__le64			id_table_start;
> +	__le64			xattr_id_table_start;
> +	__le64			inode_table_start;
> +	__le64			directory_table_start;
> +	__le64			fragment_table_start;
> +	__le64			lookup_table_start;
> +};
> +
> +struct squashfs_dir_index {
> +	__le32			index;
> +	__le32			start_block;
> +	__le32			size;
> +	unsigned char		name[0];
> +};
> +
> +struct squashfs_base_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +};
> +
> +struct squashfs_ipc_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le32			nlink;
> +};
> +
> +struct squashfs_lipc_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le32			nlink;
> +	__le32			xattr;
> +};
> +
> +struct squashfs_dev_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le32			nlink;
> +	__le32			rdev;
> +};
> +
> +struct squashfs_ldev_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le32			nlink;
> +	__le32			rdev;
> +	__le32			xattr;
> +};
> +
> +struct squashfs_symlink_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le32			nlink;
> +	__le32			symlink_size;
> +	char			symlink[0];
> +};
> +
> +struct squashfs_reg_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le32			start_block;
> +	__le32			fragment;
> +	__le32			offset;
> +	__le32			file_size;
> +	__le16			block_list[0];
> +};
> +
> +struct squashfs_lreg_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le64			start_block;
> +	__le64			file_size;
> +	__le64			sparse;
> +	__le32			nlink;
> +	__le32			fragment;
> +	__le32			offset;
> +	__le32			xattr;
> +	__le16			block_list[0];
> +};
> +
> +struct squashfs_dir_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le32			start_block;
> +	__le32			nlink;
> +	__le16			file_size;
> +	__le16			offset;
> +	__le32			parent_inode;
> +};
> +
> +struct squashfs_ldir_inode {
> +	__le16			inode_type;
> +	__le16			mode;
> +	__le16			uid;
> +	__le16			guid;
> +	__le32			mtime;
> +	__le32			inode_number;
> +	__le32			nlink;
> +	__le32			file_size;
> +	__le32			start_block;
> +	__le32			parent_inode;
> +	__le16			i_count;
> +	__le16			offset;
> +	__le32			xattr;
> +	struct squashfs_dir_index	index[0];
> +};
> +
> +union squashfs_inode {
> +	struct squashfs_base_inode		base;
> +	struct squashfs_dev_inode		dev;
> +	struct squashfs_ldev_inode		ldev;
> +	struct squashfs_symlink_inode		symlink;
> +	struct squashfs_reg_inode		reg;
> +	struct squashfs_lreg_inode		lreg;
> +	struct squashfs_dir_inode		dir;
> +	struct squashfs_ldir_inode		ldir;
> +	struct squashfs_ipc_inode		ipc;
> +	struct squashfs_lipc_inode		lipc;
> +};
> +
> +struct squashfs_dir_entry {
> +	__le16			offset;
> +	__le16			inode_number;
> +	__le16			type;
> +	__le16			size;
> +	char			name[0];
> +};
> +
> +struct squashfs_dir_header {
> +	__le32			count;
> +	__le32			start_block;
> +	__le32			inode_number;
> +};
> +
> +struct squashfs_fragment_entry {
> +	__le64			start_block;
> +	__le32			size;
> +	unsigned int		unused;
> +};
> +
> +struct squashfs_xattr_entry {
> +	__le16			type;
> +	__le16			size;
> +	char			data[0];
> +};
> +
> +struct squashfs_xattr_val {
> +	__le32			vsize;
> +	char			value[0];
> +};
> +
> +struct squashfs_xattr_id {
> +	__le64			xattr;
> +	__le32			count;
> +	__le32			size;
> +};
> +
> +struct squashfs_xattr_id_table {
> +	__le64			xattr_table_start;
> +	__le32			xattr_ids;
> +	__le32			unused;
> +};
> +
> +#endif
> diff --git a/fs/squashfs/squashfs_fs_i.h b/fs/squashfs/squashfs_fs_i.h
> new file mode 100644
> index 0000000..ad15554
> --- /dev/null
> +++ b/fs/squashfs/squashfs_fs_i.h
> @@ -0,0 +1,52 @@
> +#ifndef SQUASHFS_FS_I
> +#define SQUASHFS_FS_I
> +/*
> + * Squashfs
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * squashfs_fs_i.h
> + */
> +
> +#include <linux/kernel.h>
> +
> +struct squashfs_inode_info {
> +	u64		start;
> +	int		offset;
> +	u64		xattr;
> +	unsigned int	xattr_size;
> +	int		xattr_count;
> +	union {
> +		struct {
> +			u64		fragment_block;
> +			int		fragment_size;
> +			int		fragment_offset;
> +			u64		block_list_start;
> +		};
> +		struct {
> +			u64		dir_idx_start;
> +			int		dir_idx_offset;
> +			int		dir_idx_cnt;
> +			int		parent;
> +		};
> +	};
> +	struct inode	vfs_inode;
> +};
> +
> +
> +static inline struct squashfs_inode_info *squashfs_i(struct inode *inode)
> +{
> +	return container_of(inode, struct squashfs_inode_info, vfs_inode);
> +}
> +#endif
> diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
> new file mode 100644
> index 0000000..2b6f81d
> --- /dev/null
> +++ b/fs/squashfs/squashfs_fs_sb.h
> @@ -0,0 +1,82 @@
> +#ifndef SQUASHFS_FS_SB
> +#define SQUASHFS_FS_SB
> +/*
> + * Squashfs
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * squashfs_fs_sb.h
> + */
> +
> +#include <linux/spinlock.h>
> +#include <linux/wait.h>
> +#include <linux/mutex.h>
> +#include <linux/barebox-wrapper.h>
> +#include "squashfs_fs.h"
> +
> +struct squashfs_cache {
> +	char			*name;
> +	int			entries;
> +	int			curr_blk;
> +	int			next_blk;
> +	int			num_waiters;
> +	int			unused;
> +	int			block_size;
> +	int			pages;
> +	spinlock_t		lock;
> +	wait_queue_head_t	wait_queue;
> +	struct squashfs_cache_entry *entry;
> +};
> +
> +struct squashfs_cache_entry {
> +	u64			block;
> +	int			length;
> +	int			refcount;
> +	u64			next_index;
> +	int			pending;
> +	int			error;
> +	int			num_waiters;
> +	wait_queue_head_t	wait_queue;
> +	struct squashfs_cache	*cache;
> +	void			**data;
> +	struct squashfs_page_actor	*actor;
> +};
> +
> +struct squashfs_sb_info {
> +	const struct squashfs_decompressor	*decompressor;
> +	int					devblksize;
> +	int					devblksize_log2;
> +	struct squashfs_cache			*block_cache;
> +	struct squashfs_cache			*fragment_cache;
> +	struct squashfs_cache			*read_page;
> +	int					next_meta_index;
> +	__le64					*id_table;
> +	__le64					*fragment_index;
> +	__le64					*xattr_id_table;
> +	struct mutex				meta_index_mutex;
> +	struct meta_index			*meta_index;
> +	struct squashfs_stream			*stream;
> +	__le64					*inode_lookup_table;
> +	u64					inode_table;
> +	u64					directory_table;
> +	u64					xattr_table;
> +	unsigned int				block_size;
> +	unsigned short				block_log;
> +	long long				bytes_used;
> +	unsigned int				inodes;
> +	int					xattr_ids;
> +	struct cdev				*cdev;
> +	struct device_d				*dev;
> +};
> +#endif
> diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
> new file mode 100644
> index 0000000..4c730e0
> --- /dev/null
> +++ b/fs/squashfs/super.c
> @@ -0,0 +1,344 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * super.c
> + */
> +
> +/*
> + * This file implements code to read the superblock, read and initialise
> + * in-memory structures at mount time, and all the VFS glue code to register
> + * the filesystem.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <errno.h>
> +#include <linux/fs.h>
> +#include <linux/kernel.h>
> +#include <linux/pagemap.h>
> +#include <linux/magic.h>
> +#include <linux/bitops.h>
> +
> +#include "page_actor.h"
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs_fs_i.h"
> +#include "squashfs.h"
> +#include "decompressor.h"
> +
> +static struct dentry *d_make_root(struct inode *inode)
> +{
> +	struct dentry *de = malloc(sizeof(struct dentry));
> +	de->d_name.name = "/";
> +	de->d_name.len = strlen("/");
> +	de->d_inode = inode;
> +	return de;
> +}
> +
> +static const struct squashfs_decompressor *supported_squashfs_filesystem(short
> +	major, short minor, short id)
> +{
> +	const struct squashfs_decompressor *decompressor;
> +
> +	if (major < SQUASHFS_MAJOR) {
> +		ERROR("Major/Minor mismatch, older Squashfs %d.%d "
> +			"filesystems are unsupported\n", major, minor);
> +		return NULL;
> +	} else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) {
> +		ERROR("Major/Minor mismatch, trying to mount newer "
> +			"%d.%d filesystem\n", major, minor);
> +		ERROR("Please update your kernel\n");
> +		return NULL;
> +	}
> +
> +	decompressor = squashfs_lookup_decompressor(id);
> +	if (!decompressor->supported) {
> +		ERROR("Filesystem uses \"%s\" compression. This is not "
> +			"supported\n", decompressor->name);
> +		return NULL;
> +	}
> +
> +	return decompressor;
> +}
> +
> +void squashfs_put_super(struct super_block *sb)
> +{
> +	if (sb->s_fs_info) {
> +		struct squashfs_sb_info *sbi = sb->s_fs_info;
> +		squashfs_cache_delete(sbi->block_cache);
> +		squashfs_cache_delete(sbi->fragment_cache);
> +		squashfs_cache_delete(sbi->read_page);
> +		squashfs_decompressor_destroy(sbi);
> +		kfree(sbi->id_table);
> +		kfree(sbi->fragment_index);
> +		kfree(sbi->meta_index);
> +		kfree(sbi->inode_lookup_table);
> +		kfree(sbi->xattr_id_table);
> +		kfree(sb->s_fs_info);
> +		sb->s_fs_info = NULL;
> +	}
> +
> +	if (sb->s_root) {
> +		kfree(squashfs_i(sb->s_root->d_inode));
> +		kfree(sb->s_root);
> +		sb->s_root = NULL;
> +	}
> +}
> +
> +static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
> +{
> +	struct squashfs_sb_info *msblk;
> +	struct fs_device_d *fsdev = (struct fs_device_d *)data;
> +	struct squashfs_super_block *sblk = NULL;
> +	struct inode *root;
> +	long long root_inode;
> +	unsigned short flags;
> +	unsigned int fragments;
> +	u64 lookup_table_start, next_table;
> +	int err;
> +
> +	TRACE("Entered squashfs_fill_superblock\n");
> +
> +	sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL);
> +	if (sb->s_fs_info == NULL) {
> +		ERROR("Failed to allocate squashfs_sb_info\n");
> +		return -ENOMEM;
> +	}
> +	msblk = sb->s_fs_info;
> +	msblk->cdev = fsdev->cdev;
> +	msblk->dev = &fsdev->dev;
> +
> +	msblk->devblksize = 1024;
> +	msblk->devblksize_log2 = ffz(~msblk->devblksize);
> +
> +	mutex_init(&msblk->meta_index_mutex);
> +	/*
> +	 * msblk->bytes_used is checked in squashfs_read_table to ensure reads
> +	 * are not beyond filesystem end.  But as we're using
> +	 * squashfs_read_table here to read the superblock (including the value
> +	 * of bytes_used) we need to set it to an initial sensible dummy value
> +	 */
> +	msblk->bytes_used = sizeof(*sblk);
> +	sblk = squashfs_read_table(sb, SQUASHFS_START, sizeof(*sblk));
> +
> +	if (IS_ERR(sblk)) {
> +		ERROR("unable to read squashfs_super_block\n");
> +		err = PTR_ERR(sblk);
> +		sblk = NULL;
> +		goto failed_mount;
> +	}
> +
> +	err = -EINVAL;
> +
> +	/* Check it is a SQUASHFS superblock */
> +	sb->s_magic = le32_to_cpu(sblk->s_magic);
> +	if (sb->s_magic != SQUASHFS_MAGIC) {
> +		if (!silent)
> +			ERROR("Can't find a SQUASHFS superblock on %pg\n",
> +						sb->s_bdev);
> +		goto failed_mount;
> +	}
> +
> +	/* Check the MAJOR & MINOR versions and lookup compression type */
> +	msblk->decompressor = supported_squashfs_filesystem(
> +			le16_to_cpu(sblk->s_major),
> +			le16_to_cpu(sblk->s_minor),
> +			le16_to_cpu(sblk->compression));
> +	if (msblk->decompressor == NULL)
> +		goto failed_mount;
> +
> +	/* Check the filesystem does not extend beyond the end of the
> +	   block device */
> +	msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
> +	if (msblk->bytes_used < 0 || msblk->bytes_used >
> +			msblk->cdev->size)
> +		goto failed_mount;
> +
> +	/* Check block size for sanity */
> +	msblk->block_size = le32_to_cpu(sblk->block_size);
> +	if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE)
> +		goto failed_mount;
> +
> +	/*
> +	 * Check the system page size is not larger than the filesystem
> +	 * block size (by default 128K).  This is currently not supported.
> +	 */
> +	if (PAGE_CACHE_SIZE > msblk->block_size) {
> +		ERROR("Page size > filesystem block size (%d).  This is "
> +			"currently not supported!\n", msblk->block_size);
> +		goto failed_mount;
> +	}
> +
> +	/* Check block log for sanity */
> +	msblk->block_log = le16_to_cpu(sblk->block_log);
> +	if (msblk->block_log > SQUASHFS_FILE_MAX_LOG)
> +		goto failed_mount;
> +
> +	/* Check that block_size and block_log match */
> +	if (msblk->block_size != (1 << msblk->block_log))
> +		goto failed_mount;
> +
> +	/* Check the root inode for sanity */
> +	root_inode = le64_to_cpu(sblk->root_inode);
> +	if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE)
> +		goto failed_mount;
> +
> +	msblk->inode_table = le64_to_cpu(sblk->inode_table_start);
> +	msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
> +	msblk->inodes = le32_to_cpu(sblk->inodes);
> +	flags = le16_to_cpu(sblk->flags);
> +
> +	TRACE("Found valid superblock on %pg\n", sb->s_bdev);
> +	TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
> +				? "un" : "");
> +	TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags)
> +				? "un" : "");
> +	TRACE("Filesystem size %lld bytes\n", msblk->bytes_used);
> +	TRACE("Block size %d\n", msblk->block_size);
> +	TRACE("Number of inodes %d\n", msblk->inodes);
> +	TRACE("Number of fragments %d\n", le32_to_cpu(sblk->fragments));
> +	TRACE("Number of ids %d\n", le16_to_cpu(sblk->no_ids));
> +	TRACE("sblk->inode_table_start %llx\n", msblk->inode_table);
> +	TRACE("sblk->directory_table_start %llx\n", msblk->directory_table);
> +	TRACE("sblk->fragment_table_start %llx\n",
> +		(u64) le64_to_cpu(sblk->fragment_table_start));
> +	TRACE("sblk->id_table_start %llx\n",
> +		(u64) le64_to_cpu(sblk->id_table_start));
> +
> +	sb->s_maxbytes = MAX_LFS_FILESIZE;
> +	sb->s_flags |= MS_RDONLY;
> +
> +	err = -ENOMEM;
> +
> +	msblk->block_cache = squashfs_cache_init("metadata",
> +			SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
> +	if (msblk->block_cache == NULL)
> +		goto failed_mount;
> +
> +	/* Allocate read_page block */
> +	msblk->read_page = squashfs_cache_init("data",
> +		squashfs_max_decompressors(), msblk->block_size);
> +	if (msblk->read_page == NULL) {
> +		ERROR("Failed to allocate read_page block\n");
> +		goto failed_mount;
> +	}
> +
> +	msblk->stream = squashfs_decompressor_setup(sb, flags);
> +	if (IS_ERR(msblk->stream)) {
> +		err = PTR_ERR(msblk->stream);
> +		msblk->stream = NULL;
> +		goto failed_mount;
> +	}
> +
> +	next_table = msblk->bytes_used;
> +
> +	/* Allocate and read id index table */
> +	msblk->id_table = squashfs_read_id_index_table(sb,
> +		le64_to_cpu(sblk->id_table_start), next_table,
> +		le16_to_cpu(sblk->no_ids));
> +	if (IS_ERR(msblk->id_table)) {
> +		ERROR("unable to read id index table\n");
> +		err = PTR_ERR(msblk->id_table);
> +		msblk->id_table = NULL;
> +		goto failed_mount;
> +	}
> +	next_table = le64_to_cpu(msblk->id_table[0]);
> +
> +	/* Handle inode lookup table */
> +	lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
> +	if (lookup_table_start == SQUASHFS_INVALID_BLK)
> +		goto handle_fragments;
> +
> +handle_fragments:
> +	fragments = le32_to_cpu(sblk->fragments);
> +	if (fragments == 0)
> +		goto check_directory_table;
> +	msblk->fragment_cache = squashfs_cache_init("fragment",
> +		SQUASHFS_CACHED_FRAGMENTS, msblk->block_size);
> +	if (msblk->fragment_cache == NULL) {
> +		err = -ENOMEM;
> +		goto failed_mount;
> +	}
> +
> +	/* Allocate and read fragment index table */
> +	msblk->fragment_index = squashfs_read_fragment_index_table(sb,
> +		le64_to_cpu(sblk->fragment_table_start), next_table, fragments);
> +	if (IS_ERR(msblk->fragment_index)) {
> +		ERROR("unable to read fragment index table\n");
> +		err = PTR_ERR(msblk->fragment_index);
> +		msblk->fragment_index = NULL;
> +		goto failed_mount;
> +	}
> +	next_table = le64_to_cpu(msblk->fragment_index[0]);
> +
> +check_directory_table:
> +	/* Sanity check directory_table */
> +	if (msblk->directory_table > next_table) {
> +		err = -EINVAL;
> +		goto failed_mount;
> +	}
> +
> +	/* Sanity check inode_table */
> +	if (msblk->inode_table >= msblk->directory_table) {
> +		err = -EINVAL;
> +		goto failed_mount;
> +	}
> +
> +	/* allocate root */
> +	root = squashfs_iget(sb, root_inode, 1);
> +	if (!root) {
> +		err = -ENOMEM;
> +		goto failed_mount;
> +	}
> +
> +	sb->s_root = d_make_root(root);
> +	if (sb->s_root == NULL) {
> +		ERROR("Root inode create failed\n");
> +		err = -ENOMEM;
> +		goto failed_mount;
> +	}
> +
> +	kfree(sblk);
> +
> +	return 0;
> +
> +failed_mount:
> +	squashfs_cache_delete(msblk->block_cache);
> +	squashfs_cache_delete(msblk->fragment_cache);
> +	squashfs_cache_delete(msblk->read_page);
> +	squashfs_decompressor_destroy(msblk);
> +	kfree(msblk->inode_lookup_table);
> +	kfree(msblk->fragment_index);
> +	kfree(msblk->id_table);
> +	kfree(msblk->xattr_id_table);
> +	kfree(sb->s_fs_info);
> +	sb->s_fs_info = NULL;
> +	kfree(sblk);
> +	return err;
> +}
> +
> +
> +int squashfs_mount(struct fs_device_d *fsdev, int silent)
> +{
> +	struct squashfs_priv *priv = fsdev->dev.priv;
> +
> +	dev_dbg(&fsdev->dev, "squashfs_mount\n");
> +
> +	if (squashfs_fill_super(&priv->sb, fsdev, silent))
> +		return -EINVAL;
> +
> +	return 0;
> +}
> diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c
> new file mode 100644
> index 0000000..2659c45
> --- /dev/null
> +++ b/fs/squashfs/xz_wrapper.c
> @@ -0,0 +1,190 @@
> +/*
> + * Squashfs - a compressed read only filesystem for Linux
> + *
> + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
> + * Phillip Lougher <phillip@xxxxxxxxxxxxxxx>
> + *
> + * 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.
> + *
> + * xz_wrapper.c
> + */
> +
> +
> +#include <linux/xz.h>
> +#include <linux/bitops.h>
> +
> +#include "squashfs_fs.h"
> +#include "squashfs_fs_sb.h"
> +#include "squashfs.h"
> +#include "decompressor.h"
> +#include "page_actor.h"
> +
> +struct squashfs_xz {
> +	struct xz_dec *state;
> +	struct xz_buf buf;
> +};
> +
> +struct disk_comp_opts {
> +	__le32 dictionary_size;
> +	__le32 flags;
> +};
> +
> +struct comp_opts {
> +	int dict_size;
> +};
> +
> +static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk,
> +	void *buff, int len)
> +{
> +	struct disk_comp_opts *comp_opts = buff;
> +	struct comp_opts *opts;
> +	int err = 0, n;
> +
> +	opts = kmalloc(sizeof(*opts), GFP_KERNEL);
> +	if (opts == NULL) {
> +		err = -ENOMEM;
> +		goto out2;
> +	}
> +
> +	if (comp_opts) {
> +		/* check compressor options are the expected length */
> +		if (len < sizeof(*comp_opts)) {
> +			err = -EIO;
> +			goto out;
> +		}
> +
> +		opts->dict_size = le32_to_cpu(comp_opts->dictionary_size);
> +
> +		/* the dictionary size should be 2^n or 2^n+2^(n+1) */
> +		n = ffs(opts->dict_size) - 1;
> +		if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) +
> +						(1 << (n + 1))) {
> +			err = -EIO;
> +			goto out;
> +		}
> +	} else
> +		/* use defaults */
> +		opts->dict_size = max_t(int, msblk->block_size,
> +							SQUASHFS_METADATA_SIZE);
> +
> +	return opts;
> +
> +out:
> +	kfree(opts);
> +out2:
> +	return ERR_PTR(err);
> +}
> +
> +
> +static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff)
> +{
> +	struct comp_opts *comp_opts = buff;
> +	struct squashfs_xz *stream;
> +	int err;
> +
> +	stream = kmalloc(sizeof(*stream), GFP_KERNEL);
> +	if (stream == NULL) {
> +		err = -ENOMEM;
> +		goto failed;
> +	}
> +
> +	stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size);
> +	if (stream->state == NULL) {
> +		kfree(stream);
> +		err = -ENOMEM;
> +		goto failed;
> +	}
> +
> +#if XZ_INTERNAL_CRC32
> +	xz_crc32_init();
> +#endif
> +
> +	return stream;
> +
> +failed:
> +	ERROR("Failed to initialise xz decompressor\n");
> +	return ERR_PTR(err);
> +}
> +
> +
> +static void squashfs_xz_free(void *strm)
> +{
> +	struct squashfs_xz *stream = strm;
> +
> +	if (stream) {
> +		xz_dec_end(stream->state);
> +		kfree(stream);
> +	}
> +}
> +
> +
> +static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm,
> +	char **bh, int b, int offset, int length,
> +	struct squashfs_page_actor *output)
> +{
> +	enum xz_ret xz_err;
> +	int avail, total = 0, k = 0;
> +	struct squashfs_xz *stream = strm;
> +
> +	xz_dec_reset(stream->state);
> +	stream->buf.in_pos = 0;
> +	stream->buf.in_size = 0;
> +	stream->buf.out_pos = 0;
> +	stream->buf.out_size = PAGE_CACHE_SIZE;
> +	stream->buf.out = squashfs_first_page(output);
> +
> +	do {
> +		if (stream->buf.in_pos == stream->buf.in_size && k < b) {
> +			avail = min(length, msblk->devblksize - offset);
> +			length -= avail;
> +			stream->buf.in = bh[k] + offset;
> +			stream->buf.in_size = avail;
> +			stream->buf.in_pos = 0;
> +			offset = 0;
> +		}
> +
> +		if (stream->buf.out_pos == stream->buf.out_size) {
> +			stream->buf.out = squashfs_next_page(output);
> +			if (stream->buf.out != NULL) {
> +				stream->buf.out_pos = 0;
> +				total += PAGE_CACHE_SIZE;
> +			}
> +		}
> +
> +		xz_err = xz_dec_run(stream->state, &stream->buf);
> +
> +		if (stream->buf.in_pos == stream->buf.in_size && k < b)
> +			kfree(bh[k++]);
> +	} while (xz_err == XZ_OK);
> +
> +	squashfs_finish_page(output);
> +
> +	if (xz_err != XZ_STREAM_END || k < b)
> +		goto out;
> +
> +	return total + stream->buf.out_pos;
> +
> +out:
> +	for (; k < b; k++)
> +		kfree(bh[k]);
> +
> +	return -EIO;
> +}
> +
> +const struct squashfs_decompressor squashfs_xz_comp_ops = {
> +	.init = squashfs_xz_init,
> +	.comp_opts = squashfs_xz_comp_opts,
> +	.free = squashfs_xz_free,
> +	.decompress = squashfs_xz_uncompress,
> +	.id = XZ_COMPRESSION,
> +	.name = "xz",
> +	.supported = 1
> +};
> -- 
> 2.1.4
> 


-- 
-- 
Best regards,
  Antony Pavlov

_______________________________________________
barebox mailing list
barebox@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/barebox





[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux