X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fsquashfs%2Finode.c;h=ccbaeb6bf94ed11007705e5e1dcd5714c4544fa7;hb=c469b50b11a8eaa1dc314687c2b6030c8fdea5b7;hp=f8b806d91c9603f78d4114b8b342908e41764b4a;hpb=16cf0ec7408f389279d413869e94c1a351392f97;p=linux-2.6.git diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c index f8b806d91..ccbaeb6bf 100644 --- a/fs/squashfs/inode.c +++ b/fs/squashfs/inode.c @@ -1,7 +1,8 @@ /* * Squashfs - a compressed read only filesystem for Linux * - * Copyright (c) 2002, 2003, 2004, 2005 Phillip Lougher + * Copyright (c) 2002, 2003, 2004, 2005, 2006 + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,13 +21,12 @@ * inode.c */ -#define SQUASHFS_1_0_COMPATIBILITY - #include #include #include #include #include +#include #include #include #include @@ -36,56 +36,34 @@ #include #include #include -#include #include -#include -#include #include #include +#include +#include -#ifdef SQUASHFS_TRACE -#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) -#else -#define TRACE(s, args...) {} -#endif - -#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args) - -#define SERROR(s, args...) if(!silent) printk(KERN_ERR "SQUASHFS error: "s, ## args) -#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args) +#include "squashfs.h" static void squashfs_put_super(struct super_block *); -static int squashfs_statfs(struct super_block *, struct kstatfs *); +static int squashfs_statfs(struct dentry *, struct kstatfs *); static int squashfs_symlink_readpage(struct file *file, struct page *page); static int squashfs_readpage(struct file *file, struct page *page); static int squashfs_readpage4K(struct file *file, struct page *page); static int squashfs_readdir(struct file *, void *, filldir_t); -static struct dentry *squashfs_lookup(struct inode *, struct dentry *, struct nameidata *); -static unsigned int read_data(struct super_block *s, char *buffer, - unsigned int index, unsigned int length, 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, unsigned short **block_p, unsigned int *bsize); -static void squashfs_put_super(struct super_block *s); -static struct super_block *squashfs_get_sb(struct file_system_type *, int, const char *, void *); static struct inode *squashfs_alloc_inode(struct super_block *sb); static void squashfs_destroy_inode(struct inode *inode); static int init_inodecache(void); static void destroy_inodecache(void); - -#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, unsigned short **block_p, unsigned int *bsize); -#endif - -DECLARE_MUTEX(read_data_mutex); - -static z_stream stream; +static struct dentry *squashfs_lookup(struct inode *, struct dentry *, + struct nameidata *); +static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode); +static long long read_blocklist(struct inode *inode, int index, + int readahead_blks, char *block_list, + unsigned short **block_p, unsigned int *bsize); +static int squashfs_get_sb(struct file_system_type *,int, const char *, void *, + struct vfsmount *); +static void vfs_read_inode(struct inode *i); +static struct dentry *squashfs_get_parent(struct dentry *child); static struct file_system_type squashfs_fs_type = { .owner = THIS_MODULE, @@ -93,57 +71,57 @@ static struct file_system_type squashfs_fs_type = { .get_sb = squashfs_get_sb, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV - }; +}; static unsigned char squashfs_filetype_table[] = { DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK }; -static struct super_operations squashfs_ops = { +static struct super_operations squashfs_super_ops = { + .alloc_inode = squashfs_alloc_inode, + .destroy_inode = squashfs_destroy_inode, + .statfs = squashfs_statfs, + .put_super = squashfs_put_super, +}; + +static struct super_operations squashfs_export_super_ops = { .alloc_inode = squashfs_alloc_inode, .destroy_inode = squashfs_destroy_inode, .statfs = squashfs_statfs, .put_super = squashfs_put_super, + .read_inode = vfs_read_inode +}; + +struct export_operations squashfs_export_ops = { + .get_parent = squashfs_get_parent }; -static struct address_space_operations squashfs_symlink_aops = { +SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = { .readpage = squashfs_symlink_readpage }; -static struct address_space_operations squashfs_aops = { +SQSH_EXTERN struct address_space_operations squashfs_aops = { .readpage = squashfs_readpage }; -static struct address_space_operations squashfs_aops_4K = { +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 }; -static struct inode_operations squashfs_dir_inode_ops = { +SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = { .lookup = squashfs_lookup }; -static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode) -{ - return list_entry(inode, struct squashfs_inode_info, vfs_inode); -} - - static struct buffer_head *get_block_length(struct super_block *s, int *cur_index, int *offset, int *c_byte) { - squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_sb_info *msblk = s->s_fs_info; unsigned short temp; struct buffer_head *bh; @@ -184,7 +162,7 @@ static struct buffer_head *get_block_length(struct super_block *s, *offset += 2; } - if (SQUASHFS_CHECK_DATA(msblk->sBlk.flags)) { + if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { if (*offset == msblk->devblksize) { brelse(bh); if (!(bh = sb_bread(s, ++(*cur_index)))) @@ -207,58 +185,76 @@ out: } -static unsigned int read_data(struct super_block *s, char *buffer, - unsigned int index, unsigned int length, 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, int srclength) { - squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info; - struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> msBlk->devblksize_log2) + 2]; - unsigned int offset = index & ((1 << msBlk->devblksize_log2) - 1); - unsigned int cur_index = index >> msBlk->devblksize_log2; - int bytes, avail_bytes, b = 0, k; + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> + msblk->devblksize_log2) + 2]; + unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); + unsigned int cur_index = index >> msblk->devblksize_log2; + int bytes, avail_bytes, b = 0, k = 0; char *c_buffer; unsigned int compressed; unsigned int c_byte = length; - if(c_byte) { - bytes = msBlk->devblksize - offset; + if (c_byte) { + bytes = msblk->devblksize - offset; compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); - c_buffer = compressed ? msBlk->read_data : buffer; + 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, src size %d\n", index, compressed + ? "" : "un", (unsigned int) c_byte, srclength); + + if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used) + goto read_failure; - if(!(bh[0] = sb_getblk(s, cur_index))) + 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 { - if(!(bh[0] = get_block_length(s, &cur_index, &offset, &c_byte))) + if (index < 0 || (index + 2) > sblk->bytes_used) + goto read_failure; + + if (!(bh[0] = get_block_length(s, &cur_index, &offset, + &c_byte))) goto read_failure; - bytes = msBlk->devblksize - offset; + bytes = msblk->devblksize - offset; compressed = SQUASHFS_COMPRESSED(c_byte); - c_buffer = compressed ? msBlk->read_data : buffer; + 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))) + if (c_byte > srclength || (index + c_byte) > sblk->bytes_used) + goto read_failure; + + for (b = 1; bytes < c_byte; b++) { + if (!(bh[b] = sb_getblk(s, ++cur_index))) goto block_release; - bytes += msBlk->devblksize; + 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 < 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; @@ -271,30 +267,39 @@ static unsigned int read_data(struct super_block *s, char *buffer, /* * uncompress block */ - if(compressed) { + if (compressed) { int zlib_err; - stream.next_in = c_buffer; - stream.avail_in = c_byte; - stream.next_out = buffer; - stream.avail_out = msBlk->read_size; - if(((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || - ((zlib_err = zlib_inflate(&stream, Z_FINISH)) != Z_STREAM_END) || - ((zlib_err = zlib_inflateEnd(&stream)) != Z_OK)) { - ERROR("zlib_fs returned unexpected result 0x%x\n", zlib_err); + msblk->stream.next_in = c_buffer; + msblk->stream.avail_in = c_byte; + msblk->stream.next_out = buffer; + //msblk->stream.avail_out = msblk->read_size;//srclength; + msblk->stream.avail_out = srclength; + + if (((zlib_err = zlib_inflateInit(&msblk->stream)) != Z_OK) || + ((zlib_err = zlib_inflate(&msblk->stream, Z_FINISH)) + != Z_STREAM_END) || ((zlib_err = + zlib_inflateEnd(&msblk->stream)) != Z_OK)) { + //ERROR("zlib_fs returned unexpected result 0x%x\n", + // zlib_err); + ERROR("zlib_fs returned unexpected result 0x%x, srclength %d\n", + zlib_err, srclength); bytes = 0; } else - bytes = stream.total_out; - up(&read_data_mutex); + bytes = msblk->stream.total_out; + + up(&msblk->read_data_mutex); } - if(next_index) - *next_index = index + c_byte + (length ? 0 : (SQUASHFS_CHECK_DATA(msBlk->sBlk.flags) ? 3 : 2)); - + 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]); + for (; k < b; k++) + brelse(bh[k]); read_failure: ERROR("sb_bread failed reading block 0x%x\n", cur_index); @@ -302,820 +307,1009 @@ 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 = (squashfs_sb_info *)s->s_fs_info; + struct squashfs_sb_info *msblk = s->s_fs_info; 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) { + + if (n == 0) { wait_queue_t wait; init_waitqueue_entry(&wait, current); - add_wait_queue(&msBlk->waitq, &wait); - up(&msBlk->block_cache_mutex); + 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); + 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, - &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); + + msblk->block_cache[i].length = squashfs_read_data(s, + msblk->block_cache[i].data, block, 0, &next_index, SQUASHFS_METADATA_SIZE); + if (msblk->block_cache[i].length == 0) { + ERROR("Unable to read cache block [%llx:%x]\n", + block, offset); + down(&msblk->block_cache_mutex); + msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; + kfree(msblk->block_cache[i].data); + wake_up(&msblk->waitq); + up(&msblk->block_cache_mutex); + goto out; } - down(&msBlk->block_cache_mutex); - wake_up(&msBlk->waitq); - msBlk->block_cache[i].block = block; - msBlk->block_cache[i].next_index = next_index; - TRACE("Read cache block [%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; + bytes = msblk->block_cache[i].length - offset; + + if (bytes < 1) { + up(&msblk->block_cache_mutex); + goto out; + } else if (bytes >= length) { + if (buffer) + memcpy(buffer, msblk->block_cache[i].data + + offset, length); + if (msblk->block_cache[i].length - offset == length) { + *next_block = msblk->block_cache[i].next_index; *next_offset = 0; } else { *next_block = block; *next_offset = offset + length; } - - up(&msBlk->block_cache_mutex); - 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 = (squashfs_sb_info *)s->s_fs_info; - unsigned int start_block = msBlk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; + struct squashfs_sb_info *msblk = s->s_fs_info; + long long start_block = + msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); - 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 = (squashfs_sb_info *)s->s_fs_info; + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + + while ( 1 ) { + down(&msblk->fragment_mutex); + + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && + msblk->fragment[i].block != start_block; i++); - 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 (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) { + if (n == 0) { wait_queue_t wait; init_waitqueue_entry(&wait, current); - add_wait_queue(&msBlk->fragment_wait_queue, &wait); - up(&msBlk->fragment_mutex); + 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); + 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 *) - SQUASHFS_ALLOC(SQUASHFS_FILE_MAX_SIZE))) { - 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, - 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, sblk->block_size))) { + 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]; -#ifdef SQUASHFS_1_0_COMPATIBILITY -static struct inode *squashfs_iget_1(struct super_block *s, squashfs_inode inode) -{ - struct inode *i = new_inode(s); - squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info; - 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; +out: + return NULL; +} - TRACE("Entered squashfs_iget_1\n"); - if(msBlk->swap) { - squashfs_base_inode_header_1 sinodeb; +static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i, + struct squashfs_base_inode_header *inodeb) +{ + i->i_ino = inodeb->inode_number; + i->i_mtime.tv_sec = inodeb->mtime; + i->i_atime.tv_sec = inodeb->mtime; + i->i_ctime.tv_sec = inodeb->mtime; + i->i_uid = msblk->uid[inodeb->uid]; + i->i_mode = inodeb->mode; + i->i_size = 0; + if (inodeb->guid == SQUASHFS_GUIDS) + i->i_gid = i->i_uid; + else + i->i_gid = msblk->guid[inodeb->guid]; +} - 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; - i->i_nlink = 1; +static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino) +{ + struct squashfs_sb_info *msblk = s->s_fs_info; + long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)]; + int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1); + squashfs_inode_t inode; - i->i_mtime.tv_sec = sBlk->mkfs_time; - i->i_atime.tv_sec = sBlk->mkfs_time; - i->i_ctime.tv_sec = sBlk->mkfs_time; + TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino); - 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); + if (msblk->swap) { + squashfs_inode_t sinode; - i->i_mode = inodeb.mode; + if (!squashfs_get_cached_block(s, (char *) &sinode, start, offset, + sizeof(sinode), &start, &offset)) + goto out; + SQUASHFS_SWAP_INODE_T((&inode), &sinode); + } else if (!squashfs_get_cached_block(s, (char *) &inode, start, offset, + sizeof(inode), &start, &offset)) + goto out; - switch(inodeb.inode_type == SQUASHFS_IPC_TYPE ? SQUASHFS_IPC_TYPE : (inodeb.inode_type - 1) % SQUASHFS_TYPES + 1) { - case SQUASHFS_FILE_TYPE: { - squashfs_reg_inode_header_1 inodep; + TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode); - if(msBlk->swap) { - squashfs_reg_inode_header_1 sinodep; + return inode; - 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); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; +out: + return SQUASHFS_INVALID_BLK; +} + - 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 if(sBlk->block_size == 4096) - i->i_data.a_ops = &squashfs_aops_4K; - else - i->i_data.a_ops = &squashfs_aops_lessthan4K; - i->i_mode |= S_IFREG; - i->i_mtime.tv_sec = inodep.mtime; - i->i_atime.tv_sec = inodep.mtime; - i->i_ctime.tv_sec = inodep.mtime; - i->i_blocks = ((i->i_size - 1) >> 9) + 1; - i->i_blksize = PAGE_CACHE_SIZE; - SQUASHFS_I(i)->u.s1.fragment_start_block = SQUASHFS_INVALID_BLK; - SQUASHFS_I(i)->u.s1.fragment_offset = 0; - SQUASHFS_I(i)->start_block = inodep.start_block; - SQUASHFS_I(i)->block_list_start = next_block; - SQUASHFS_I(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); - break; - } - case SQUASHFS_DIR_TYPE: { - squashfs_dir_inode_header_1 inodep; +static void vfs_read_inode(struct inode *i) +{ + struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; + squashfs_inode_t inode = squashfs_inode_lookup(i->i_sb, i->i_ino); - if(msBlk->swap) { - squashfs_dir_inode_header_1 sinodep; + TRACE("Entered vfs_read_inode\n"); - 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); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; + if(inode != SQUASHFS_INVALID_BLK) + (msblk->read_inode)(i, inode); +} - 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.tv_sec = inodep.mtime; - i->i_atime.tv_sec = inodep.mtime; - i->i_ctime.tv_sec = inodep.mtime; - SQUASHFS_I(i)->start_block = inodep.start_block; - SQUASHFS_I(i)->offset = inodep.offset; - SQUASHFS_I(i)->u.s2.directory_index_count = 0; - TRACE("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; - - 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)) - goto failed_read; - SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(&inodep, &sinodep); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; +static struct dentry *squashfs_get_parent(struct dentry *child) +{ + struct inode *i = child->d_inode; + struct inode *parent = iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode); + struct dentry *rv; - i->i_size = inodep.symlink_size; - i->i_op = &page_symlink_inode_operations; - i->i_data.a_ops = &squashfs_symlink_aops; - i->i_mode |= S_IFLNK; - SQUASHFS_I(i)->start_block = next_block; - SQUASHFS_I(i)->offset = next_offset; - TRACE("Symbolic link inode %x:%x, start_block %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_1 inodep; + TRACE("Entered squashfs_get_parent\n"); - if(msBlk->swap) { - squashfs_dev_inode_header_1 sinodep; + if(parent == NULL) { + rv = ERR_PTR(-EACCES); + goto out; + } - 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); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; + rv = d_alloc_anon(parent); + if(rv == NULL) + rv = ERR_PTR(-ENOMEM); - i->i_size = 0; - i->i_mode |= (inodeb.inode_type == SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : S_IFBLK; - init_special_inode(i, i->i_mode, old_decode_dev(inodep.rdev)); - TRACE("Device inode %x:%x, rdev %x\n", SQUASHFS_INODE_BLK(inode), offset, inodep.rdev); - break; - } - case SQUASHFS_IPC_TYPE: { - squashfs_ipc_inode_header_1 inodep; +out: + return rv; +} - if(msBlk->swap) { - squashfs_ipc_inode_header_1 sinodep; + +SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number) +{ + struct squashfs_sb_info *msblk = s->s_fs_info; + struct inode *i = iget_locked(s, inode_number); - 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); - } else - if(!squashfs_get_cached_block(s, (char *) &inodep, block, offset, sizeof(inodep), - &next_block, &next_offset)) - goto failed_read; + TRACE("Entered squashfs_iget\n"); - 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]; - 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; + if(i && (i->i_state & I_NEW)) { + (msblk->read_inode)(i, inode); + unlock_new_inode(i); } - - 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); - -failed_read1: - return NULL; } -#endif -static struct inode *squashfs_iget(struct super_block *s, squashfs_inode inode) +static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode) { - struct inode *i = new_inode(s); - squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info; - squashfs_super_block *sBlk = &msBlk->sBlk; - unsigned int block = SQUASHFS_INODE_BLK(inode) + sBlk->inode_table_start; + struct super_block *s = i->i_sb; + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + long long block = SQUASHFS_INODE_BLK(inode) + + sblk->inode_table_start; unsigned int offset = SQUASHFS_INODE_OFFSET(inode); - unsigned int next_block, next_offset; - squashfs_base_inode_header inodeb; - - TRACE("Entered squashfs_iget\n"); - - if(msBlk->swap) { - squashfs_base_inode_header sinodeb; - - if(!squashfs_get_cached_block(s, (char *) &sinodeb, block, offset, - sizeof(sinodeb), &next_block, &next_offset)) + long long next_block; + unsigned int next_offset; + union squashfs_inode_header id, sid; + struct squashfs_base_inode_header *inodeb = &id.base, + *sinodeb = &sid.base; + + TRACE("Entered squashfs_read_inode\n"); + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) sinodeb, block, + offset, sizeof(*sinodeb), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_BASE_INODE_HEADER(&inodeb, &sinodeb, sizeof(sinodeb)); + 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)) + 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.tv_sec = sBlk->mkfs_time; - i->i_atime.tv_sec = sBlk->mkfs_time; - i->i_ctime.tv_sec = sBlk->mkfs_time; - - i->i_uid = msBlk->uid[inodeb.uid]; - i->i_ino = SQUASHFS_MK_VFS_INODE(block - sBlk->inode_table_start, offset); + squashfs_new_inode(msblk, i, inodeb); - i->i_mode = inodeb.mode; - - switch(inodeb.inode_type) { + 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)) + unsigned int frag_size; + long long frag_blk; + struct squashfs_reg_inode_header *inodep = &id.reg; + struct squashfs_reg_inode_header *sinodep = &sid.reg; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_REG_INODE_HEADER(&inodep, &sinodep); + 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; - SQUASHFS_I(i)->u.s1.fragment_start_block = SQUASHFS_INVALID_BLK; - if(inodep.fragment != SQUASHFS_INVALID_BLK && !get_fragment_location(s, inodep.fragment, - &SQUASHFS_I(i)->u.s1.fragment_start_block, &SQUASHFS_I(i)->u.s1.fragment_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; - - SQUASHFS_I(i)->u.s1.fragment_offset = inodep.offset; - i->i_size = inodep.file_size; + + 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; + SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; + SQUASHFS_I(i)->u.s1.fragment_size = frag_size; + SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->u.s1.block_list_start = next_block; + SQUASHFS_I(i)->offset = next_offset; + if (sblk->block_size > 4096) i->i_data.a_ops = &squashfs_aops; else i->i_data.a_ops = &squashfs_aops_4K; + + TRACE("File inode %x:%x, start_block %llx, " + "block_list_start %llx, offset %x\n", + SQUASHFS_INODE_BLK(inode), offset, + inodep->start_block, next_block, + next_offset); + break; + } + case SQUASHFS_LREG_TYPE: { + unsigned int frag_size; + long long frag_blk; + struct squashfs_lreg_inode_header *inodep = &id.lreg; + struct squashfs_lreg_inode_header *sinodep = &sid.lreg; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) + goto failed_read; + SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); + } else + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) + goto failed_read; + + frag_blk = SQUASHFS_INVALID_BLK; + if (inodep->fragment != SQUASHFS_INVALID_FRAG && + !get_fragment_location(s, + inodep->fragment, &frag_blk, &frag_size)) + goto failed_read; + + i->i_nlink = inodep->nlink; + i->i_size = inodep->file_size; + i->i_fop = &generic_ro_fops; i->i_mode |= S_IFREG; - i->i_mtime.tv_sec = inodep.mtime; - i->i_atime.tv_sec = inodep.mtime; - i->i_ctime.tv_sec = inodep.mtime; i->i_blocks = ((i->i_size - 1) >> 9) + 1; - i->i_blksize = PAGE_CACHE_SIZE; - SQUASHFS_I(i)->start_block = inodep.start_block; - SQUASHFS_I(i)->block_list_start = next_block; + 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; - 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); + 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 inodep; - - if(msBlk->swap) { - squashfs_dir_inode_header sinodep; - - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) + 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); + SQUASHFS_SWAP_DIR_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; + 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.tv_sec = inodep.mtime; - i->i_atime.tv_sec = inodep.mtime; - i->i_ctime.tv_sec = inodep.mtime; - SQUASHFS_I(i)->start_block = inodep.start_block; - SQUASHFS_I(i)->offset = inodep.offset; + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->offset = inodep->offset; SQUASHFS_I(i)->u.s2.directory_index_count = 0; - TRACE("Directory inode %x:%x, start_block %x, offset %x\n", SQUASHFS_INODE_BLK(inode), offset, - inodep.start_block, inodep.offset); + 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: { - squashfs_ldir_inode_header inodep; - - if(msBlk->swap) { - squashfs_ldir_inode_header sinodep; - - if(!squashfs_get_cached_block(s, (char *) &sinodep, block, offset, sizeof(sinodep), - &next_block, &next_offset)) + struct squashfs_ldir_inode_header *inodep = &id.ldir; + struct squashfs_ldir_inode_header *sinodep = &sid.ldir; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_LDIR_INODE_HEADER(&inodep, &sinodep); + 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; + 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.tv_sec = inodep.mtime; - i->i_atime.tv_sec = inodep.mtime; - i->i_ctime.tv_sec = inodep.mtime; - SQUASHFS_I(i)->start_block = inodep.start_block; - SQUASHFS_I(i)->offset = inodep.offset; + SQUASHFS_I(i)->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; - TRACE("Long directory inode %x:%x, start_block %x, offset %x\n", SQUASHFS_INODE_BLK(inode), offset, - inodep.start_block, inodep.offset); + 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 inodep; + struct squashfs_symlink_inode_header *inodep = + &id.symlink; + struct squashfs_symlink_inode_header *sinodep = + &sid.symlink; - if(msBlk->swap) { - squashfs_symlink_inode_header 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(&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; + i->i_nlink = inodep->nlink; + i->i_size = inodep->symlink_size; i->i_op = &page_symlink_inode_operations; i->i_data.a_ops = &squashfs_symlink_aops; i->i_mode |= S_IFLNK; SQUASHFS_I(i)->start_block = next_block; SQUASHFS_I(i)->offset = next_offset; - TRACE("Symbolic link inode %x:%x, start_block %x, offset %x\n", - SQUASHFS_INODE_BLK(inode), offset, next_block, 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 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)) + struct squashfs_dev_inode_header *inodep = &id.dev; + struct squashfs_dev_inode_header *sinodep = &sid.dev; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) goto failed_read; - SQUASHFS_SWAP_DEV_INODE_HEADER(&inodep, &sinodep); + 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, old_decode_dev(inodep.rdev)); - TRACE("Device inode %x:%x, rdev %x\n", SQUASHFS_INODE_BLK(inode), offset, inodep.rdev); + i->i_nlink = inodep->nlink; + i->i_mode |= (inodeb->inode_type == + SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : + S_IFBLK; + init_special_inode(i, i->i_mode, + old_decode_dev(inodep->rdev)); + + TRACE("Device inode %x:%x, rdev %x\n", + SQUASHFS_INODE_BLK(inode), offset, + inodep->rdev); break; } case SQUASHFS_FIFO_TYPE: case SQUASHFS_SOCKET_TYPE: { - i->i_size = 0; - i->i_mode |= (inodeb.inode_type == SQUASHFS_FIFO_TYPE) ? S_IFIFO : S_IFSOCK; + struct squashfs_ipc_inode_header *inodep = &id.ipc; + struct squashfs_ipc_inode_header *sinodep = &sid.ipc; + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) + sinodep, block, offset, + sizeof(*sinodep), &next_block, + &next_offset)) + goto failed_read; + SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); + } else + if (!squashfs_get_cached_block(s, (char *) + inodep, block, offset, + sizeof(*inodep), &next_block, + &next_offset)) + goto failed_read; + + i->i_nlink = inodep->nlink; + i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) + ? S_IFIFO : S_IFSOCK; init_special_inode(i, i->i_mode, 0); break; } default: - ERROR("Unknown inode type %d in squashfs_iget!\n", inodeb.inode_type); - goto failed_read1; + ERROR("Unknown inode type %d in squashfs_iget!\n", + inodeb->inode_type); + goto failed_read1; } - if(inodeb.guid == SQUASHFS_GUIDS) - i->i_gid = i->i_uid; - else - i->i_gid = msBlk->guid[inodeb.guid]; - - insert_inode_hash(i); - return i; + return 1; 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; + make_bad_inode(i); + return 0; } -static int squashfs_fill_super(struct super_block *s, - void *data, int silent) +static int read_inode_lookup_table(struct super_block *s) { - squashfs_sb_info *msBlk; - squashfs_super_block *sBlk; + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes); + + TRACE("In read_inode_lookup_table, length %d\n", length); + + /* Allocate inode lookup table */ + if (!(msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL))) { + ERROR("Failed to allocate inode lookup table\n"); + return 0; + } + + if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table, + sblk->lookup_table_start, length | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) { + ERROR("unable to read inode lookup table\n"); + return 0; + } + + if (msblk->swap) { + int i; + long long block; + + for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) { + SQUASHFS_SWAP_LOOKUP_BLOCKS((&block), + &msblk->inode_lookup_table[i], 1); + msblk->inode_lookup_table[i] = block; + } + } + + return 1; +} + + +static int read_fragment_index_table(struct super_block *s) +{ + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments); + + if(length == 0) + return 1; + + /* Allocate fragment index table */ + if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) { + ERROR("Failed to allocate fragment index table\n"); + return 0; + } + + if (!squashfs_read_data(s, (char *) msblk->fragment_index, + sblk->fragment_table_start, length | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) { + ERROR("unable to read fragment index table\n"); + return 0; + } + + if (msblk->swap) { + int i; + long long fragment; + + for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) { + SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), + &msblk->fragment_index[i], 1); + msblk->fragment_index[i] = fragment; + } + } + + return 1; +} + + +static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) +{ + struct squashfs_super_block *sblk = &msblk->sblk; + + msblk->read_inode = squashfs_read_inode; + msblk->read_blocklist = read_blocklist; + msblk->read_fragment_index_table = read_fragment_index_table; + + if (sblk->s_major == 1) { + if (!squashfs_1_0_supported(msblk)) { + SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " + "are unsupported\n"); + SERROR("Please recompile with " + "Squashfs 1.0 support enabled\n"); + return 0; + } + } else if (sblk->s_major == 2) { + if (!squashfs_2_0_supported(msblk)) { + SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " + "are unsupported\n"); + SERROR("Please recompile with " + "Squashfs 2.0 support enabled\n"); + return 0; + } + } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > + SQUASHFS_MINOR) { + SERROR("Major/Minor mismatch, trying to mount newer %d.%d " + "filesystem\n", sblk->s_major, sblk->s_minor); + SERROR("Please update your kernel\n"); + return 0; + } + + return 1; +} + + +static int squashfs_fill_super(struct super_block *s, void *data, int silent) +{ + struct squashfs_sb_info *msblk; + struct squashfs_super_block *sblk; int i; char b[BDEVNAME_SIZE]; + struct inode *root; TRACE("Entered squashfs_read_superblock\n"); - if(!(s->s_fs_info = (void *) kmalloc(sizeof(squashfs_sb_info), GFP_KERNEL))) { + if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info), + GFP_KERNEL))) { ERROR("Failed to allocate superblock\n"); - return -ENOMEM; + goto failure; + } + memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info)); + msblk = s->s_fs_info; + if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { + ERROR("Failed to allocate zlib workspace\n"); + goto failure; } - msBlk = (squashfs_sb_info *) s->s_fs_info; - sBlk = &msBlk->sBlk; + sblk = &msblk->sblk; - msBlk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); - msBlk->devblksize_log2 = ffz(~msBlk->devblksize); - - init_MUTEX(&msBlk->read_page_mutex); - init_MUTEX(&msBlk->block_cache_mutex); - init_MUTEX(&msBlk->fragment_mutex); + msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); + msblk->devblksize_log2 = ffz(~msblk->devblksize); + + init_MUTEX(&msblk->read_data_mutex); + init_MUTEX(&msblk->read_page_mutex); + init_MUTEX(&msblk->block_cache_mutex); + init_MUTEX(&msblk->fragment_mutex); + init_MUTEX(&msblk->meta_index_mutex); - init_waitqueue_head(&msBlk->waitq); - init_waitqueue_head(&msBlk->fragment_wait_queue); + 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_BLOCK, NULL)) { + sblk->bytes_used = sizeof(struct squashfs_super_block); + if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, + sizeof(struct squashfs_super_block) | + SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) { SERROR("unable to read superblock\n"); goto failed_mount; } /* Check it is a SQUASHFS superblock */ - msBlk->swap = 0; - if((s->s_magic = sBlk->s_magic) != SQUASHFS_MAGIC) { - if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP) { - squashfs_super_block sblk; - WARNING("Mounting a different endian SQUASHFS filesystem on %s\n", bdevname(s->s_bdev, b)); - 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(s->s_bdev, b)); + + SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); + memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); + msblk->swap = 1; } else { - SERROR("Can't find a SQUASHFS superblock on %s\n", bdevname(s->s_bdev, b)); + SERROR("Can't find a SQUASHFS superblock on %s\n", + bdevname(s->s_bdev, b)); 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); + + /* Check the filesystem does not extend beyond the end of the + block device */ + if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode)) + goto failed_mount; + + /* Check the root inode for sanity */ + if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE) goto failed_mount; - } -#endif TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); - TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : ""); - TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : ""); - TRACE("Check data is %s present in the filesystem\n", SQUASHFS_CHECK_DATA(sBlk->flags) ? "" : "not"); - TRACE("Filesystem size %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; + s->s_op = &squashfs_super_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) { - if(!(msBlk->read_page = (char *) kmalloc(sBlk->block_size, GFP_KERNEL))) { - ERROR("Failed to allocate read_page block\n"); - goto failed_mount2; - } - } else - msBlk->read_page = NULL; + if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { + ERROR("Failed to allocate read_page block\n"); + goto failed_mount; + } /* Allocate uid and gid tables */ - if(!(msBlk->uid = (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_BLOCK, 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, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) { + ERROR("unable to read uid/gid table\n"); + goto failed_mount; } - SQUASHFS_SWAP_DATA(msBlk->uid, suid, (sBlk->no_uids + sBlk->no_guids), (sizeof(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_BLOCK, 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, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) { + 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 = NULL; - 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_BLOCK, NULL)) { - SERROR("unable to read fragment index table\n"); - goto failed_mount6; - } + /* Allocate and read fragment index table */ + if (msblk->read_fragment_index_table(s) == 0) + goto failed_mount; - if(msBlk->swap) { - int i; - squashfs_fragment_index fragment; + if(sblk->lookup_table_start == SQUASHFS_INVALID_BLK) + goto allocate_root; - 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; - } - } + /* Allocate and read inode lookup table */ + if (read_inode_lookup_table(s) == 0) + goto failed_mount; + + s->s_op = &squashfs_export_super_ops; + s->s_export_op = &squashfs_export_ops; -#ifdef SQUASHFS_1_0_COMPATIBILITY allocate_root: -#endif - if(!(s->s_root = d_alloc_root((msBlk->iget)(s, sBlk->root_inode)))) { + root = new_inode(s); + if ((msblk->read_inode)(root, sblk->root_inode) == 0) + goto failed_mount; + insert_inode_hash(root); + + if ((s->s_root = d_alloc_root(root)) == NULL) { ERROR("Root inode create failed\n"); - goto failed_mount5; + iput(root); + goto failed_mount; } TRACE("Leaving squashfs_read_super\n"); return 0; -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->inode_lookup_table); + 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); + vfree(msblk->stream.workspace); kfree(s->s_fs_info); s->s_fs_info = NULL; return -EINVAL; + +failure: + return -ENOMEM; } -static int squashfs_statfs(struct super_block *s, struct kstatfs *buf) +static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) { - squashfs_super_block *sBlk = &((squashfs_sb_info *)s->s_fs_info)->sBlk; + struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; TRACE("Entered squashfs_statfs\n"); + buf->f_type = SQUASHFS_MAGIC; - buf->f_bsize = sBlk->block_size; - buf->f_blocks = ((sBlk->bytes_used - 1) >> sBlk->block_log) + 1; + buf->f_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; } @@ -1124,35 +1318,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; - unsigned int block = SQUASHFS_I(inode)->start_block; + 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 %d, start block %x, 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 [%x:%x]\n", block, offset); + 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); unlock_page(page); @@ -1160,179 +1360,386 @@ 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->s_fs_info; + int i; + + down(&msblk->meta_index_mutex); -#ifdef SQUASHFS_1_0_COMPATIBILITY -static unsigned int read_blocklist_1(struct inode *inode, int index, int readahead_blks, - char *block_list, unsigned short **block_p, unsigned int *bsize) + TRACE("locate_meta_index: index %d, offset %d\n", index, offset); + + if(msblk->meta_index == NULL) + goto not_allocated; + + for (i = 0; i < SQUASHFS_META_NUMBER; i ++) + if (msblk->meta_index[i].inode_number == inode->i_ino && + msblk->meta_index[i].offset >= offset && + msblk->meta_index[i].offset <= index && + msblk->meta_index[i].locked == 0) { + TRACE("locate_meta_index: entry %d, offset %d\n", i, + msblk->meta_index[i].offset); + meta = &msblk->meta_index[i]; + offset = meta->offset; + } + + if (meta) + meta->locked = 1; + +not_allocated: + up(&msblk->meta_index_mutex); + + return meta; +} + + +struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) { - squashfs_sb_info *msBlk = (squashfs_sb_info *)inode->i_sb->s_fs_info; - unsigned short *block_listp; - int i = 0; - int block_ptr = SQUASHFS_I(inode)->block_list_start; - int offset = SQUASHFS_I(inode)->offset; - unsigned int block = SQUASHFS_I(inode)->start_block; + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; + struct meta_index *meta = NULL; + int i; - for(;;) { - int blocks = (index + readahead_blks - i); - if(blocks > (SIZE >> 1)) { - if((index - i) <= (SIZE >> 1)) - blocks = index - i; - else - blocks = SIZE >> 1; + 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 - *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 -static unsigned int read_blocklist(struct inode *inode, int index, int readahead_blks, - char *block_list, unsigned short **block_p, unsigned int *bsize) +void release_meta_index(struct inode *inode, struct meta_index *meta) { - squashfs_sb_info *msBlk = (squashfs_sb_info *)inode->i_sb->s_fs_info; + meta->locked = 0; +} + + +static int read_block_index(struct super_block *s, int blocks, char *block_list, + long long *start_block, int *offset) +{ + struct squashfs_sb_info *msblk = s->s_fs_info; unsigned int *block_listp; - int i = 0; - int block_ptr = SQUASHFS_I(inode)->block_list_start; - int offset = SQUASHFS_I(inode)->offset; - unsigned int block = SQUASHFS_I(inode)->start_block; + int block = 0; + + if (msblk->swap) { + char sblock_list[blocks << 2]; + + if (!squashfs_get_cached_block(s, sblock_list, *start_block, + *offset, blocks << 2, start_block, offset)) { + ERROR("Unable to read block list [%llx:%x]\n", + *start_block, *offset); + goto failure; + } + SQUASHFS_SWAP_INTS(((unsigned int *)block_list), + ((unsigned int *)sblock_list), blocks); + } else + if (!squashfs_get_cached_block(s, block_list, *start_block, + *offset, blocks << 2, start_block, offset)) { + ERROR("Unable to read block list [%llx:%x]\n", + *start_block, *offset); + goto failure; + } - for(;;) { - int blocks = (index + readahead_blks - i); - if(blocks > (SIZE >> 2)) { - if((index - i) <= (SIZE >> 2)) - blocks = index - i; - else - blocks = SIZE >> 2; + for (block_listp = (unsigned int *) block_list; blocks; + block_listp++, blocks --) + block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); + + return block; + +failure: + return -1; +} + + +#define SIZE 256 + +static inline int calculate_skip(int blocks) { + int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); + return skip >= 7 ? 7 : skip + 1; +} + + +static int get_meta_index(struct inode *inode, int index, + long long *index_block, int *index_offset, + long long *data_block, char *block_list) +{ + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); + int offset = 0; + struct meta_index *meta; + struct meta_entry *meta_entry; + long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; + int cur_offset = SQUASHFS_I(inode)->offset; + long long cur_data_block = SQUASHFS_I(inode)->start_block; + int i; + + index /= SQUASHFS_META_INDEXES * skip; + + while ( offset < index ) { + meta = locate_meta_index(inode, index, offset + 1); + + if (meta == NULL) { + if ((meta = empty_meta_index(inode, offset + 1, + skip)) == NULL) + goto all_done; + } else { + if(meta->entries == 0) + goto failed; + offset = index < meta->offset + meta->entries ? index : + meta->offset + meta->entries - 1; + meta_entry = &meta->meta_entry[offset - meta->offset]; + cur_index_block = meta_entry->index_block + sblk->inode_table_start; + cur_offset = meta_entry->offset; + cur_data_block = meta_entry->data_block; + TRACE("get_meta_index: offset %d, meta->offset %d, " + "meta->entries %d\n", offset, meta->offset, + meta->entries); + TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" + " data_block 0x%llx\n", cur_index_block, + cur_offset, cur_data_block); } - 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 (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 = (squashfs_sb_info *)inode->i_sb->s_fs_info; - 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_sb_info *msblk = inode->i_sb->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + unsigned char *block_list; + long long block; + unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; + int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); + void *pageaddr; struct squashfs_fragment_cache *fragment = NULL; - char *data_ptr = msBlk->read_page; + 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, - SQUASHFS_I(inode)->start_block); + TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", + page->index, + SQUASHFS_I(inode)->start_block); - if(page->index >= ((inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)) { + if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) { + ERROR("Failed to allocate block_list\n"); goto skip_read; } - if(SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK || 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, 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, sblk->block_size))) { + ERROR("Unable to read page, block %llx, size %x\n", block, + bsize); + up(&msblk->read_page_mutex); goto skip_read; } } else { - if((fragment = get_cached_fragment(inode->i_sb, SQUASHFS_I(inode)->u.s1.fragment_start_block, SQUASHFS_I(inode)->u.s1.fragment_size)) == NULL) { - ERROR("Unable to read page, block %x, size %x\n", SQUASHFS_I(inode)->u.s1.fragment_start_block, (int) SQUASHFS_I(inode)->u.s1.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 = SQUASHFS_I(inode)->u.s1.fragment_offset + (inode->i_size & (sBlk->block_size - 1)); + 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); - flush_dcache_page(page); - SetPageUptodate(page); - unlock_page(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); - flush_dcache_page(push_page); - SetPageUptodate(push_page); - unlock_page(push_page); + int avail = (bytes - byte_offset) > PAGE_CACHE_SIZE ? + PAGE_CACHE_SIZE : bytes - byte_offset; + + TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", + bytes, i, byte_offset, avail); + + push_page = (i == page->index) ? page : + grab_cache_page_nowait(page->mapping, i); + + if (!push_page) + continue; + + if (PageUptodate(push_page)) + goto skip_page; + + pageaddr = kmap_atomic(push_page, KM_USER0); + memcpy(pageaddr, data_ptr + byte_offset, avail); + memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); + kunmap_atomic(pageaddr, KM_USER0); + flush_dcache_page(push_page); + SetPageUptodate(push_page); +skip_page: + unlock_page(push_page); + if(i != page->index) page_cache_release(push_page); - } } - if(SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK || index < (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); + kfree(block_list); 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); unlock_page(page); + kfree(block_list); return 0; } @@ -1340,346 +1747,439 @@ skip_read: static int squashfs_readpage4K(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; - squashfs_sb_info *msBlk = (squashfs_sb_info *)inode->i_sb->s_fs_info; - squashfs_super_block *sBlk = &msBlk->sBlk; - unsigned char block_list[SIZE]; - unsigned int bsize, block, bytes = 0; - void *pageaddr = kmap(page); + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + unsigned char *block_list; + long long block; + unsigned int bsize, bytes = 0; + void *pageaddr; - TRACE("Entered squashfs_readpage4K, page index %x, start block %x\n", (unsigned int) page->index, - SQUASHFS_I(inode)->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 + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)) { + if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT)) { + block_list = NULL; goto skip_read; } - if(SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK || page->index < (inode->i_size >> sBlk->block_log)) { - block = (msBlk->read_blocklist)(inode, page->index, 1, block_list, NULL, &bsize); - - if(!(bytes = read_data(inode->i_sb, pageaddr, block, bsize, NULL))) - ERROR("Unable to read page, block %x, size %x\n", block, bsize); - } else { - struct squashfs_fragment_cache *fragment; - - 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 %x, size %x\n", SQUASHFS_I(inode)->u.s1.fragment_start_block, (int) SQUASHFS_I(inode)->u.s1.fragment_size); - else { - bytes = inode->i_size & (sBlk->block_size - 1); - memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->u.s1.fragment_offset, bytes); - release_cached_fragment(msBlk, fragment); - } + if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) { + ERROR("Failed to allocate block_list\n"); + goto skip_read; } -skip_read: - memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); - kunmap(page); - flush_dcache_page(page); - SetPageUptodate(page); - unlock_page(page); - - return 0; -} - - -#ifdef SQUASHFS_1_0_COMPATIBILITY -static int squashfs_readpage_lessthan4K(struct file *file, struct page *page) -{ - struct inode *inode = page->mapping->host; - squashfs_sb_info *msBlk = (squashfs_sb_info *)inode->i_sb->s_fs_info; - 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, - SQUASHFS_I(inode)->start_block); - - block = read_blocklist_1(inode, index, readahead_blks, block_list, &block_listp, NULL); - - if(i_end > file_blocks) - i_end = file_blocks; - - while(index < i_end) { - int c_byte = !SQUASHFS_COMPRESSED(*block_listp) ? SQUASHFS_COMPRESSED_SIZE(*block_listp) | SQUASHFS_COMPRESSED_BIT_BLOCK : *block_listp; - if(!(byte = read_data(inode->i_sb, pageaddr, block, c_byte, NULL))) { - ERROR("Unable to read page, block %x, size %x\n", block, *block_listp); + if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK + || page->index < (i_size_read(inode) >> + sblk->block_log)) { + block = (msblk->read_blocklist)(inode, page->index, 1, + block_list, NULL, &bsize); + if(block == 0) goto skip_read; - } - block += SQUASHFS_COMPRESSED_SIZE(*block_listp); - pageaddr += byte; - bytes += byte; - index ++; - block_listp ++; + + down(&msblk->read_page_mutex); + bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, + bsize, NULL, sblk->block_size); + if (bytes) { + pageaddr = kmap_atomic(page, KM_USER0); + memcpy(pageaddr, msblk->read_page, bytes); + kunmap_atomic(pageaddr, KM_USER0); + } else + ERROR("Unable to read page, block %llx, size %x\n", + block, bsize); + up(&msblk->read_page_mutex); + } else { + struct squashfs_fragment_cache *fragment = + get_cached_fragment(inode->i_sb, + SQUASHFS_I(inode)-> + u.s1.fragment_start_block, + SQUASHFS_I(inode)-> u.s1.fragment_size); + if (fragment) { + bytes = i_size_read(inode) & (sblk->block_size - 1); + pageaddr = kmap_atomic(page, KM_USER0); + memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> + u.s1.fragment_offset, bytes); + kunmap_atomic(pageaddr, KM_USER0); + release_cached_fragment(msblk, fragment); + } else + ERROR("Unable to read page, block %llx, size %x\n", + SQUASHFS_I(inode)-> + u.s1.fragment_start_block, (int) + SQUASHFS_I(inode)-> u.s1.fragment_size); } skip_read: - memset(pageaddr, 0, PAGE_CACHE_SIZE - bytes); - kunmap(page); + pageaddr = kmap_atomic(page, KM_USER0); + memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); + kunmap_atomic(pageaddr, KM_USER0); flush_dcache_page(page); SetPageUptodate(page); unlock_page(page); + kfree(block_list); return 0; } -#endif -static int get_dir_index_using_offset(struct super_block *s, unsigned int *next_block, - unsigned int *next_offset, unsigned int index_start, unsigned int index_offset, - int i_count, long long f_pos) +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) { - squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info; - squashfs_super_block *sBlk = &msBlk->sBlk; + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; int i, length = 0; - squashfs_dir_index index; + 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); + TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", + i_count, (unsigned int) f_pos); - if(f_pos == 0) - return 0; + f_pos =- 3; + if (f_pos == 0) + goto finish; - for(i = 0; i < i_count; i++) { - if(msBlk->swap) { - squashfs_dir_index sindex; - squashfs_get_cached_block(s, (char *) &sindex, index_start, index_offset, - sizeof(sindex), &index_start, &index_offset); + 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); + squashfs_get_cached_block(s, (char *) &index, + index_start, index_offset, + sizeof(index), &index_start, + &index_offset); - if(index.index > f_pos) + if (index.index > f_pos) break; squashfs_get_cached_block(s, NULL, index_start, index_offset, - index.size + 1, &index_start, &index_offset); + index.size + 1, &index_start, + &index_offset); length = index.index; - *next_block = index.start_block + sBlk->directory_table_start; + *next_block = index.start_block + sblk->directory_table_start; } *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; - return length; + +finish: + return length + 3; } -static int get_dir_index_using_name(struct super_block *s, unsigned int *next_block, - unsigned int *next_offset, unsigned int index_start, unsigned int index_offset, - int i_count, const char *name, int size) +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) { - squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info; - squashfs_super_block *sBlk = &msBlk->sBlk; + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; int i, length = 0; - char buffer[sizeof(squashfs_dir_index) + SQUASHFS_NAME_LEN + 1]; - squashfs_dir_index *index = (squashfs_dir_index *) buffer; - char str[SQUASHFS_NAME_LEN + 1]; + struct squashfs_dir_index *index; + char *str; TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); + if (!(str = kmalloc(sizeof(struct squashfs_dir_index) + + (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) { + ERROR("Failed to allocate squashfs_dir_index\n"); + goto failure; + } + + index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1); strncpy(str, name, size); str[size] = '\0'; - for(i = 0; i < i_count; i++) { - if(msBlk->swap) { - squashfs_dir_index sindex; - squashfs_get_cached_block(s, (char *) &sindex, index_start, index_offset, - sizeof(sindex), &index_start, &index_offset); + 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(squashfs_dir_index), &index_start, &index_offset); + 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); + 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) + if (strcmp(index->name, str) > 0) break; length = index->index; - *next_block = index->start_block + sBlk->directory_table_start; + *next_block = index->start_block + sblk->directory_table_start; } *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; - return length; + kfree(str); +failure: + return length + 3; } static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) { struct inode *i = file->f_dentry->d_inode; - squashfs_sb_info *msBlk = (squashfs_sb_info *)i->i_sb->s_fs_info; - squashfs_super_block *sBlk = &msBlk->sBlk; - int next_block = SQUASHFS_I(i)->start_block + sBlk->directory_table_start, next_offset = - SQUASHFS_I(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->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + long long next_block = SQUASHFS_I(i)->start_block + + sblk->directory_table_start; + int next_offset = SQUASHFS_I(i)->offset, length = 0, + dir_count; + struct squashfs_dir_header dirh; + struct squashfs_dir_entry *dire; + + TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); + + if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) + + SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) { + ERROR("Failed to allocate squashfs_dir_entry\n"); + goto finish; + } - TRACE("Entered squashfs_readdir [%x:%x]\n", next_block, next_offset); + while(file->f_pos < 3) { + char *name; + int size, i_ino; - lock_kernel(); + if(file->f_pos == 0) { + name = "."; + size = 1; + i_ino = i->i_ino; + } else { + name = ".."; + size = 2; + i_ino = SQUASHFS_I(i)->u.s2.parent_inode; + } + TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", + (unsigned int) dirent, name, size, (int) + file->f_pos, i_ino, + squashfs_filetype_table[1]); + + if (filldir(dirent, name, size, + file->f_pos, i_ino, + squashfs_filetype_table[1]) < 0) { + TRACE("Filldir returned less than 0\n"); + goto finish; + } + file->f_pos += size; + } - length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, SQUASHFS_I(i)->u.s2.directory_index_start, - SQUASHFS_I(i)->u.s2.directory_index_offset, SQUASHFS_I(i)->u.s2.directory_index_count, file->f_pos); + 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"); - unlock_kernel(); - return dirs_read; + goto finish; } - file->f_pos = length; - dirs_read ++; } } - unlock_kernel(); - return dirs_read; +finish: + kfree(dire); + return 0; failed_read: - unlock_kernel(); - 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); + kfree(dire); return 0; } -static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, struct nameidata *nd) +static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, + struct nameidata *nd) { - const unsigned 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 = (squashfs_sb_info *)i->i_sb->s_fs_info; - squashfs_super_block *sBlk = &msBlk->sBlk; - int next_block = SQUASHFS_I(i)->start_block + sBlk->directory_table_start, next_offset = - SQUASHFS_I(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; - int squashfs_2_1 = sBlk->s_major == 2 && sBlk->s_minor == 1; - - TRACE("Entered squashfs_lookup [%x:%x]\n", next_block, next_offset); + struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + long long next_block = SQUASHFS_I(i)->start_block + + sblk->directory_table_start; + int next_offset = SQUASHFS_I(i)->offset, length = 0, + dir_count; + struct squashfs_dir_header dirh; + struct squashfs_dir_entry *dire; + + TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); + + if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) + + SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) { + ERROR("Failed to allocate squashfs_dir_entry\n"); + goto exit_lookup; + } - lock_kernel(); + if (len > SQUASHFS_NAME_LEN) + goto exit_lookup; - length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, SQUASHFS_I(i)->u.s2.directory_index_start, - SQUASHFS_I(i)->u.s2.directory_index_offset, SQUASHFS_I(i)->u.s2.directory_index_count, name, len); + 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->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(squashfs_2_1 && name[0] < dire->name[0]) - goto exit_loop; + if (name[0] < dire->name[0]) + goto exit_lookup; - if((len == dire->size + 1) && !strncmp(name, dire->name, len)) { - squashfs_inode ino = SQUASHFS_MKINODE(dirh.start_block, dire->offset); + if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) { + squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block, + dire->offset); - TRACE("calling squashfs_iget for directory entry %s, inode %x:%x\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 = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number); - goto exit_loop; + goto exit_lookup; } } } -exit_loop: +exit_lookup: + kfree(dire); + if (inode) + return d_splice_alias(inode, dentry); d_add(dentry, inode); - unlock_kernel(); return ERR_PTR(0); failed_read: - ERROR("Unable to read directory block [%x:%x]\n", next_block, next_offset); - goto exit_loop; + ERROR("Unable to read directory block [%llx:%x]\n", next_block, + next_offset); + goto exit_lookup; } @@ -1687,62 +2187,59 @@ static void squashfs_put_super(struct super_block *s) { int i; - if(s->s_fs_info) { - squashfs_sb_info *sbi = (squashfs_sb_info *) s->s_fs_info; - if(sbi->block_cache) { - for(i = 0; i < SQUASHFS_CACHED_BLKS; i++) - if(sbi->block_cache[i].block != SQUASHFS_INVALID_BLK) + if (s->s_fs_info) { + struct squashfs_sb_info *sbi = s->s_fs_info; + if (sbi->block_cache) + for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) + if (sbi->block_cache[i].block != + SQUASHFS_INVALID_BLK) kfree(sbi->block_cache[i].data); - kfree(sbi->block_cache); - } - if(sbi->read_data) kfree(sbi->read_data); - if(sbi->read_page) kfree(sbi->read_page); - if(sbi->uid) kfree(sbi->uid); - if(sbi->fragment) { - for(i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) - if(sbi->fragment[i].data != NULL) - SQUASHFS_FREE(sbi->fragment[i].data); - kfree(sbi->fragment); - } - if(sbi->fragment_index) kfree(sbi->fragment_index); + if (sbi->fragment) + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) + SQUASHFS_FREE(sbi->fragment[i].data); + kfree(sbi->fragment); + kfree(sbi->block_cache); + kfree(sbi->read_data); + kfree(sbi->read_page); + kfree(sbi->uid); + kfree(sbi->fragment_index); + kfree(sbi->fragment_index_2); + kfree(sbi->meta_index); + vfree(sbi->stream.workspace); kfree(s->s_fs_info); s->s_fs_info = NULL; } } -static struct super_block *squashfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) +static int squashfs_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, + mnt); } static int __init init_squashfs_fs(void) { int err = init_inodecache(); - if(err) - return err; + if (err) + goto out; - printk(KERN_INFO "Squashfs 2.2-r2 (released 2005/09/08) (C) 2002-2005 Phillip Lougher\n"); + printk(KERN_INFO "squashfs: version 3.2-alpha (2006/12/12) " + "Phillip Lougher\n"); - if(!(stream.workspace = (char *) vmalloc(zlib_inflate_workspacesize()))) { - ERROR("Failed to allocate zlib workspace\n"); + if ((err = register_filesystem(&squashfs_fs_type))) destroy_inodecache(); - return -ENOMEM; - } - - if((err = register_filesystem(&squashfs_fs_type))) { - vfree(stream.workspace); - destroy_inodecache(); - } +out: return err; } static void __exit exit_squashfs_fs(void) { - vfree(stream.workspace); unregister_filesystem(&squashfs_fs_type); destroy_inodecache(); } @@ -1754,7 +2251,7 @@ static kmem_cache_t * squashfs_inode_cachep; static struct inode *squashfs_alloc_inode(struct super_block *sb) { struct squashfs_inode_info *ei; - ei = (struct squashfs_inode_info *)kmem_cache_alloc(squashfs_inode_cachep, SLAB_KERNEL); + ei = kmem_cache_alloc(squashfs_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; return &ei->vfs_inode; @@ -1769,20 +2266,20 @@ static void squashfs_destroy_inode(struct inode *inode) static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { - struct squashfs_inode_info *ei = (struct squashfs_inode_info *) foo; + struct squashfs_inode_info *ei = foo; if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == - SLAB_CTOR_CONSTRUCTOR) + SLAB_CTOR_CONSTRUCTOR) inode_init_once(&ei->vfs_inode); } -static int init_inodecache(void) +static int __init init_inodecache(void) { squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", - sizeof(struct squashfs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, - init_once, NULL); + sizeof(struct squashfs_inode_info), + 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + init_once, NULL); if (squashfs_inode_cachep == NULL) return -ENOMEM; return 0; @@ -1791,13 +2288,12 @@ static int init_inodecache(void) static void destroy_inodecache(void) { - if (kmem_cache_destroy(squashfs_inode_cachep)) - printk(KERN_INFO "squashfs_inode_cache: not all structures were freed\n"); + kmem_cache_destroy(squashfs_inode_cachep); } module_init(init_squashfs_fs); module_exit(exit_squashfs_fs); -MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem"); -MODULE_AUTHOR("Phillip Lougher "); +MODULE_DESCRIPTION("squashfs 3.2, a compressed read-only filesystem"); +MODULE_AUTHOR("Phillip Lougher "); MODULE_LICENSE("GPL");