diff options
Diffstat (limited to 'release/src/linux/linux/fs/squashfs/inode.c')
-rw-r--r-- | release/src/linux/linux/fs/squashfs/inode.c | 2401 |
1 files changed, 1477 insertions, 924 deletions
diff --git a/release/src/linux/linux/fs/squashfs/inode.c b/release/src/linux/linux/fs/squashfs/inode.c index 08193482..5c17f070 100644 --- a/release/src/linux/linux/fs/squashfs/inode.c +++ b/release/src/linux/linux/fs/squashfs/inode.c @@ -1,7 +1,11 @@ /* * Squashfs - a compressed read only filesystem for Linux * - * Copyright (c) 2002, 2003, 2004 Phillip Lougher <plougher@users.sourceforge.net> + * Copyright (c) 2002, 2003, 2004, 2005, 2006 + * Phillip Lougher <phillip@lougher.org.uk> + * + * LZMA decompressor support added by Oleg I. Vdovikin + * Copyright (c) 2005 Oleg I.Vdovikin <oleg@cs.msu.su> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -16,39 +20,47 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * Squashfs - a compressed read only filesystem for Linux * * inode.c */ -#define SQUASHFS_1_0_COMPATIBILITY - #include <linux/types.h> #include <linux/squashfs_fs.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/fs.h> +#include <linux/smp_lock.h> #include <linux/locks.h> #include <linux/init.h> #include <linux/dcache.h> -#include <asm/uaccess.h> #include <linux/wait.h> -#include <asm/semaphore.h> #include <linux/zlib.h> #include <linux/blkdev.h> #include <linux/vmalloc.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> +#include <linux/autoconf.h> -#ifdef SQUASHFS_TRACE -#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) -#else -#define TRACE(s, args...) {} +#include "squashfs.h" + +#ifdef CONFIG_LZMA_FS_INFLATE + #define SQUASHFS_LZMA #endif -#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args) +#ifdef SQUASHFS_LZMA +#include "LzmaDecode.h" + +/* default LZMA settings, should be in sync with mksquashfs */ +#define LZMA_LC 3 +#define LZMA_LP 0 +#define LZMA_PB 2 + +#define LZMA_WORKSPACE_SIZE ((LZMA_BASE_SIZE + \ + (LZMA_LIT_SIZE << (LZMA_LC + LZMA_LP))) * sizeof(CProb)) + +#endif -#define SERROR(s, args...) if(!silent) printk(KERN_ERR "SQUASHFS error: "s, ## args) -#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args) static struct super_block *squashfs_read_super(struct super_block *, void *, int); static void squashfs_put_super(struct super_block *); @@ -58,26 +70,16 @@ static int squashfs_readpage(struct file *file, struct page *page); static int squashfs_readpage4K(struct file *file, struct page *page); static int squashfs_readdir(struct file *, void *, filldir_t); static struct dentry *squashfs_lookup(struct inode *, struct dentry *); -static unsigned int read_data(struct super_block *s, char *buffer, - unsigned int index, unsigned int length, int, unsigned int *next_index); -static int squashfs_get_cached_block(struct super_block *s, char *buffer, - unsigned int block, unsigned int offset, int length, - unsigned int *next_block, unsigned int *next_offset); -static struct inode *squashfs_iget(struct super_block *s, squashfs_inode inode); -static unsigned int read_blocklist(struct inode *inode, int index, int readahead_blks, - char *block_list, char **block_p, unsigned int *bsize); -static void squashfs_put_super(struct super_block *s); - -#ifdef SQUASHFS_1_0_COMPATIBILITY -static int squashfs_readpage_lessthan4K(struct file *file, struct page *page); -static struct inode *squashfs_iget_1(struct super_block *s, squashfs_inode inode); -static unsigned int read_blocklist_1(struct inode *inode, int index, int readahead_blks, - char *block_list, char **block_p, unsigned int *bsize); -#endif - -DECLARE_MUTEX(read_data_mutex); +static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode); +static long long read_blocklist(struct inode *inode, int index, + int readahead_blks, char *block_list, + unsigned short **block_p, unsigned int *bsize); +#ifdef SQUASHFS_LZMA +static unsigned char lzma_workspace[LZMA_WORKSPACE_SIZE]; +#else static z_stream stream; +#endif static DECLARE_FSTYPE_DEV(squashfs_fs_type, "squashfs", squashfs_read_super); @@ -86,140 +88,161 @@ static unsigned char squashfs_filetype_table[] = { }; static struct super_operations squashfs_ops = { - statfs: squashfs_statfs, - put_super: squashfs_put_super, + .statfs = squashfs_statfs, + .put_super = squashfs_put_super, }; -static struct address_space_operations squashfs_symlink_aops = { - readpage: squashfs_symlink_readpage +SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = { + .readpage = squashfs_symlink_readpage }; -static struct address_space_operations squashfs_aops = { - readpage: squashfs_readpage +SQSH_EXTERN struct address_space_operations squashfs_aops = { + .readpage = squashfs_readpage }; -static struct address_space_operations squashfs_aops_4K = { - readpage: squashfs_readpage4K +SQSH_EXTERN struct address_space_operations squashfs_aops_4K = { + .readpage = squashfs_readpage4K }; -#ifdef SQUASHFS_1_0_COMPATIBILITY -static struct address_space_operations squashfs_aops_lessthan4K = { - readpage: squashfs_readpage_lessthan4K -}; -#endif - static struct file_operations squashfs_dir_ops = { - read: generic_read_dir, - readdir: squashfs_readdir + .read = generic_read_dir, + .readdir = squashfs_readdir }; static struct inode_operations squashfs_dir_inode_ops = { - lookup: squashfs_lookup + .lookup = squashfs_lookup }; +static struct buffer_head *get_block_length(struct super_block *s, + int *cur_index, int *offset, int *c_byte) +{ + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + unsigned short temp; + struct buffer_head *bh; + + if (!(bh = sb_bread(s, *cur_index))) + goto out; + + if (msblk->devblksize - *offset == 1) { + if (msblk->swap) + ((unsigned char *) &temp)[1] = *((unsigned char *) + (bh->b_data + *offset)); + else + ((unsigned char *) &temp)[0] = *((unsigned char *) + (bh->b_data + *offset)); + brelse(bh); + if (!(bh = sb_bread(s, ++(*cur_index)))) + goto out; + if (msblk->swap) + ((unsigned char *) &temp)[0] = *((unsigned char *) + bh->b_data); + else + ((unsigned char *) &temp)[1] = *((unsigned char *) + bh->b_data); + *c_byte = temp; + *offset = 1; + } else { + if (msblk->swap) { + ((unsigned char *) &temp)[1] = *((unsigned char *) + (bh->b_data + *offset)); + ((unsigned char *) &temp)[0] = *((unsigned char *) + (bh->b_data + *offset + 1)); + } else { + ((unsigned char *) &temp)[0] = *((unsigned char *) + (bh->b_data + *offset)); + ((unsigned char *) &temp)[1] = *((unsigned char *) + (bh->b_data + *offset + 1)); + } + *c_byte = temp; + *offset += 2; + } + + if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { + if (*offset == msblk->devblksize) { + brelse(bh); + if (!(bh = sb_bread(s, ++(*cur_index)))) + goto out; + *offset = 0; + } + if (*((unsigned char *) (bh->b_data + *offset)) != + SQUASHFS_MARKER_BYTE) { + ERROR("Metadata block marker corrupt @ %x\n", + *cur_index); + brelse(bh); + goto out; + } + (*offset)++; + } + return bh; + +out: + return NULL; +} + -static unsigned int read_data(struct super_block *s, char *buffer, - unsigned int index, unsigned int length, int datablock, unsigned int *next_index) +SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, + long long index, unsigned int length, + long long *next_index) { - squashfs_sb_info *msBlk = &s->u.squashfs_sb; - struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> msBlk->devblksize_log2) + 2]; - unsigned int offset = index & ((1 << msBlk->devblksize_log2) - 1); - unsigned int cur_index = index >> msBlk->devblksize_log2; - int bytes, avail_bytes, b, k; + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> + msblk->devblksize_log2) + 2]; + unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); + unsigned int cur_index = index >> msblk->devblksize_log2; + int bytes, avail_bytes, b = 0, k; char *c_buffer; unsigned int compressed; unsigned int c_byte = length; - if(c_byte) { - bytes = msBlk->devblksize - offset; - if(datablock) { - c_buffer = (compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte)) ? msBlk->read_data : buffer; - c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); - } else { - c_buffer = (compressed = SQUASHFS_COMPRESSED(c_byte)) ? msBlk->read_data : buffer; - c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); - } + if (c_byte) { + bytes = msblk->devblksize - offset; + compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); + c_buffer = compressed ? msblk->read_data : buffer; + c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); - TRACE("Block @ 0x%x, %scompressed size %d\n", index, compressed ? "" : "un", (unsigned int) c_byte); + TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed + ? "" : "un", (unsigned int) c_byte); - if(!(bh[0] = sb_getblk(s, cur_index))) + if (!(bh[0] = sb_getblk(s, cur_index))) goto block_release; - for(b = 1; bytes < c_byte; b++) { - if(!(bh[b] = sb_getblk(s, ++cur_index))) + + for (b = 1; bytes < c_byte; b++) { + if (!(bh[b] = sb_getblk(s, ++cur_index))) goto block_release; - bytes += msBlk->devblksize; + bytes += msblk->devblksize; } ll_rw_block(READ, b, bh); } else { - unsigned short temp; - if(!(bh[0] = sb_bread(s, cur_index))) + if (!(bh[0] = get_block_length(s, &cur_index, &offset, + &c_byte))) goto read_failure; - if(msBlk->devblksize - offset == 1) { - if(msBlk->swap) - ((unsigned char *) &temp)[1] = *((unsigned char *) (bh[0]->b_data + offset)); - else - ((unsigned char *) &temp)[0] = *((unsigned char *) (bh[0]->b_data + offset)); - brelse(bh[0]); - if(!(bh[0] = sb_bread(s, ++cur_index))) - goto read_failure; - if(msBlk->swap) - ((unsigned char *) &temp)[0] = *((unsigned char *) bh[0]->b_data); - else - ((unsigned char *) &temp)[1] = *((unsigned char *) bh[0]->b_data); - c_byte = temp; - offset = 1; - } - else { - if(msBlk->swap) { - unsigned short temp; - ((unsigned char *) &temp)[1] = *((unsigned char *) (bh[0]->b_data + offset)); - ((unsigned char *) &temp)[0] = *((unsigned char *) (bh[0]->b_data + offset + 1)); - c_byte = temp; - } else - c_byte = *((unsigned short *) (bh[0]->b_data + offset)); - offset += 2; - } - if(SQUASHFS_CHECK_DATA(msBlk->sBlk.flags)) { - if(offset == msBlk->devblksize) { - brelse(bh[0]); - if(!(bh[0] = sb_bread(s, ++cur_index))) - goto read_failure; - offset = 0; - } - if(*((unsigned char *) (bh[0]->b_data + offset)) != SQUASHFS_MARKER_BYTE) { - ERROR("Metadata block marker corrupt @ %x\n", index); - brelse(bh[0]); - return 0; - } - offset ++; - } - - bytes = msBlk->devblksize - offset; - if(datablock) { - c_buffer = (compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte)) ? msBlk->read_data : buffer; - c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); - } else { - c_buffer = (compressed = SQUASHFS_COMPRESSED(c_byte)) ? msBlk->read_data : buffer; - c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); - } + bytes = msblk->devblksize - offset; + compressed = SQUASHFS_COMPRESSED(c_byte); + c_buffer = compressed ? msblk->read_data : buffer; + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); - TRACE("Block @ 0x%x, %scompressed size %d\n", index, compressed ? "" : "un", (unsigned int) c_byte); + TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed + ? "" : "un", (unsigned int) c_byte); - for(b = 1; bytes < c_byte; b++) { - if(!(bh[b] = sb_getblk(s, ++cur_index))) + for (b = 1; bytes < c_byte; b++) { + if (!(bh[b] = sb_getblk(s, ++cur_index))) goto block_release; - bytes += msBlk->devblksize; + bytes += msblk->devblksize; } ll_rw_block(READ, b - 1, bh + 1); } - if(compressed) - down(&read_data_mutex); + if (compressed) + down(&msblk->read_data_mutex); - for(bytes = 0, k = 0; k < b; k++) { - avail_bytes = (c_byte - bytes) > (msBlk->devblksize - offset) ? msBlk->devblksize - offset : c_byte - bytes; + for (bytes = 0, k = 0; k < b; k++) { + avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? + msblk->devblksize - offset : + c_byte - bytes; wait_on_buffer(bh[k]); + if (!buffer_uptodate(bh[k])) + goto block_release; memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes); bytes += avail_bytes; offset = 0; @@ -229,30 +252,46 @@ static unsigned int read_data(struct super_block *s, char *buffer, /* * uncompress block */ - if(compressed) { + if (compressed) { int zlib_err; +#ifdef SQUASHFS_LZMA + if ((zlib_err = LzmaDecode(lzma_workspace, + LZMA_WORKSPACE_SIZE, LZMA_LC, LZMA_LP, LZMA_PB, + c_buffer, c_byte, buffer, msblk->read_size, &bytes)) != LZMA_RESULT_OK) + { + ERROR("lzma returned unexpected result 0x%x\n", zlib_err); + bytes = 0; + } +#else stream.next_in = c_buffer; stream.avail_in = c_byte; stream.next_out = buffer; - stream.avail_out = msBlk->read_size; - if(((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || - ((zlib_err = zlib_inflate(&stream, Z_FINISH)) != Z_STREAM_END) || - ((zlib_err = zlib_inflateEnd(&stream)) != Z_OK)) { - ERROR("zlib_fs returned unexpected result 0x%x\n", zlib_err); + stream.avail_out = msblk->read_size; + + if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || + ((zlib_err = zlib_inflate(&stream, Z_FINISH)) + != Z_STREAM_END) || ((zlib_err = + zlib_inflateEnd(&stream)) != Z_OK)) { + ERROR("zlib_fs returned unexpected result 0x%x\n", + zlib_err); bytes = 0; } else bytes = stream.total_out; - up(&read_data_mutex); +#endif + + up(&msblk->read_data_mutex); } - if(next_index) - *next_index = index + c_byte + (length ? 0 : (SQUASHFS_CHECK_DATA(msBlk->sBlk.flags) ? 3 : 2)); - + if (next_index) + *next_index = index + c_byte + (length ? 0 : + (SQUASHFS_CHECK_DATA(msblk->sblk.flags) + ? 3 : 2)); return bytes; block_release: - while(--b >= 0) brelse(bh[b]); + while (--b >= 0) + brelse(bh[b]); read_failure: ERROR("sb_bread failed reading block 0x%x\n", cur_index); @@ -260,543 +299,668 @@ read_failure: } -static int squashfs_get_cached_block(struct super_block *s, char *buffer, - unsigned int block, unsigned int offset, int length, - unsigned int *next_block, unsigned int *next_offset) +SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer, + long long block, unsigned int offset, + int length, long long *next_block, + unsigned int *next_offset) { - squashfs_sb_info *msBlk = &s->u.squashfs_sb; + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; int n, i, bytes, return_length = length; - unsigned int next_index; + long long next_index; - TRACE("Entered squashfs_get_cached_block [%x:%x]\n", block, offset); + TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); - for(;;) { - for(i = 0; i < SQUASHFS_CACHED_BLKS; i++) - if(msBlk->block_cache[i].block == block) + while ( 1 ) { + for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) + if (msblk->block_cache[i].block == block) break; - down(&msBlk->block_cache_mutex); - if(i == SQUASHFS_CACHED_BLKS) { + down(&msblk->block_cache_mutex); + + if (i == SQUASHFS_CACHED_BLKS) { /* read inode header block */ - for(i = msBlk->next_cache, n = SQUASHFS_CACHED_BLKS; n ; n --, i = (i + 1) % SQUASHFS_CACHED_BLKS) - if(msBlk->block_cache[i].block != SQUASHFS_USED_BLK) + for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS; + n ; n --, i = (i + 1) % + SQUASHFS_CACHED_BLKS) + if (msblk->block_cache[i].block != + SQUASHFS_USED_BLK) break; - if(n == 0) { - up(&msBlk->block_cache_mutex); - sleep_on(&msBlk->waitq); + + if (n == 0) { + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&msblk->waitq, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + up(&msblk->block_cache_mutex); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&msblk->waitq, &wait); continue; } - msBlk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; - - if(msBlk->block_cache[i].block == SQUASHFS_INVALID_BLK) { - if(!(msBlk->block_cache[i].data = (unsigned char *) - kmalloc(SQUASHFS_METADATA_SIZE, GFP_KERNEL))) { - ERROR("Failed to allocate cache block\n"); - up(&msBlk->block_cache_mutex); - return 0; + msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; + + if (msblk->block_cache[i].block == + SQUASHFS_INVALID_BLK) { + if (!(msblk->block_cache[i].data = + kmalloc(SQUASHFS_METADATA_SIZE, + GFP_KERNEL))) { + ERROR("Failed to allocate cache" + "block\n"); + up(&msblk->block_cache_mutex); + goto out; } } - msBlk->block_cache[i].block = SQUASHFS_USED_BLK; - up(&msBlk->block_cache_mutex); - if(!(msBlk->block_cache[i].length = read_data(s, msBlk->block_cache[i].data, block, 0, 0, - &next_index))) { - ERROR("Unable to read cache block [%x:%x]\n", block, offset); - return 0; + msblk->block_cache[i].block = SQUASHFS_USED_BLK; + up(&msblk->block_cache_mutex); + + if (!(msblk->block_cache[i].length = + squashfs_read_data(s, + msblk->block_cache[i].data, + block, 0, &next_index))) { + ERROR("Unable to read cache block [%llx:%x]\n", + block, offset); + goto out; } - down(&msBlk->block_cache_mutex); - wake_up(&msBlk->waitq); - msBlk->block_cache[i].block = block; - msBlk->block_cache[i].next_index = next_index; - TRACE("Read cache block [%x:%x]\n", block, offset); + + down(&msblk->block_cache_mutex); + wake_up(&msblk->waitq); + msblk->block_cache[i].block = block; + msblk->block_cache[i].next_index = next_index; + TRACE("Read cache block [%llx:%x]\n", block, offset); } - if(msBlk->block_cache[i].block != block) { - up(&msBlk->block_cache_mutex); + if (msblk->block_cache[i].block != block) { + up(&msblk->block_cache_mutex); continue; } - if((bytes = msBlk->block_cache[i].length - offset) >= length) { - if(buffer) - memcpy(buffer, msBlk->block_cache[i].data + offset, length); - if(msBlk->block_cache[i].length - offset == length) { - *next_block = msBlk->block_cache[i].next_index; + if ((bytes = msblk->block_cache[i].length - offset) >= length) { + if (buffer) + memcpy(buffer, msblk->block_cache[i].data + + offset, length); + if (msblk->block_cache[i].length - offset == length) { + *next_block = msblk->block_cache[i].next_index; *next_offset = 0; } else { *next_block = block; *next_offset = offset + length; } - - up(&msBlk->block_cache_mutex); - return return_length; + up(&msblk->block_cache_mutex); + goto finish; } else { - if(buffer) { - memcpy(buffer, msBlk->block_cache[i].data + offset, bytes); + if (buffer) { + memcpy(buffer, msblk->block_cache[i].data + + offset, bytes); buffer += bytes; } - block = msBlk->block_cache[i].next_index; - up(&msBlk->block_cache_mutex); + block = msblk->block_cache[i].next_index; + up(&msblk->block_cache_mutex); length -= bytes; offset = 0; } } + +finish: + return return_length; +out: + return 0; } -static int get_fragment_location(struct super_block *s, unsigned int fragment, unsigned int *fragment_start_block, unsigned int *fragment_size) +static int get_fragment_location(struct super_block *s, unsigned int fragment, + long long *fragment_start_block, + unsigned int *fragment_size) { - squashfs_sb_info *msBlk = &s->u.squashfs_sb; - unsigned int start_block = msBlk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + long long start_block = + msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); - squashfs_fragment_entry fragment_entry; + struct squashfs_fragment_entry fragment_entry; - if(msBlk->swap) { - squashfs_fragment_entry sfragment_entry; + if (msblk->swap) { + struct squashfs_fragment_entry sfragment_entry; - if(!squashfs_get_cached_block(s, (char *) &sfragment_entry, start_block, offset, - sizeof(sfragment_entry), &start_block, &offset)) - return 0; + if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, + start_block, offset, + sizeof(sfragment_entry), &start_block, + &offset)) + goto out; SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); } else - if(!squashfs_get_cached_block(s, (char *) &fragment_entry, start_block, offset, - sizeof(fragment_entry), &start_block, &offset)) - return 0; + if (!squashfs_get_cached_block(s, (char *) &fragment_entry, + start_block, offset, + sizeof(fragment_entry), &start_block, + &offset)) + goto out; *fragment_start_block = fragment_entry.start_block; *fragment_size = fragment_entry.size; return 1; + +out: + return 0; } -void release_cached_fragment(squashfs_sb_info *msBlk, struct squashfs_fragment_cache *fragment) +SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct + squashfs_fragment_cache *fragment) { - down(&msBlk->fragment_mutex); + down(&msblk->fragment_mutex); fragment->locked --; - wake_up(&msBlk->fragment_wait_queue); - up(&msBlk->fragment_mutex); + wake_up(&msblk->fragment_wait_queue); + up(&msblk->fragment_mutex); } -struct squashfs_fragment_cache *get_cached_fragment(struct super_block *s, unsigned int start_block, int length) +SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block + *s, long long start_block, + int length) { int i, n; - squashfs_sb_info *msBlk = &s->u.squashfs_sb; - - for(;;) { - down(&msBlk->fragment_mutex); - for(i = 0; i < SQUASHFS_CACHED_FRAGMENTS && msBlk->fragment[i].block != start_block; i++); - if(i == SQUASHFS_CACHED_FRAGMENTS) { - for(i = msBlk->next_fragment, n = SQUASHFS_CACHED_FRAGMENTS; - n && msBlk->fragment[i].locked; n--, i = (i + 1) % SQUASHFS_CACHED_FRAGMENTS); - - if(n == 0) { - up(&msBlk->fragment_mutex); - sleep_on(&msBlk->fragment_wait_queue); + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + + while ( 1 ) { + down(&msblk->fragment_mutex); + + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && + msblk->fragment[i].block != start_block; i++); + + if (i == SQUASHFS_CACHED_FRAGMENTS) { + for (i = msblk->next_fragment, n = + SQUASHFS_CACHED_FRAGMENTS; n && + msblk->fragment[i].locked; n--, i = (i + 1) % + SQUASHFS_CACHED_FRAGMENTS); + + if (n == 0) { + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&msblk->fragment_wait_queue, + &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + up(&msblk->fragment_mutex); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&msblk->fragment_wait_queue, + &wait); continue; } - msBlk->next_fragment = (msBlk->next_fragment + 1) % SQUASHFS_CACHED_FRAGMENTS; + msblk->next_fragment = (msblk->next_fragment + 1) % + SQUASHFS_CACHED_FRAGMENTS; - if(msBlk->fragment[i].data == NULL) - if(!(msBlk->fragment[i].data = (unsigned char *) - kmalloc(SQUASHFS_FILE_MAX_SIZE, GFP_KERNEL))) { - ERROR("Failed to allocate fragment cache block\n"); - up(&msBlk->fragment_mutex); - return NULL; + if (msblk->fragment[i].data == NULL) + if (!(msblk->fragment[i].data = SQUASHFS_ALLOC + (SQUASHFS_FILE_MAX_SIZE))) { + ERROR("Failed to allocate fragment " + "cache block\n"); + up(&msblk->fragment_mutex); + goto out; } - msBlk->fragment[i].block = SQUASHFS_INVALID_BLK; - msBlk->fragment[i].locked = 1; - up(&msBlk->fragment_mutex); - if(!(msBlk->fragment[i].length = read_data(s, msBlk->fragment[i].data, start_block, length, - 1, NULL))) { - ERROR("Unable to read fragment cache block [%x]\n", start_block); - msBlk->fragment[i].locked = 0; - return NULL; + msblk->fragment[i].block = SQUASHFS_INVALID_BLK; + msblk->fragment[i].locked = 1; + up(&msblk->fragment_mutex); + + if (!(msblk->fragment[i].length = squashfs_read_data(s, + msblk->fragment[i].data, + start_block, length, NULL))) { + ERROR("Unable to read fragment cache block " + "[%llx]\n", start_block); + msblk->fragment[i].locked = 0; + goto out; } - msBlk->fragment[i].block = start_block; - TRACE("New fragment %d, start block %d, locked %d\n", i, msBlk->fragment[i].block, msBlk->fragment[i].locked); - return &msBlk->fragment[i]; + + msblk->fragment[i].block = start_block; + TRACE("New fragment %d, start block %lld, locked %d\n", + i, msblk->fragment[i].block, + msblk->fragment[i].locked); + break; } - msBlk->fragment[i].locked ++; - up(&msBlk->fragment_mutex); - - TRACE("Got fragment %d, start block %d, locked %d\n", i, msBlk->fragment[i].block, msBlk->fragment[i].locked); - return &msBlk->fragment[i]; + msblk->fragment[i].locked++; + up(&msblk->fragment_mutex); + TRACE("Got fragment %d, start block %lld, locked %d\n", i, + msblk->fragment[i].block, + msblk->fragment[i].locked); + break; } + + return &msblk->fragment[i]; + +out: + return NULL; } -#ifdef SQUASHFS_1_0_COMPATIBILITY -static struct inode *squashfs_iget_1(struct super_block *s, squashfs_inode inode) +static struct inode *squashfs_new_inode(struct super_block *s, + struct squashfs_base_inode_header *inodeb) { + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; struct inode *i = new_inode(s); - squashfs_sb_info *msBlk = &s->u.squashfs_sb; - squashfs_super_block *sBlk = &msBlk->sBlk; - unsigned int block = SQUASHFS_INODE_BLK(inode) + sBlk->inode_table_start; - unsigned int offset = SQUASHFS_INODE_OFFSET(inode); - unsigned int next_block, next_offset; - squashfs_base_inode_header_1 inodeb; - - TRACE("Entered squashfs_iget_1\n"); - if(msBlk->swap) { - squashfs_base_inode_header_1 sinodeb; + if (i) { + i->i_ino = inodeb->inode_number; + i->i_mtime = inodeb->mtime; + i->i_atime = inodeb->mtime; + i->i_ctime = inodeb->mtime; + i->i_uid = msblk->uid[inodeb->uid]; + i->i_mode = inodeb->mode; + i->i_size = 0; + if (inodeb->guid == SQUASHFS_GUIDS) + i->i_gid = i->i_uid; + else + i->i_gid = msblk->guid[inodeb->guid]; + } - if(!squashfs_get_cached_block(s, (char *) &sinodeb, block, offset, - sizeof(sinodeb), &next_block, &next_offset)) - goto failed_read; - SQUASHFS_SWAP_BASE_INODE_HEADER_1(&inodeb, &sinodeb, sizeof(sinodeb)); - } else - if(!squashfs_get_cached_block(s, (char *) &inodeb, block, offset, - sizeof(inodeb), &next_block, &next_offset)) - goto failed_read; + return i; +} - i->i_nlink = 1; - i->i_mtime = sBlk->mkfs_time; - i->i_atime = sBlk->mkfs_time; - i->i_ctime = sBlk->mkfs_time; +static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode) +{ + struct inode *i; + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; + long long block = SQUASHFS_INODE_BLK(inode) + + sblk->inode_table_start; + unsigned int offset = SQUASHFS_INODE_OFFSET(inode); + long long next_block; + unsigned int next_offset; + union squashfs_inode_header id, sid; + struct squashfs_base_inode_header *inodeb = &id.base, + *sinodeb = &sid.base; - if(inodeb.inode_type != SQUASHFS_IPC_TYPE) - i->i_uid = msBlk->uid[((inodeb.inode_type - 1) / SQUASHFS_TYPES) * 16 + inodeb.uid]; - i->i_ino = SQUASHFS_MK_VFS_INODE(block - sBlk->inode_table_start, offset); + TRACE("Entered squashfs_iget\n"); - i->i_mode = inodeb.mode; + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) sinodeb, block, + offset, sizeof(*sinodeb), &next_block, + &next_offset)) + goto failed_read; + SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, + sizeof(*sinodeb)); + } else + if (!squashfs_get_cached_block(s, (char *) inodeb, block, + offset, sizeof(*inodeb), &next_block, + &next_offset)) + goto failed_read; - switch(inodeb.inode_type == SQUASHFS_IPC_TYPE ? SQUASHFS_IPC_TYPE : (inodeb.inode_type - 1) % SQUASHFS_TYPES + 1) { + switch(inodeb->inode_type) { case SQUASHFS_FILE_TYPE: { - squashfs_reg_inode_header_1 inodep; - - if(msBlk->swap) { - squashfs_reg_inode_header_1 sinodep; - - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) + unsigned int frag_size; + long long frag_blk; + struct squashfs_reg_inode_header *inodep = &id.reg; + struct squashfs_reg_inode_header *sinodep = &sid.reg; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_REG_INODE_HEADER_1(&inodep, &sinodep); + SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) goto failed_read; - i->i_size = inodep.file_size; + frag_blk = SQUASHFS_INVALID_BLK; + if (inodep->fragment != SQUASHFS_INVALID_FRAG && + !get_fragment_location(s, + inodep->fragment, &frag_blk, &frag_size)) + goto failed_read; + + if((i = squashfs_new_inode(s, inodeb)) == NULL) + goto failed_read1; + + i->i_nlink = 1; + i->i_size = inodep->file_size; i->i_fop = &generic_ro_fops; - if(sBlk->block_size > 4096) + i->i_mode |= S_IFREG; + i->i_blocks = ((i->i_size - 1) >> 9) + 1; + i->i_blksize = PAGE_CACHE_SIZE; + SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; + SQUASHFS_I(i)->u.s1.fragment_size = frag_size; + SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->u.s1.block_list_start = next_block; + SQUASHFS_I(i)->offset = next_offset; + if (sblk->block_size > 4096) i->i_data.a_ops = &squashfs_aops; - else if(sBlk->block_size == 4096) - i->i_data.a_ops = &squashfs_aops_4K; else - i->i_data.a_ops = &squashfs_aops_lessthan4K; + i->i_data.a_ops = &squashfs_aops_4K; + + TRACE("File inode %x:%x, start_block %llx, " + "block_list_start %llx, offset %x\n", + SQUASHFS_INODE_BLK(inode), offset, + inodep->start_block, next_block, + next_offset); + break; + } + case SQUASHFS_LREG_TYPE: { + unsigned int frag_size; + long long frag_blk; + struct squashfs_lreg_inode_header *inodep = &id.lreg; + struct squashfs_lreg_inode_header *sinodep = &sid.lreg; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) + goto failed_read; + SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); + } else + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) + goto failed_read; + + frag_blk = SQUASHFS_INVALID_BLK; + if (inodep->fragment != SQUASHFS_INVALID_FRAG && + !get_fragment_location(s, + inodep->fragment, &frag_blk, &frag_size)) + goto failed_read; + + if((i = squashfs_new_inode(s, inodeb)) == NULL) + goto failed_read1; + + i->i_nlink = inodep->nlink; + i->i_size = inodep->file_size; + i->i_fop = &generic_ro_fops; i->i_mode |= S_IFREG; - i->i_mtime = inodep.mtime; - i->i_atime = inodep.mtime; - i->i_ctime = inodep.mtime; i->i_blocks = ((i->i_size - 1) >> 9) + 1; i->i_blksize = PAGE_CACHE_SIZE; - i->u.squashfs_i.fragment_start_block = SQUASHFS_INVALID_BLK; - i->u.squashfs_i.fragment_offset = 0; - i->u.squashfs_i.start_block = inodep.start_block; - i->u.squashfs_i.block_list_start = next_block; - i->u.squashfs_i.offset = next_offset; - TRACE("File inode %x:%x, start_block %x, block_list_start %x, offset %x\n", - SQUASHFS_INODE_BLK(inode), offset, inodep.start_block, next_block, next_offset); + SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; + SQUASHFS_I(i)->u.s1.fragment_size = frag_size; + SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->u.s1.block_list_start = next_block; + SQUASHFS_I(i)->offset = next_offset; + if (sblk->block_size > 4096) + i->i_data.a_ops = &squashfs_aops; + else + i->i_data.a_ops = &squashfs_aops_4K; + + TRACE("File inode %x:%x, start_block %llx, " + "block_list_start %llx, offset %x\n", + SQUASHFS_INODE_BLK(inode), offset, + inodep->start_block, next_block, + next_offset); break; } case SQUASHFS_DIR_TYPE: { - squashfs_dir_inode_header_1 inodep; + struct squashfs_dir_inode_header *inodep = &id.dir; + struct squashfs_dir_inode_header *sinodep = &sid.dir; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) + goto failed_read; + SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); + } else + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) + goto failed_read; - if(msBlk->swap) { - squashfs_dir_inode_header_1 sinodep; + if((i = squashfs_new_inode(s, inodeb)) == NULL) + goto failed_read1; - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) + i->i_nlink = inodep->nlink; + i->i_size = inodep->file_size; + i->i_op = &squashfs_dir_inode_ops; + i->i_fop = &squashfs_dir_ops; + i->i_mode |= S_IFDIR; + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->offset = inodep->offset; + SQUASHFS_I(i)->u.s2.directory_index_count = 0; + SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; + + TRACE("Directory inode %x:%x, start_block %x, offset " + "%x\n", SQUASHFS_INODE_BLK(inode), + offset, inodep->start_block, + inodep->offset); + break; + } + case SQUASHFS_LDIR_TYPE: { + struct squashfs_ldir_inode_header *inodep = &id.ldir; + struct squashfs_ldir_inode_header *sinodep = &sid.ldir; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_DIR_INODE_HEADER_1(&inodep, &sinodep); + SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, + sinodep); } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) goto failed_read; - i->i_size = inodep.file_size; + if((i = squashfs_new_inode(s, inodeb)) == NULL) + goto failed_read1; + + i->i_nlink = inodep->nlink; + i->i_size = inodep->file_size; i->i_op = &squashfs_dir_inode_ops; i->i_fop = &squashfs_dir_ops; i->i_mode |= S_IFDIR; - i->i_mtime = inodep.mtime; - i->i_atime = inodep.mtime; - i->i_ctime = inodep.mtime; - i->u.squashfs_i.start_block = inodep.start_block; - i->u.squashfs_i.offset = inodep.offset; - TRACE("Directory inode %x:%x, start_block %x, offset %x\n", SQUASHFS_INODE_BLK(inode), offset, - inodep.start_block, inodep.offset); + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->offset = inodep->offset; + SQUASHFS_I(i)->u.s2.directory_index_start = next_block; + SQUASHFS_I(i)->u.s2.directory_index_offset = + next_offset; + SQUASHFS_I(i)->u.s2.directory_index_count = + inodep->i_count; + SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; + + TRACE("Long directory inode %x:%x, start_block %x, " + "offset %x\n", + SQUASHFS_INODE_BLK(inode), offset, + inodep->start_block, inodep->offset); break; } case SQUASHFS_SYMLINK_TYPE: { - squashfs_symlink_inode_header_1 inodep; + struct squashfs_symlink_inode_header *inodep = + &id.symlink; + struct squashfs_symlink_inode_header *sinodep = + &sid.symlink; - if(msBlk->swap) { - squashfs_symlink_inode_header_1 sinodep; - - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(&inodep, &sinodep); + SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, + sinodep); } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) goto failed_read; - i->i_size = inodep.symlink_size; + if((i = squashfs_new_inode(s, inodeb)) == NULL) + goto failed_read1; + + i->i_nlink = inodep->nlink; + i->i_size = inodep->symlink_size; i->i_op = &page_symlink_inode_operations; i->i_data.a_ops = &squashfs_symlink_aops; i->i_mode |= S_IFLNK; - i->u.squashfs_i.start_block = next_block; - i->u.squashfs_i.offset = next_offset; - TRACE("Symbolic link inode %x:%x, start_block %x, offset %x\n", - SQUASHFS_INODE_BLK(inode), offset, next_block, next_offset); + SQUASHFS_I(i)->start_block = next_block; + SQUASHFS_I(i)->offset = next_offset; + + TRACE("Symbolic link inode %x:%x, start_block %llx, " + "offset %x\n", + SQUASHFS_INODE_BLK(inode), offset, + next_block, next_offset); break; } case SQUASHFS_BLKDEV_TYPE: case SQUASHFS_CHRDEV_TYPE: { - squashfs_dev_inode_header_1 inodep; - - if(msBlk->swap) { - squashfs_dev_inode_header_1 sinodep; - - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) + struct squashfs_dev_inode_header *inodep = &id.dev; + struct squashfs_dev_inode_header *sinodep = &sid.dev; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_DEV_INODE_HEADER_1(&inodep, &sinodep); + SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) goto failed_read; - i->i_size = 0; - i->i_mode |= (inodeb.inode_type == SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : S_IFBLK; - init_special_inode(i, i->i_mode, inodep.rdev); - TRACE("Device inode %x:%x, rdev %x\n", SQUASHFS_INODE_BLK(inode), offset, inodep.rdev); - break; - } - case SQUASHFS_IPC_TYPE: { - squashfs_ipc_inode_header_1 inodep; + if ((i = squashfs_new_inode(s, inodeb)) == NULL) + goto failed_read1; - if(msBlk->swap) { - squashfs_ipc_inode_header_1 sinodep; + i->i_nlink = inodep->nlink; + i->i_mode |= (inodeb->inode_type == + SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : + S_IFBLK; + init_special_inode(i, i->i_mode, inodep->rdev); - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) + TRACE("Device inode %x:%x, rdev %x\n", + SQUASHFS_INODE_BLK(inode), offset, + inodep->rdev); + break; + } + case SQUASHFS_FIFO_TYPE: + case SQUASHFS_SOCKET_TYPE: { + struct squashfs_ipc_inode_header *inodep = &id.ipc; + struct squashfs_ipc_inode_header *sinodep = &sid.ipc; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_IPC_INODE_HEADER_1(&inodep, &sinodep); + SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) goto failed_read; - i->i_size = 0; - i->i_mode |= (inodep.type == SQUASHFS_FIFO_TYPE) ? S_IFIFO : S_IFSOCK; - i->i_uid = msBlk->uid[inodep.offset * 16 + inodeb.uid]; + if ((i = squashfs_new_inode(s, inodeb)) == NULL) + goto failed_read1; + + i->i_nlink = inodep->nlink; + i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) + ? S_IFIFO : S_IFSOCK; init_special_inode(i, i->i_mode, 0); break; } default: - ERROR("Unknown inode type %d in squashfs_iget!\n", inodeb.inode_type); - goto failed_read1; + ERROR("Unknown inode type %d in squashfs_iget!\n", + inodeb->inode_type); + goto failed_read1; } - if(inodeb.guid == 15) - i->i_gid = i->i_uid; - else - i->i_gid = msBlk->guid[inodeb.guid]; - + insert_inode_hash(i); return i; failed_read: - ERROR("Unable to read inode [%x:%x]\n", block, offset); + ERROR("Unable to read inode [%llx:%x]\n", block, offset); failed_read1: return NULL; } -#endif -static struct inode *squashfs_iget(struct super_block *s, squashfs_inode inode) +int read_fragment_index_table(struct super_block *s) { - struct inode *i = new_inode(s); - squashfs_sb_info *msBlk = &s->u.squashfs_sb; - squashfs_super_block *sBlk = &msBlk->sBlk; - unsigned int block = SQUASHFS_INODE_BLK(inode) + sBlk->inode_table_start; - unsigned int offset = SQUASHFS_INODE_OFFSET(inode); - unsigned int next_block, next_offset; - squashfs_base_inode_header inodeb; - - TRACE("Entered squashfs_iget\n"); - - if(msBlk->swap) { - squashfs_base_inode_header sinodeb; + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; - if(!squashfs_get_cached_block(s, (char *) &sinodeb, block, offset, - sizeof(sinodeb), &next_block, &next_offset)) - goto failed_read; - SQUASHFS_SWAP_BASE_INODE_HEADER(&inodeb, &sinodeb, sizeof(sinodeb)); - } else - if(!squashfs_get_cached_block(s, (char *) &inodeb, block, offset, - sizeof(inodeb), &next_block, &next_offset)) - goto failed_read; - - i->i_nlink = 1; - - i->i_mtime = sBlk->mkfs_time; - i->i_atime = sBlk->mkfs_time; - i->i_ctime = sBlk->mkfs_time; - - if(inodeb.inode_type != SQUASHFS_IPC_TYPE) - i->i_uid = msBlk->uid[((inodeb.inode_type - 1) / SQUASHFS_TYPES) * 16 + inodeb.uid]; - i->i_ino = SQUASHFS_MK_VFS_INODE(block - sBlk->inode_table_start, offset); - - i->i_mode = inodeb.mode; - - switch(inodeb.inode_type) { - case SQUASHFS_FILE_TYPE: { - squashfs_reg_inode_header inodep; - - if(msBlk->swap) { - squashfs_reg_inode_header sinodep; - - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) - goto failed_read; - SQUASHFS_SWAP_REG_INODE_HEADER(&inodep, &sinodep); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; + if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES + (sblk->fragments), GFP_KERNEL))) { + ERROR("Failed to allocate uid/gid table\n"); + return 0; + } + + if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) && + !squashfs_read_data(s, (char *) + msblk->fragment_index, + sblk->fragment_table_start, + SQUASHFS_FRAGMENT_INDEX_BYTES + (sblk->fragments) | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { + ERROR("unable to read fragment index table\n"); + return 0; + } - i->u.squashfs_i.fragment_start_block = SQUASHFS_INVALID_BLK; - if(inodep.fragment != SQUASHFS_INVALID_BLK && !get_fragment_location(s, inodep.fragment, - &i->u.squashfs_i.fragment_start_block, &i->u.squashfs_i.fragment_size)) - goto failed_read; + if (msblk->swap) { + int i; + long long fragment; - i->u.squashfs_i.fragment_offset = inodep.offset; - i->i_size = inodep.file_size; - i->i_fop = &generic_ro_fops; - if(sBlk->block_size > 4096) - i->i_data.a_ops = &squashfs_aops; - else - i->i_data.a_ops = &squashfs_aops_4K; - i->i_mode |= S_IFREG; - i->i_mtime = inodep.mtime; - i->i_atime = inodep.mtime; - i->i_ctime = inodep.mtime; - i->i_blocks = ((i->i_size - 1) >> 9) + 1; - i->i_blksize = PAGE_CACHE_SIZE; - i->u.squashfs_i.start_block = inodep.start_block; - i->u.squashfs_i.block_list_start = next_block; - i->u.squashfs_i.offset = next_offset; - TRACE("File inode %x:%x, start_block %x, block_list_start %x, offset %x fragment_index %x fragment_offset %x\n", - SQUASHFS_INODE_BLK(inode), offset, inodep.start_block, next_block, next_offset, inodep.fragment, inodep.offset); - break; + for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); + i++) { + SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), + &msblk->fragment_index[i], 1); + msblk->fragment_index[i] = fragment; } - case SQUASHFS_DIR_TYPE: { - squashfs_dir_inode_header inodep; + } - if(msBlk->swap) { - squashfs_dir_inode_header sinodep; + return 1; +} - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) - goto failed_read; - SQUASHFS_SWAP_DIR_INODE_HEADER(&inodep, &sinodep); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; - i->i_size = inodep.file_size; - i->i_op = &squashfs_dir_inode_ops; - i->i_fop = &squashfs_dir_ops; - i->i_mode |= S_IFDIR; - i->i_mtime = inodep.mtime; - i->i_atime = inodep.mtime; - i->i_ctime = inodep.mtime; - i->u.squashfs_i.start_block = inodep.start_block; - i->u.squashfs_i.offset = inodep.offset; - TRACE("Directory inode %x:%x, start_block %x, offset %x\n", SQUASHFS_INODE_BLK(inode), offset, - inodep.start_block, inodep.offset); - break; +static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) +{ + struct squashfs_super_block *sblk = &msblk->sblk; + + msblk->iget = squashfs_iget; + msblk->read_blocklist = read_blocklist; + msblk->read_fragment_index_table = read_fragment_index_table; + + if (sblk->s_major == 1) { + if (!squashfs_1_0_supported(msblk)) { + SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " + "are unsupported\n"); + SERROR("Please recompile with " + "Squashfs 1.0 support enabled\n"); + return 0; } - case SQUASHFS_SYMLINK_TYPE: { - squashfs_symlink_inode_header inodep; - - if(msBlk->swap) { - squashfs_symlink_inode_header sinodep; - - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) - goto failed_read; - SQUASHFS_SWAP_SYMLINK_INODE_HEADER(&inodep, &sinodep); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; - - i->i_size = inodep.symlink_size; - i->i_op = &page_symlink_inode_operations; - i->i_data.a_ops = &squashfs_symlink_aops; - i->i_mode |= S_IFLNK; - i->u.squashfs_i.start_block = next_block; - i->u.squashfs_i.offset = next_offset; - TRACE("Symbolic link inode %x:%x, start_block %x, offset %x\n", - SQUASHFS_INODE_BLK(inode), offset, next_block, next_offset); - break; - } - case SQUASHFS_BLKDEV_TYPE: - case SQUASHFS_CHRDEV_TYPE: { - squashfs_dev_inode_header inodep; - - if(msBlk->swap) { - squashfs_dev_inode_header sinodep; - - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) - goto failed_read; - SQUASHFS_SWAP_DEV_INODE_HEADER(&inodep, &sinodep); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; - - i->i_size = 0; - i->i_mode |= (inodeb.inode_type == SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : S_IFBLK; - init_special_inode(i, i->i_mode, inodep.rdev); - TRACE("Device inode %x:%x, rdev %x\n", SQUASHFS_INODE_BLK(inode), offset, inodep.rdev); - break; - } - case SQUASHFS_FIFO_TYPE: - case SQUASHFS_SOCKET_TYPE: { - i->i_size = 0; - i->i_mode |= (inodeb.inode_type == SQUASHFS_FIFO_TYPE) ? S_IFIFO : S_IFSOCK; - init_special_inode(i, i->i_mode, 0); - break; - } - default: - ERROR("Unknown inode type %d in squashfs_iget!\n", inodeb.inode_type); - goto failed_read1; + } else if (sblk->s_major == 2) { + if (!squashfs_2_0_supported(msblk)) { + SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " + "are unsupported\n"); + SERROR("Please recompile with " + "Squashfs 2.0 support enabled\n"); + return 0; + } + } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > + SQUASHFS_MINOR) { + SERROR("Major/Minor mismatch, trying to mount newer %d.%d " + "filesystem\n", sblk->s_major, sblk->s_minor); + SERROR("Please update your kernel\n"); + return 0; } - - if(inodeb.guid == SQUASHFS_GUIDS) - i->i_gid = i->i_uid; - else - i->i_gid = msBlk->guid[inodeb.guid]; - - return i; -failed_read: - ERROR("Unable to read inode [%x:%x]\n", block, offset); - -failed_read1: - return NULL; + return 1; } @@ -804,221 +968,204 @@ static struct super_block *squashfs_read_super(struct super_block *s, void *data, int silent) { kdev_t dev = s->s_dev; - squashfs_sb_info *msBlk = &s->u.squashfs_sb; - squashfs_super_block *sBlk = &msBlk->sBlk; + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; int i; - - TRACE("Entered squashfs_read_superblock\n"); - - msBlk->devblksize = get_hardsect_size(dev); - if(msBlk->devblksize < BLOCK_SIZE) - msBlk->devblksize = BLOCK_SIZE; - msBlk->devblksize_log2 = ffz(~msBlk->devblksize); - set_blocksize(dev, msBlk->devblksize); - s->s_blocksize = msBlk->devblksize; - s->s_blocksize_bits = msBlk->devblksize_log2; - - init_MUTEX(&msBlk->read_page_mutex); - init_MUTEX(&msBlk->block_cache_mutex); - init_MUTEX(&msBlk->fragment_mutex); + struct inode *root; + + msblk->devblksize = get_hardsect_size(dev); + if(msblk->devblksize < BLOCK_SIZE) + msblk->devblksize = BLOCK_SIZE; + msblk->devblksize_log2 = ffz(~msblk->devblksize); + set_blocksize(dev, msblk->devblksize); + s->s_blocksize = msblk->devblksize; + s->s_blocksize_bits = msblk->devblksize_log2; + + init_MUTEX(&msblk->read_data_mutex); + init_MUTEX(&msblk->read_page_mutex); + init_MUTEX(&msblk->block_cache_mutex); + init_MUTEX(&msblk->fragment_mutex); - init_waitqueue_head(&msBlk->waitq); - init_waitqueue_head(&msBlk->fragment_wait_queue); + init_waitqueue_head(&msblk->waitq); + init_waitqueue_head(&msblk->fragment_wait_queue); - if(!read_data(s, (char *) sBlk, SQUASHFS_START, sizeof(squashfs_super_block) | SQUASHFS_COMPRESSED_BIT, 0, NULL)) { + if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, + sizeof(struct squashfs_super_block) | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { SERROR("unable to read superblock\n"); goto failed_mount; } /* Check it is a SQUASHFS superblock */ - msBlk->swap = 0; - if((s->s_magic = sBlk->s_magic) != SQUASHFS_MAGIC) { - if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP) { - squashfs_super_block sblk; - WARNING("Mounting a different endian SQUASHFS filesystem on %s\n", bdevname(dev)); - SQUASHFS_SWAP_SUPER_BLOCK(&sblk, sBlk); - memcpy(sBlk, &sblk, sizeof(squashfs_super_block)); - msBlk->swap = 1; + msblk->swap = 0; + if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { + if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { + struct squashfs_super_block ssblk; + + WARNING("Mounting a different endian SQUASHFS " + "filesystem on %s\n", bdevname(dev)); + + SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); + memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); + msblk->swap = 1; } else { - SERROR("Can't find a SQUASHFS superblock on %s\n", bdevname(dev)); + SERROR("Can't find a SQUASHFS superblock on %s\n", + bdevname(dev)); goto failed_mount; } } /* Check the MAJOR & MINOR versions */ -#ifdef SQUASHFS_1_0_COMPATIBILITY - if((sBlk->s_major != 1) && (sBlk->s_major != 2 || sBlk->s_minor > SQUASHFS_MINOR)) { - SERROR("Major/Minor mismatch, filesystem is (%d:%d), I support (1 : x) or (2 : <= %d)\n", - sBlk->s_major, sBlk->s_minor, SQUASHFS_MINOR); + if(!supported_squashfs_filesystem(msblk, silent)) goto failed_mount; - } - if(sBlk->s_major == 1) - sBlk->block_size = sBlk->block_size_1; -#else - if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) { - SERROR("Major/Minor mismatch, filesystem is (%d:%d), I support (%d: <= %d)\n", - sBlk->s_major, sBlk->s_minor, SQUASHFS_MAJOR, SQUASHFS_MINOR); - goto failed_mount; - } -#endif TRACE("Found valid superblock on %s\n", bdevname(dev)); - TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : ""); - TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : ""); - TRACE("Check data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(sBlk->flags) ? "" : "not"); - TRACE("Filesystem size %d bytes\n", sBlk->bytes_used); - TRACE("Block size %d\n", sBlk->block_size); - TRACE("Number of inodes %d\n", sBlk->inodes); - if(sBlk->s_major > 1) - TRACE("Number of fragments %d\n", sBlk->fragments); - TRACE("Number of uids %d\n", sBlk->no_uids); - TRACE("Number of gids %d\n", sBlk->no_guids); - TRACE("sBlk->inode_table_start %x\n", sBlk->inode_table_start); - TRACE("sBlk->directory_table_start %x\n", sBlk->directory_table_start); - if(sBlk->s_major > 1) - TRACE("sBlk->fragment_table_start %x\n", sBlk->fragment_table_start); - TRACE("sBlk->uid_start %x\n", sBlk->uid_start); + TRACE("Inodes are %scompressed\n", + SQUASHFS_UNCOMPRESSED_INODES + (sblk->flags) ? "un" : ""); + TRACE("Data is %scompressed\n", + SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) + ? "un" : ""); + TRACE("Check data is %s present in the filesystem\n", + SQUASHFS_CHECK_DATA(sblk->flags) ? + "" : "not"); + TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); + TRACE("Block size %d\n", sblk->block_size); + TRACE("Number of inodes %d\n", sblk->inodes); + if (sblk->s_major > 1) + TRACE("Number of fragments %d\n", sblk->fragments); + TRACE("Number of uids %d\n", sblk->no_uids); + TRACE("Number of gids %d\n", sblk->no_guids); + TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); + TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); + if (sblk->s_major > 1) + TRACE("sblk->fragment_table_start %llx\n", + sblk->fragment_table_start); + TRACE("sblk->uid_start %llx\n", sblk->uid_start); s->s_flags |= MS_RDONLY; s->s_op = &squashfs_ops; /* Init inode_table block pointer array */ - if(!(msBlk->block_cache = (squashfs_cache *) kmalloc(sizeof(squashfs_cache) * SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { + if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * + SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { ERROR("Failed to allocate block cache\n"); goto failed_mount; } - for(i = 0; i < SQUASHFS_CACHED_BLKS; i++) - msBlk->block_cache[i].block = SQUASHFS_INVALID_BLK; + for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) + msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; - msBlk->next_cache = 0; + msblk->next_cache = 0; /* Allocate read_data block */ - msBlk->read_size = (sBlk->block_size < SQUASHFS_METADATA_SIZE) ? SQUASHFS_METADATA_SIZE : sBlk->block_size; - if(!(msBlk->read_data = (char *) kmalloc(msBlk->read_size, GFP_KERNEL))) { + msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ? + SQUASHFS_METADATA_SIZE : + sblk->block_size; + + if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) { ERROR("Failed to allocate read_data block\n"); - goto failed_mount1; + goto failed_mount; } /* Allocate read_page block */ - if(sBlk->block_size > PAGE_CACHE_SIZE && - !(msBlk->read_page = (char *) kmalloc(sBlk->block_size, GFP_KERNEL))) { + if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { ERROR("Failed to allocate read_page block\n"); - goto failed_mount2; + goto failed_mount; } /* Allocate uid and gid tables */ - if(!(msBlk->uid = (squashfs_uid *) kmalloc((sBlk->no_uids + - sBlk->no_guids) * sizeof(squashfs_uid), GFP_KERNEL))) { + if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * + sizeof(unsigned int), GFP_KERNEL))) { ERROR("Failed to allocate uid/gid table\n"); - goto failed_mount3; + goto failed_mount; } - msBlk->guid = msBlk->uid + sBlk->no_uids; + msblk->guid = msblk->uid + sblk->no_uids; - if(msBlk->swap) { - squashfs_uid suid[sBlk->no_uids + sBlk->no_guids]; - - if(!read_data(s, (char *) &suid, sBlk->uid_start, ((sBlk->no_uids + sBlk->no_guids) * - sizeof(squashfs_uid)) | SQUASHFS_COMPRESSED_BIT, 0, NULL)) { - SERROR("unable to read uid/gid table\n"); - goto failed_mount4; + if (msblk->swap) { + unsigned int suid[sblk->no_uids + sblk->no_guids]; + + if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, + ((sblk->no_uids + sblk->no_guids) * + sizeof(unsigned int)) | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { + ERROR("unable to read uid/gid table\n"); + goto failed_mount; } - SQUASHFS_SWAP_DATA(msBlk->uid, suid, (sBlk->no_uids + sBlk->no_guids), (sizeof(squashfs_uid) * 8)); + + SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids + + sblk->no_guids), (sizeof(unsigned int) * 8)); } else - if(!read_data(s, (char *) msBlk->uid, sBlk->uid_start, ((sBlk->no_uids + sBlk->no_guids) * - sizeof(squashfs_uid)) | SQUASHFS_COMPRESSED_BIT, 0, NULL)) { - SERROR("unable to read uid/gid table\n"); - goto failed_mount4; + if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, + ((sblk->no_uids + sblk->no_guids) * + sizeof(unsigned int)) | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { + ERROR("unable to read uid/gid table\n"); + goto failed_mount; } -#ifdef SQUASHFS_1_0_COMPATIBILITY - if(sBlk->s_major == 1) { - msBlk->iget = squashfs_iget_1; - msBlk->read_blocklist = read_blocklist_1; - msBlk->fragment = (struct squashfs_fragment_cache *) msBlk->fragment_index = NULL; + if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) goto allocate_root; - } -#endif - msBlk->iget = squashfs_iget; - msBlk->read_blocklist = read_blocklist; - if(!(msBlk->fragment = (struct squashfs_fragment_cache *) kmalloc(sizeof(struct squashfs_fragment_cache) * SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { + if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) * + SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { ERROR("Failed to allocate fragment block cache\n"); - goto failed_mount4; + goto failed_mount; } - for(i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { - msBlk->fragment[i].locked = 0; - msBlk->fragment[i].block = SQUASHFS_INVALID_BLK; - msBlk->fragment[i].data = NULL; + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { + msblk->fragment[i].locked = 0; + msblk->fragment[i].block = SQUASHFS_INVALID_BLK; + msblk->fragment[i].data = NULL; } - msBlk->next_fragment = 0; + msblk->next_fragment = 0; /* Allocate fragment index table */ - if(!(msBlk->fragment_index = (squashfs_fragment_index *) kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments), GFP_KERNEL))) { - ERROR("Failed to allocate uid/gid table\n"); - goto failed_mount5; - } - - if(SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments) && - !read_data(s, (char *) msBlk->fragment_index, sBlk->fragment_table_start, - SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments) | SQUASHFS_COMPRESSED_BIT, 0, NULL)) { - SERROR("unable to read fragment index table\n"); - goto failed_mount6; - } - - if(msBlk->swap) { - int i; - squashfs_fragment_index fragment; - - for(i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments); i++) { - SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), &msBlk->fragment_index[i], 1); - msBlk->fragment_index[i] = fragment; - } - } + if(msblk->read_fragment_index_table(s) == 0) + goto failed_mount; -#ifdef SQUASHFS_1_0_COMPATIBILITY allocate_root: -#endif - if(!(s->s_root = d_alloc_root((msBlk->iget)(s, sBlk->root_inode)))) { + if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL) + goto failed_mount; + + if ((s->s_root = d_alloc_root(root)) == NULL) { ERROR("Root inode create failed\n"); - goto failed_mount5; + iput(root); + goto failed_mount; } TRACE("Leaving squashfs_read_super\n"); return s; -failed_mount6: - kfree(msBlk->fragment_index); -failed_mount5: - kfree(msBlk->fragment); -failed_mount4: - kfree(msBlk->uid); -failed_mount3: - kfree(msBlk->read_page); -failed_mount2: - kfree(msBlk->read_data); -failed_mount1: - kfree(msBlk->block_cache); failed_mount: + kfree(msblk->fragment_index); + kfree(msblk->fragment); + kfree(msblk->uid); + kfree(msblk->read_page); + kfree(msblk->read_data); + kfree(msblk->block_cache); + kfree(msblk->fragment_index_2); return NULL; } static int squashfs_statfs(struct super_block *s, struct statfs *buf) { - squashfs_super_block *sBlk = &s->u.squashfs_sb.sBlk; + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; TRACE("Entered squashfs_statfs\n"); + buf->f_type = SQUASHFS_MAGIC; - buf->f_bsize = sBlk->block_size; - buf->f_blocks = ((sBlk->bytes_used - 1) >> sBlk->block_log) + 1; + buf->f_bsize = sblk->block_size; + buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1; buf->f_bfree = buf->f_bavail = 0; - buf->f_files = sBlk->inodes; + buf->f_files = sblk->inodes; buf->f_ffree = 0; buf->f_namelen = SQUASHFS_NAME_LEN; + return 0; } @@ -1027,35 +1174,41 @@ static int squashfs_symlink_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; int index = page->index << PAGE_CACHE_SHIFT, length, bytes; - int block = inode->u.squashfs_i.start_block; - int offset = inode->u.squashfs_i.offset; - void *pageaddr = kmap(page); - - TRACE("Entered squashfs_symlink_readpage, page index %x, start block %x, offset %x\n", - (unsigned int) page->index, inode->u.squashfs_i.start_block, inode->u.squashfs_i.offset); - - for(length = 0; length < index; length += bytes) { - if(!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, block, offset, - PAGE_CACHE_SIZE, &block, &offset))) { - ERROR("Unable to read symbolic link [%x:%x]\n", block, offset); + long long block = SQUASHFS_I(inode)->start_block; + int offset = SQUASHFS_I(inode)->offset; + void *pageaddr = kmap(page); + + TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " + "%llx, offset %x\n", page->index, + SQUASHFS_I(inode)->start_block, + SQUASHFS_I(inode)->offset); + + for (length = 0; length < index; length += bytes) { + if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, + block, offset, PAGE_CACHE_SIZE, &block, + &offset))) { + ERROR("Unable to read symbolic link [%llx:%x]\n", block, + offset); goto skip_read; } } - if(length != index) { + if (length != index) { ERROR("(squashfs_symlink_readpage) length != index\n"); bytes = 0; goto skip_read; } - bytes = (inode->i_size - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : inode->i_size - length; - if(!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, offset, bytes, &block, &offset))) - ERROR("Unable to read symbolic link [%x:%x]\n", block, offset); + bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : + i_size_read(inode) - length; + + if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, + offset, bytes, &block, &offset))) + ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); skip_read: memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); kunmap(page); - flush_dcache_page(page); SetPageUptodate(page); UnlockPage(page); @@ -1063,155 +1216,358 @@ skip_read: } -#define SIZE 256 +struct meta_index *locate_meta_index(struct inode *inode, int index, int offset) +{ + struct meta_index *meta = NULL; + struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb; + int i; + + down(&msblk->meta_index_mutex); + + TRACE("locate_meta_index: index %d, offset %d\n", index, offset); + + if(msblk->meta_index == NULL) + goto not_allocated; + + for (i = 0; i < SQUASHFS_META_NUMBER; i ++) + if (msblk->meta_index[i].inode_number == inode->i_ino && + msblk->meta_index[i].offset >= offset && + msblk->meta_index[i].offset <= index && + msblk->meta_index[i].locked == 0) { + TRACE("locate_meta_index: entry %d, offset %d\n", i, + msblk->meta_index[i].offset); + meta = &msblk->meta_index[i]; + offset = meta->offset; + } -#ifdef SQUASHFS_1_0_COMPATIBILITY -static unsigned int read_blocklist_1(struct inode *inode, int index, int readahead_blks, - char *block_list, char **block_p, unsigned int *bsize) + if (meta) + meta->locked = 1; + +not_allocated: + up(&msblk->meta_index_mutex); + + return meta; +} + + +struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) { - squashfs_sb_info *msBlk = &inode->i_sb->u.squashfs_sb; - unsigned short *block_listp; - int i = 0; - int block_ptr = inode->u.squashfs_i.block_list_start; - int offset = inode->u.squashfs_i.offset; - int block = inode->u.squashfs_i.start_block; - - for(;;) { - int blocks = (index + readahead_blks - i); - if(blocks > (SIZE >> 1)) { - if((index - i) <= (SIZE >> 1)) - blocks = index - i; - else - blocks = SIZE >> 1; + struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb; + struct meta_index *meta = NULL; + int i; + + down(&msblk->meta_index_mutex); + + TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); + + if(msblk->meta_index == NULL) { + if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) * + SQUASHFS_META_NUMBER, GFP_KERNEL))) { + ERROR("Failed to allocate meta_index\n"); + goto failed; } + for(i = 0; i < SQUASHFS_META_NUMBER; i++) { + msblk->meta_index[i].inode_number = 0; + msblk->meta_index[i].locked = 0; + } + msblk->next_meta_index = 0; + } - if(msBlk->swap) { - unsigned char sblock_list[SIZE]; - if(!squashfs_get_cached_block(inode->i_sb, (char *) sblock_list, block_ptr, offset, blocks << 1, &block_ptr, &offset)) { - ERROR("Unable to read block list [%d:%x]\n", block_ptr, offset); - return 0; - } - SQUASHFS_SWAP_SHORTS(((unsigned short *)block_list), ((unsigned short *)sblock_list), blocks); - } else - if(!squashfs_get_cached_block(inode->i_sb, (char *) block_list, block_ptr, offset, blocks << 1, &block_ptr, &offset)) { - ERROR("Unable to read block list [%d:%x]\n", block_ptr, offset); - return 0; - } - for(block_listp = (unsigned short *) block_list; i < index && blocks; i ++, block_listp ++, blocks --) - block += SQUASHFS_COMPRESSED_SIZE(*block_listp); - if(blocks >= readahead_blks) - break; + for(i = SQUASHFS_META_NUMBER; i && + msblk->meta_index[msblk->next_meta_index].locked; i --) + msblk->next_meta_index = (msblk->next_meta_index + 1) % + SQUASHFS_META_NUMBER; + + if(i == 0) { + TRACE("empty_meta_index: failed!\n"); + goto failed; } - if(bsize) - *bsize = SQUASHFS_COMPRESSED_SIZE(*block_listp) | (!SQUASHFS_COMPRESSED(*block_listp) ? SQUASHFS_COMPRESSED_BIT_BLOCK : 0); - else - (unsigned short *) *block_p = block_listp; - return block; + TRACE("empty_meta_index: returned meta entry %d, %p\n", + msblk->next_meta_index, + &msblk->meta_index[msblk->next_meta_index]); + + meta = &msblk->meta_index[msblk->next_meta_index]; + msblk->next_meta_index = (msblk->next_meta_index + 1) % + SQUASHFS_META_NUMBER; + + meta->inode_number = inode->i_ino; + meta->offset = offset; + meta->skip = skip; + meta->entries = 0; + meta->locked = 1; + +failed: + up(&msblk->meta_index_mutex); + return meta; } -#endif +void release_meta_index(struct inode *inode, struct meta_index *meta) +{ + meta->locked = 0; +} + -static unsigned int read_blocklist(struct inode *inode, int index, int readahead_blks, - char *block_list, char **block_p, unsigned int *bsize) +static int read_block_index(struct super_block *s, int blocks, char *block_list, + long long *start_block, int *offset) { - squashfs_sb_info *msBlk = &inode->i_sb->u.squashfs_sb; + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; unsigned int *block_listp; - int i = 0; - int block_ptr = inode->u.squashfs_i.block_list_start; - int offset = inode->u.squashfs_i.offset; - int block = inode->u.squashfs_i.start_block; - - for(;;) { - int blocks = (index + readahead_blks - i); - if(blocks > (SIZE >> 2)) { - if((index - i) <= (SIZE >> 2)) - blocks = index - i; - else - blocks = SIZE >> 2; + int block = 0; + + if (msblk->swap) { + char sblock_list[blocks << 2]; + + if (!squashfs_get_cached_block(s, sblock_list, *start_block, + *offset, blocks << 2, start_block, offset)) { + ERROR("Unable to read block list [%llx:%x]\n", + *start_block, *offset); + goto failure; + } + SQUASHFS_SWAP_INTS(((unsigned int *)block_list), + ((unsigned int *)sblock_list), blocks); + } else + if (!squashfs_get_cached_block(s, block_list, *start_block, + *offset, blocks << 2, start_block, offset)) { + ERROR("Unable to read block list [%llx:%x]\n", + *start_block, *offset); + goto failure; } - if(msBlk->swap) { - unsigned char sblock_list[SIZE]; - if(!squashfs_get_cached_block(inode->i_sb, (char *) sblock_list, block_ptr, offset, blocks << 2, &block_ptr, &offset)) { - ERROR("Unable to read block list [%d:%x]\n", block_ptr, offset); - return 0; - } - SQUASHFS_SWAP_INTS(((unsigned int *)block_list), ((unsigned int *)sblock_list), blocks); - } else - if(!squashfs_get_cached_block(inode->i_sb, (char *) block_list, block_ptr, offset, blocks << 2, &block_ptr, &offset)) { - ERROR("Unable to read block list [%d:%x]\n", block_ptr, offset); - return 0; + for (block_listp = (unsigned int *) block_list; blocks; + block_listp++, blocks --) + block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); + + return block; + +failure: + return -1; +} + + +#define SIZE 256 + +static inline int calculate_skip(int blocks) { + int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); + return skip >= 7 ? 7 : skip + 1; +} + + +static int get_meta_index(struct inode *inode, int index, + long long *index_block, int *index_offset, + long long *data_block, char *block_list) +{ + struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; + int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); + int offset = 0; + struct meta_index *meta; + struct meta_entry *meta_entry; + long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; + int cur_offset = SQUASHFS_I(inode)->offset; + long long cur_data_block = SQUASHFS_I(inode)->start_block; + int i; + + index /= SQUASHFS_META_INDEXES * skip; + + while ( offset < index ) { + meta = locate_meta_index(inode, index, offset + 1); + + if (meta == NULL) { + if ((meta = empty_meta_index(inode, offset + 1, + skip)) == NULL) + goto all_done; + } else { + offset = index < meta->offset + meta->entries ? index : + meta->offset + meta->entries - 1; + meta_entry = &meta->meta_entry[offset - meta->offset]; + cur_index_block = meta_entry->index_block + sblk->inode_table_start; + cur_offset = meta_entry->offset; + cur_data_block = meta_entry->data_block; + TRACE("get_meta_index: offset %d, meta->offset %d, " + "meta->entries %d\n", offset, meta->offset, + meta->entries); + TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" + " data_block 0x%llx\n", cur_index_block, + cur_offset, cur_data_block); + } + + for (i = meta->offset + meta->entries; i <= index && + i < meta->offset + SQUASHFS_META_ENTRIES; i++) { + int blocks = skip * SQUASHFS_META_INDEXES; + + while (blocks) { + int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : + blocks; + int res = read_block_index(inode->i_sb, block, + block_list, &cur_index_block, + &cur_offset); + + if (res == -1) + goto failed; + + cur_data_block += res; + blocks -= block; } - for(block_listp = (unsigned int *) block_list; i < index && blocks; i ++, block_listp ++, blocks --) - block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); - if(blocks >= readahead_blks) - break; + + meta_entry = &meta->meta_entry[i - meta->offset]; + meta_entry->index_block = cur_index_block - sblk->inode_table_start; + meta_entry->offset = cur_offset; + meta_entry->data_block = cur_data_block; + meta->entries ++; + offset ++; + } + + TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", + meta->offset, meta->entries); + + release_meta_index(inode, meta); } - *bsize = *block_listp; +all_done: + *index_block = cur_index_block; + *index_offset = cur_offset; + *data_block = cur_data_block; + + return offset * SQUASHFS_META_INDEXES * skip; + +failed: + release_meta_index(inode, meta); + return -1; +} + + +static long long read_blocklist(struct inode *inode, int index, + int readahead_blks, char *block_list, + unsigned short **block_p, unsigned int *bsize) +{ + long long block_ptr; + int offset; + long long block; + int res = get_meta_index(inode, index, &block_ptr, &offset, &block, + block_list); + + TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" + " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, + block); + + if(res == -1) + goto failure; + + index -= res; + + while ( index ) { + int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; + int res = read_block_index(inode->i_sb, blocks, block_list, + &block_ptr, &offset); + if (res == -1) + goto failure; + block += res; + index -= blocks; + } + + if (read_block_index(inode->i_sb, 1, block_list, + &block_ptr, &offset) == -1) + goto failure; + *bsize = *((unsigned int *) block_list); + return block; + +failure: + return 0; } static int squashfs_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; - squashfs_sb_info *msBlk = &inode->i_sb->u.squashfs_sb; - squashfs_super_block *sBlk = &msBlk->sBlk; + struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; unsigned char block_list[SIZE]; - unsigned int bsize, block, i = 0, bytes = 0, byte_offset = 0; - int index = page->index >> (sBlk->block_log - PAGE_CACHE_SHIFT); - void *pageaddr = kmap(page); - struct squashfs_fragment_cache *fragment; - char *data_ptr = msBlk->read_page; + long long block; + unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; + int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); + void *pageaddr; + struct squashfs_fragment_cache *fragment = NULL; + char *data_ptr = msblk->read_page; - int mask = (1 << (sBlk->block_log - PAGE_CACHE_SHIFT)) - 1; + int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; int start_index = page->index & ~mask; int end_index = start_index | mask; - TRACE("Entered squashfs_readpage, page index %x, start block %x\n", (unsigned int) page->index, - inode->u.squashfs_i.start_block); + TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", + page->index, + SQUASHFS_I(inode)->start_block); - if(inode->u.squashfs_i.fragment_start_block == SQUASHFS_INVALID_BLK || index < (inode->i_size >> sBlk->block_log)) { - if((block = (msBlk->read_blocklist)(inode, index, 1, block_list, NULL, &bsize)) == 0) + if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT)) + goto skip_read; + + if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK + || index < (i_size_read(inode) >> + sblk->block_log)) { + if ((block = (msblk->read_blocklist)(inode, index, 1, + block_list, NULL, &bsize)) == 0) goto skip_read; - down(&msBlk->read_page_mutex); - if(!(bytes = read_data(inode->i_sb, msBlk->read_page, block, bsize, 1, NULL))) { - ERROR("Unable to read page, block %x, size %x\n", block, bsize); - up(&msBlk->read_page_mutex); + down(&msblk->read_page_mutex); + + if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, + block, bsize, NULL))) { + ERROR("Unable to read page, block %llx, size %x\n", block, + bsize); + up(&msblk->read_page_mutex); goto skip_read; } } else { - if((fragment = get_cached_fragment(inode->i_sb, inode->u.squashfs_i.fragment_start_block, inode->u.squashfs_i.fragment_size)) == NULL) { - ERROR("Unable to read page, block %x, size %x\n", inode->u.squashfs_i.fragment_start_block, (int) inode->u.squashfs_i.fragment_size); + if ((fragment = get_cached_fragment(inode->i_sb, + SQUASHFS_I(inode)-> + u.s1.fragment_start_block, + SQUASHFS_I(inode)->u.s1.fragment_size)) + == NULL) { + ERROR("Unable to read page, block %llx, size %x\n", + SQUASHFS_I(inode)-> + u.s1.fragment_start_block, + (int) SQUASHFS_I(inode)-> + u.s1.fragment_size); goto skip_read; } - bytes = inode->u.squashfs_i.fragment_offset + (inode->i_size & (sBlk->block_size - 1)); - byte_offset = inode->u.squashfs_i.fragment_offset; + bytes = SQUASHFS_I(inode)->u.s1.fragment_offset + + (i_size_read(inode) & (sblk->block_size + - 1)); + byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset; data_ptr = fragment->data; } - for(i = start_index; i <= end_index && byte_offset < bytes; i++, byte_offset += PAGE_CACHE_SIZE) { + for (i = start_index; i <= end_index && byte_offset < bytes; + i++, byte_offset += PAGE_CACHE_SIZE) { struct page *push_page; - int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : bytes - byte_offset; - - TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", bytes, i, byte_offset, available_bytes); - - if(i == page->index) { - memcpy(pageaddr, data_ptr + byte_offset, available_bytes); - memset(pageaddr + available_bytes, 0, PAGE_CACHE_SIZE - available_bytes); - kunmap(page); + int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ? + PAGE_CACHE_SIZE : bytes - byte_offset; + + TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", + bytes, i, byte_offset, available_bytes); + + if (i == page->index) { + pageaddr = kmap_atomic(page, KM_USER0); + memcpy(pageaddr, data_ptr + byte_offset, + available_bytes); + memset(pageaddr + available_bytes, 0, + PAGE_CACHE_SIZE - available_bytes); + kunmap_atomic(pageaddr, KM_USER0); flush_dcache_page(page); SetPageUptodate(page); UnlockPage(page); - } else if((push_page = grab_cache_page_nowait(page->mapping, i))) { - void *pageaddr = kmap(push_page); - memcpy(pageaddr, data_ptr + byte_offset, available_bytes); - memset(pageaddr + available_bytes, 0, PAGE_CACHE_SIZE - available_bytes); - kunmap(push_page); + } else if ((push_page = + grab_cache_page_nowait(page->mapping, i))) { + pageaddr = kmap_atomic(push_page, KM_USER0); + + memcpy(pageaddr, data_ptr + byte_offset, + available_bytes); + memset(pageaddr + available_bytes, 0, + PAGE_CACHE_SIZE - available_bytes); + kunmap_atomic(pageaddr, KM_USER0); flush_dcache_page(push_page); SetPageUptodate(push_page); UnlockPage(push_page); @@ -1219,16 +1575,19 @@ static int squashfs_readpage(struct file *file, struct page *page) } } - if(inode->u.squashfs_i.fragment_start_block == SQUASHFS_INVALID_BLK || index < (inode->i_size >> sBlk->block_log)) - up(&msBlk->read_page_mutex); + if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK + || index < (i_size_read(inode) >> + sblk->block_log)) + up(&msblk->read_page_mutex); else - release_cached_fragment(msBlk, fragment); + release_cached_fragment(msblk, fragment); return 0; skip_read: + pageaddr = kmap_atomic(page, KM_USER0); memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); - kunmap(page); + kunmap_atomic(pageaddr, KM_USER0); flush_dcache_page(page); SetPageUptodate(page); UnlockPage(page); @@ -1240,34 +1599,61 @@ skip_read: static int squashfs_readpage4K(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; - squashfs_sb_info *msBlk = &inode->i_sb->u.squashfs_sb; - squashfs_super_block *sBlk = &msBlk->sBlk; + struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; unsigned char block_list[SIZE]; - unsigned int bsize, block, bytes = 0; - void *pageaddr = kmap(page); + long long block; + unsigned int bsize, bytes = 0; + void *pageaddr; - TRACE("Entered squashfs_readpage4K, page index %x, start block %x\n", (unsigned int) page->index, - inode->u.squashfs_i.start_block); + TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n", + page->index, + SQUASHFS_I(inode)->start_block); - if(page->index < (inode->i_size >> sBlk->block_log)) { - block = (msBlk->read_blocklist)(inode, page->index, 1, block_list, NULL, &bsize); + if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT)) { + pageaddr = kmap_atomic(page, KM_USER0); + goto skip_read; + } - if(!(bytes = read_data(inode->i_sb, pageaddr, block, bsize, 1, NULL))) - ERROR("Unable to read page, block %x, size %x\n", block, bsize); + if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK + || page->index < (i_size_read(inode) >> + sblk->block_log)) { + block = (msblk->read_blocklist)(inode, page->index, 1, + block_list, NULL, &bsize); + + down(&msblk->read_page_mutex); + bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, + bsize, NULL); + pageaddr = kmap_atomic(page, KM_USER0); + if (bytes) + memcpy(pageaddr, msblk->read_page, bytes); + else + ERROR("Unable to read page, block %llx, size %x\n", + block, bsize); + up(&msblk->read_page_mutex); } else { - struct squashfs_fragment_cache *fragment; - - if((fragment = get_cached_fragment(inode->i_sb, inode->u.squashfs_i.fragment_start_block, inode->u.squashfs_i.fragment_size)) == NULL) - ERROR("Unable to read page, block %x, size %x\n", inode->u.squashfs_i.fragment_start_block, (int) inode->u.squashfs_i.fragment_size); - else { - bytes = inode->i_size & (sBlk->block_size - 1); - memcpy(pageaddr, fragment->data + inode->u.squashfs_i.fragment_offset, bytes); - release_cached_fragment(msBlk, fragment); - } + struct squashfs_fragment_cache *fragment = + get_cached_fragment(inode->i_sb, + SQUASHFS_I(inode)-> + u.s1.fragment_start_block, + SQUASHFS_I(inode)-> u.s1.fragment_size); + pageaddr = kmap_atomic(page, KM_USER0); + if (fragment) { + bytes = i_size_read(inode) & (sblk->block_size - 1); + memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> + u.s1.fragment_offset, bytes); + release_cached_fragment(msblk, fragment); + } else + ERROR("Unable to read page, block %llx, size %x\n", + SQUASHFS_I(inode)-> + u.s1.fragment_start_block, (int) + SQUASHFS_I(inode)-> u.s1.fragment_size); } +skip_read: memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); - kunmap(page); + kunmap_atomic(pageaddr, KM_USER0); flush_dcache_page(page); SetPageUptodate(page); UnlockPage(page); @@ -1276,191 +1662,331 @@ static int squashfs_readpage4K(struct file *file, struct page *page) } -#ifdef SQUASHFS_1_0_COMPATIBILITY -static int squashfs_readpage_lessthan4K(struct file *file, struct page *page) +static int get_dir_index_using_offset(struct super_block *s, long long + *next_block, unsigned int *next_offset, + long long index_start, + unsigned int index_offset, int i_count, + long long f_pos) { - struct inode *inode = page->mapping->host; - squashfs_sb_info *msBlk = &inode->i_sb->u.squashfs_sb; - squashfs_super_block *sBlk = &msBlk->sBlk; - unsigned char block_list[SIZE]; - unsigned short *block_listp, block, bytes = 0; - int index = page->index << (PAGE_CACHE_SHIFT - sBlk->block_log); - int file_blocks = ((inode->i_size - 1) >> sBlk->block_log) + 1; - int readahead_blks = 1 << (PAGE_CACHE_SHIFT - sBlk->block_log); - void *pageaddr = kmap(page); - - int i_end = index + (1 << (PAGE_CACHE_SHIFT - sBlk->block_log)); - int byte; - - TRACE("Entered squashfs_readpage_lessthan4K, page index %x, start block %x\n", (unsigned int) page->index, - inode->u.squashfs_i.start_block); + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; + int i, length = 0; + struct squashfs_dir_index index; + + TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", + i_count, (unsigned int) f_pos); + + f_pos -= 3; + if (f_pos == 0) + goto finish; + + for (i = 0; i < i_count; i++) { + if (msblk->swap) { + struct squashfs_dir_index sindex; + squashfs_get_cached_block(s, (char *) &sindex, + index_start, index_offset, + sizeof(sindex), &index_start, + &index_offset); + SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); + } else + squashfs_get_cached_block(s, (char *) &index, + index_start, index_offset, + sizeof(index), &index_start, + &index_offset); - block = read_blocklist_1(inode, index, readahead_blks, block_list, (char **) &block_listp, NULL); + if (index.index > f_pos) + break; - if(i_end > file_blocks) - i_end = file_blocks; + squashfs_get_cached_block(s, NULL, index_start, index_offset, + index.size + 1, &index_start, + &index_offset); - while(index < i_end) { - if(!(byte = read_data(inode->i_sb, pageaddr, block, *block_listp, 0, NULL))) { - ERROR("Unable to read page, block %x, size %x\n", block, *block_listp); - goto skip_read; - } - block += SQUASHFS_COMPRESSED_SIZE(*block_listp); - pageaddr += byte; - bytes += byte; - index ++; - block_listp ++; + length = index.index; + *next_block = index.start_block + sblk->directory_table_start; } -skip_read: - memset(pageaddr, 0, PAGE_CACHE_SIZE - bytes); - kunmap(page); - flush_dcache_page(page); - SetPageUptodate(page); - UnlockPage(page); + *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; - return 0; +finish: + return length + 3; } -#endif +static int get_dir_index_using_name(struct super_block *s, long long + *next_block, unsigned int *next_offset, + long long index_start, + unsigned int index_offset, int i_count, + const char *name, int size) +{ + struct squashfs_sb_info *msblk = &s->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; + int i, length = 0; + char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1]; + struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer; + char str[SQUASHFS_NAME_LEN + 1]; + + TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); + + strncpy(str, name, size); + str[size] = '\0'; + + for (i = 0; i < i_count; i++) { + if (msblk->swap) { + struct squashfs_dir_index sindex; + squashfs_get_cached_block(s, (char *) &sindex, + index_start, index_offset, + sizeof(sindex), &index_start, + &index_offset); + SQUASHFS_SWAP_DIR_INDEX(index, &sindex); + } else + squashfs_get_cached_block(s, (char *) index, + index_start, index_offset, + sizeof(struct squashfs_dir_index), + &index_start, &index_offset); + + squashfs_get_cached_block(s, index->name, index_start, + index_offset, index->size + 1, + &index_start, &index_offset); + + index->name[index->size + 1] = '\0'; + + if (strcmp(index->name, str) > 0) + break; + + length = index->index; + *next_block = index->start_block + sblk->directory_table_start; + } + + *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; + return length + 3; +} + + static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) { struct inode *i = file->f_dentry->d_inode; - squashfs_sb_info *msBlk = &i->i_sb->u.squashfs_sb; - squashfs_super_block *sBlk = &msBlk->sBlk; - int next_block = i->u.squashfs_i.start_block + sBlk->directory_table_start, next_offset = - i->u.squashfs_i.offset, length = 0, dirs_read = 0, dir_count; - squashfs_dir_header dirh; - char buffer[sizeof(squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; - squashfs_dir_entry *dire = (squashfs_dir_entry *) buffer; + struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; + long long next_block = SQUASHFS_I(i)->start_block + + sblk->directory_table_start; + int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, + dir_count; + struct squashfs_dir_header dirh; + char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; + struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; + + TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); + + while(file->f_pos < 3) { + char *name; + int size, i_ino; + + if(file->f_pos == 0) { + name = "."; + size = 1; + i_ino = i->i_ino; + } else { + name = ".."; + size = 2; + i_ino = SQUASHFS_I(i)->u.s2.parent_inode; + } + TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", + (unsigned int) dirent, name, size, (int) + file->f_pos, i_ino, + squashfs_filetype_table[1]); + + if (filldir(dirent, name, size, + file->f_pos, i_ino, + squashfs_filetype_table[1]) < 0) { + TRACE("Filldir returned less than 0\n"); + goto finish; + } + file->f_pos += size; + dirs_read++; + } - TRACE("Entered squashfs_readdir [%x:%x]\n", next_block, next_offset); + length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, + SQUASHFS_I(i)->u.s2.directory_index_start, + SQUASHFS_I(i)->u.s2.directory_index_offset, + SQUASHFS_I(i)->u.s2.directory_index_count, + file->f_pos); - while(length < i->i_size) { + while (length < i_size_read(i)) { /* read directory header */ - if(msBlk->swap) { - squashfs_dir_header sdirh; - if(!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, next_block, - next_offset, sizeof(sdirh), &next_block, &next_offset)) + if (msblk->swap) { + struct squashfs_dir_header sdirh; + + if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, + next_block, next_offset, sizeof(sdirh), + &next_block, &next_offset)) goto failed_read; + length += sizeof(sdirh); SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); } else { - if(!squashfs_get_cached_block(i->i_sb, (char *) &dirh, next_block, - next_offset, sizeof(dirh), &next_block, &next_offset)) + if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, + next_block, next_offset, sizeof(dirh), + &next_block, &next_offset)) goto failed_read; + length += sizeof(dirh); } dir_count = dirh.count + 1; - while(dir_count--) { - if(msBlk->swap) { - squashfs_dir_entry sdire; - if(!squashfs_get_cached_block(i->i_sb, (char *) &sdire, next_block, - next_offset, sizeof(sdire), &next_block, &next_offset)) + while (dir_count--) { + if (msblk->swap) { + struct squashfs_dir_entry sdire; + if (!squashfs_get_cached_block(i->i_sb, (char *) + &sdire, next_block, next_offset, + sizeof(sdire), &next_block, + &next_offset)) goto failed_read; + length += sizeof(sdire); SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); } else { - if(!squashfs_get_cached_block(i->i_sb, (char *) dire, next_block, - next_offset, sizeof(*dire), &next_block, &next_offset)) + if (!squashfs_get_cached_block(i->i_sb, (char *) + dire, next_block, next_offset, + sizeof(*dire), &next_block, + &next_offset)) goto failed_read; + length += sizeof(*dire); } - if(!squashfs_get_cached_block(i->i_sb, dire->name, next_block, - next_offset, dire->size + 1, &next_block, &next_offset)) + if (!squashfs_get_cached_block(i->i_sb, dire->name, + next_block, next_offset, + dire->size + 1, &next_block, + &next_offset)) goto failed_read; + length += dire->size + 1; - if(file->f_pos >= length) + if (file->f_pos >= length) continue; dire->name[dire->size + 1] = '\0'; - TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", (unsigned int) dirent, - dire->name, dire->size + 1, (int) file->f_pos, - dirh.start_block, dire->offset, squashfs_filetype_table[dire->type]); - - if(filldir(dirent, dire->name, dire->size + 1, file->f_pos, SQUASHFS_MK_VFS_INODE(dirh.start_block, - dire->offset), squashfs_filetype_table[dire->type]) < 0) { + TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", + (unsigned int) dirent, dire->name, + dire->size + 1, (int) file->f_pos, + dirh.start_block, dire->offset, + dirh.inode_number + dire->inode_number, + squashfs_filetype_table[dire->type]); + + if (filldir(dirent, dire->name, dire->size + 1, + file->f_pos, + dirh.inode_number + dire->inode_number, + squashfs_filetype_table[dire->type]) + < 0) { TRACE("Filldir returned less than 0\n"); - return dirs_read; + goto finish; } - file->f_pos = length; - dirs_read ++; + dirs_read++; } } +finish: return dirs_read; failed_read: - ERROR("Unable to read directory block [%x:%x]\n", next_block, next_offset); + ERROR("Unable to read directory block [%llx:%x]\n", next_block, + next_offset); return 0; } static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry) { - const char *name =dentry->d_name.name; + const unsigned char *name = dentry->d_name.name; int len = dentry->d_name.len; struct inode *inode = NULL; - squashfs_sb_info *msBlk = &i->i_sb->u.squashfs_sb; - squashfs_super_block *sBlk = &msBlk->sBlk; - int next_block = i->u.squashfs_i.start_block + sBlk->directory_table_start, next_offset = - i->u.squashfs_i.offset, length = 0, dir_count; - squashfs_dir_header dirh; - char buffer[sizeof(squashfs_dir_entry) + SQUASHFS_NAME_LEN]; - squashfs_dir_entry *dire = (squashfs_dir_entry *) buffer; - - TRACE("Entered squashfs_lookup [%x:%x]\n", next_block, next_offset); - - while(length < i->i_size) { + struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb; + struct squashfs_super_block *sblk = &msblk->sblk; + long long next_block = SQUASHFS_I(i)->start_block + + sblk->directory_table_start; + int next_offset = SQUASHFS_I(i)->offset, length = 0, + dir_count; + struct squashfs_dir_header dirh; + char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN]; + struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; + + TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); + + if (len > SQUASHFS_NAME_LEN) + goto exit_loop; + + length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, + SQUASHFS_I(i)->u.s2.directory_index_start, + SQUASHFS_I(i)->u.s2.directory_index_offset, + SQUASHFS_I(i)->u.s2.directory_index_count, name, + len); + + while (length < i_size_read(i)) { /* read directory header */ - if(msBlk->swap) { - squashfs_dir_header sdirh; - if(!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, next_block, next_offset, - sizeof(sdirh), &next_block, &next_offset)) + if (msblk->swap) { + struct squashfs_dir_header sdirh; + if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, + next_block, next_offset, sizeof(sdirh), + &next_block, &next_offset)) goto failed_read; + length += sizeof(sdirh); SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); } else { - if(!squashfs_get_cached_block(i->i_sb, (char *) &dirh, next_block, next_offset, - sizeof(dirh), &next_block, &next_offset)) + if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, + next_block, next_offset, sizeof(dirh), + &next_block, &next_offset)) goto failed_read; + length += sizeof(dirh); } dir_count = dirh.count + 1; - while(dir_count--) { - if(msBlk->swap) { - squashfs_dir_entry sdire; - if(!squashfs_get_cached_block(i->i_sb, (char *) &sdire, - next_block,next_offset, sizeof(sdire), &next_block, &next_offset)) + while (dir_count--) { + if (msblk->swap) { + struct squashfs_dir_entry sdire; + if (!squashfs_get_cached_block(i->i_sb, (char *) + &sdire, next_block,next_offset, + sizeof(sdire), &next_block, + &next_offset)) goto failed_read; + length += sizeof(sdire); SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); } else { - if(!squashfs_get_cached_block(i->i_sb, (char *) dire, - next_block,next_offset, sizeof(*dire), &next_block, &next_offset)) + if (!squashfs_get_cached_block(i->i_sb, (char *) + dire, next_block,next_offset, + sizeof(*dire), &next_block, + &next_offset)) goto failed_read; + length += sizeof(*dire); } - if(!squashfs_get_cached_block(i->i_sb, dire->name, - next_block, next_offset, dire->size + 1, &next_block, &next_offset)) + if (!squashfs_get_cached_block(i->i_sb, dire->name, + next_block, next_offset, dire->size + 1, + &next_block, &next_offset)) goto failed_read; + length += dire->size + 1; - if((len == dire->size + 1) && !strncmp(name, dire->name, len)) { - squashfs_inode ino = SQUASHFS_MKINODE(dirh.start_block, dire->offset); + if (name[0] < dire->name[0]) + goto exit_loop; + + if ((len == dire->size + 1) && !strncmp(name, + dire->name, len)) { + squashfs_inode_t ino = + SQUASHFS_MKINODE(dirh.start_block, + dire->offset); - TRACE("calling squashfs_iget for directory entry %s, inode %x:%x\n", - name, dirh.start_block, dire->offset); + TRACE("calling squashfs_iget for directory " + "entry %s, inode %x:%x, %d\n", name, + dirh.start_block, dire->offset, + dirh.inode_number + dire->inode_number); - inode = (msBlk->iget)(i->i_sb, ino); + inode = (msblk->iget)(i->i_sb, ino); goto exit_loop; } @@ -1472,36 +1998,63 @@ exit_loop: return ERR_PTR(0); failed_read: - ERROR("Unable to read directory block [%x:%x]\n", next_block, next_offset); + ERROR("Unable to read directory block [%llx:%x]\n", next_block, + next_offset); goto exit_loop; } static void squashfs_put_super(struct super_block *s) { - if(s->u.squashfs_sb.block_cache) kfree(s->u.squashfs_sb.block_cache); - if(s->u.squashfs_sb.read_data) kfree(s->u.squashfs_sb.read_data); - if(s->u.squashfs_sb.read_page) kfree(s->u.squashfs_sb.read_page); - if(s->u.squashfs_sb.uid) kfree(s->u.squashfs_sb.uid); - s->u.squashfs_sb.block_cache = (void *) s->u.squashfs_sb.uid = - s->u.squashfs_sb.read_data = s->u.squashfs_sb.read_page = NULL; + int i; + + struct squashfs_sb_info *sbi = &s->u.squashfs_sb; + if (sbi->block_cache) + for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) + if (sbi->block_cache[i].block != + SQUASHFS_INVALID_BLK) + kfree(sbi->block_cache[i].data); + if (sbi->fragment) + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) + SQUASHFS_FREE(sbi->fragment[i].data); + kfree(sbi->fragment); + kfree(sbi->block_cache); + kfree(sbi->read_data); + kfree(sbi->read_page); + kfree(sbi->uid); + kfree(sbi->fragment_index); + kfree(sbi->fragment_index_2); + sbi->block_cache = NULL; + sbi->uid = NULL; + sbi->read_data = NULL; + sbi->read_page = NULL; + sbi->fragment = NULL; + sbi->fragment_index = NULL; + sbi->fragment_index_2 = NULL; } static int __init init_squashfs_fs(void) { - if(!(stream.workspace = (char *) vmalloc(zlib_inflate_workspacesize()))) { + printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " + "Phillip Lougher\n"); + +#ifndef SQUASHFS_LZMA + if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { ERROR("Failed to allocate zlib workspace\n"); return -ENOMEM; } +#endif return register_filesystem(&squashfs_fs_type); } static void __exit exit_squashfs_fs(void) { +#ifndef SQUASHFS_LZMA vfree(stream.workspace); +#endif unregister_filesystem(&squashfs_fs_type); } @@ -1511,5 +2064,5 @@ EXPORT_NO_SYMBOLS; module_init(init_squashfs_fs); module_exit(exit_squashfs_fs); MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem"); -MODULE_AUTHOR("Phillip Lougher <plougher@users.sourceforge.net>"); +MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>"); MODULE_LICENSE("GPL"); |