📄 inode.c
字号:
/* * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004 Phillip Lougher <plougher@users.sourceforge.net> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Squashfs - a compressed read only filesystem for Linux * * inode.c */#define SQUASHFS_1_0_COMPATIBILITY#include <linux/types.h>#include <linux/squashfs_fs.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/locks.h>#include <linux/init.h>#include <linux/dcache.h>#include <asm/uaccess.h>#include <linux/wait.h>#include <asm/semaphore.h>#include <linux/zlib.h>#include <linux/blkdev.h>#include <linux/vmalloc.h>#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)static struct super_block *squashfs_read_super(struct super_block *, void *, int);static void squashfs_put_super(struct super_block *);static int squashfs_statfs(struct super_block *, struct statfs *);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 *);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);#ifdef SQUASHFS_1_0_COMPATIBILITYstatic 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);#endifDECLARE_MUTEX(read_data_mutex);static z_stream stream;static DECLARE_FSTYPE_DEV(squashfs_fs_type, "squashfs", squashfs_read_super);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 = { statfs: squashfs_statfs, put_super: squashfs_put_super,};static struct address_space_operations squashfs_symlink_aops = { readpage: squashfs_symlink_readpage};static struct address_space_operations squashfs_aops = { readpage: squashfs_readpage};static struct address_space_operations squashfs_aops_4K = { readpage: squashfs_readpage4K};#ifdef SQUASHFS_1_0_COMPATIBILITYstatic struct address_space_operations squashfs_aops_lessthan4K = { readpage: squashfs_readpage_lessthan4K};#endifstatic struct file_operations squashfs_dir_ops = { read: generic_read_dir, readdir: squashfs_readdir};static struct inode_operations squashfs_dir_inode_ops = { lookup: squashfs_lookup};static struct buffer_head *get_block_length(struct super_block *s, int *cur_index, int *offset, int *c_byte){ squashfs_sb_info *msBlk = &s->u.squashfs_sb; unsigned short temp; struct buffer_head *bh; if(!(bh = sb_bread(s, *cur_index))) return NULL; if(msBlk->devblksize - *offset == 1) { if(msBlk->swap) ((unsigned char *) &temp)[1] = *((unsigned char *) (bh->b_data + *offset)); else ((unsigned char *) &temp)[0] = *((unsigned char *) (bh->b_data + *offset)); brelse(bh); if(!(bh = sb_bread(s, ++(*cur_index)))) return NULL; if(msBlk->swap) ((unsigned char *) &temp)[0] = *((unsigned char *) bh->b_data); else ((unsigned char *) &temp)[1] = *((unsigned char *) bh->b_data); *c_byte = temp; *offset = 1; } else { if(msBlk->swap) { unsigned short temp; ((unsigned char *) &temp)[1] = *((unsigned char *) (bh->b_data + *offset)); ((unsigned char *) &temp)[0] = *((unsigned char *) (bh->b_data + *offset + 1)); *c_byte = temp; } else *c_byte = *((unsigned short *) (bh->b_data + *offset)); *offset += 2; } if(SQUASHFS_CHECK_DATA(msBlk->sBlk.flags)) { if(*offset == msBlk->devblksize) { brelse(bh); if(!(bh = sb_bread(s, ++(*cur_index)))) return NULL; offset = 0; } if(*((unsigned char *) (bh->b_data + *offset)) != SQUASHFS_MARKER_BYTE) { ERROR("Metadata block marker corrupt @ %x\n", *cur_index); brelse(bh); return NULL; } *offset ++; } return bh;}static unsigned int read_data(struct super_block *s, char *buffer, unsigned int index, unsigned int length, unsigned int *next_index){ squashfs_sb_info *msBlk = &s->u.squashfs_sb; struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> msBlk->devblksize_log2) + 2]; unsigned int offset = index & ((1 << msBlk->devblksize_log2) - 1); unsigned int cur_index = index >> msBlk->devblksize_log2; int bytes, avail_bytes, b = 0, k; char *c_buffer; unsigned int compressed; unsigned int c_byte = length; if(c_byte) { bytes = msBlk->devblksize - offset; compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); c_buffer = compressed ? msBlk->read_data : buffer; c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); TRACE("Block @ 0x%x, %scompressed size %d\n", index, compressed ? "" : "un", (unsigned int) c_byte); if(!(bh[0] = sb_getblk(s, cur_index))) goto block_release; for(b = 1; bytes < c_byte; b++) { if(!(bh[b] = sb_getblk(s, ++cur_index))) goto block_release; bytes += msBlk->devblksize; } ll_rw_block(READ, b, bh); } else { if(!(bh[0] = get_block_length(s, &cur_index, &offset, &c_byte))) goto read_failure; bytes = msBlk->devblksize - offset; compressed = SQUASHFS_COMPRESSED(c_byte); c_buffer = compressed ? msBlk->read_data : buffer; c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); TRACE("Block @ 0x%x, %scompressed size %d\n", index, compressed ? "" : "un", (unsigned int) c_byte); for(b = 1; bytes < c_byte; b++) { if(!(bh[b] = sb_getblk(s, ++cur_index))) goto block_release; bytes += msBlk->devblksize; } ll_rw_block(READ, b - 1, bh + 1); } if(compressed) down(&read_data_mutex); for(bytes = 0, k = 0; k < b; k++) { avail_bytes = (c_byte - bytes) > (msBlk->devblksize - offset) ? msBlk->devblksize - offset : c_byte - bytes; wait_on_buffer(bh[k]); memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes); bytes += avail_bytes; offset = 0; brelse(bh[k]); } /* * uncompress block */ if(compressed) { int zlib_err; stream.next_in = c_buffer; stream.avail_in = c_byte; stream.next_out = buffer; stream.avail_out = msBlk->read_size; if(((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || ((zlib_err = zlib_inflate(&stream, Z_FINISH)) != Z_STREAM_END) || ((zlib_err = zlib_inflateEnd(&stream)) != Z_OK)) { ERROR("zlib_fs returned unexpected result 0x%x\n", zlib_err); bytes = 0; } else bytes = stream.total_out; up(&read_data_mutex); } if(next_index) *next_index = index + c_byte + (length ? 0 : (SQUASHFS_CHECK_DATA(msBlk->sBlk.flags) ? 3 : 2)); return bytes;block_release: while(--b >= 0) brelse(bh[b]);read_failure: ERROR("sb_bread failed reading block 0x%x\n", cur_index); return 0;}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){ squashfs_sb_info *msBlk = &s->u.squashfs_sb; int n, i, bytes, return_length = length; unsigned int next_index; TRACE("Entered squashfs_get_cached_block [%x:%x]\n", block, offset); for(;;) { for(i = 0; i < SQUASHFS_CACHED_BLKS; i++) if(msBlk->block_cache[i].block == block) break; down(&msBlk->block_cache_mutex); if(i == SQUASHFS_CACHED_BLKS) { /* read inode header block */ for(i = msBlk->next_cache, n = SQUASHFS_CACHED_BLKS; n ; n --, i = (i + 1) % SQUASHFS_CACHED_BLKS) if(msBlk->block_cache[i].block != SQUASHFS_USED_BLK) break; if(n == 0) { up(&msBlk->block_cache_mutex); sleep_on(&msBlk->waitq); 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->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; } 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); } if(msBlk->block_cache[i].block != block) { up(&msBlk->block_cache_mutex); continue; } if((bytes = msBlk->block_cache[i].length - offset) >= length) { if(buffer) memcpy(buffer, msBlk->block_cache[i].data + offset, length); if(msBlk->block_cache[i].length - offset == length) { *next_block = msBlk->block_cache[i].next_index; *next_offset = 0; } else { *next_block = block; *next_offset = offset + length; } up(&msBlk->block_cache_mutex); return return_length; } else { if(buffer) { memcpy(buffer, msBlk->block_cache[i].data + offset, bytes); buffer += bytes; } block = msBlk->block_cache[i].next_index; up(&msBlk->block_cache_mutex); length -= bytes; offset = 0; } }}static int get_fragment_location(struct super_block *s, unsigned int fragment, unsigned int *fragment_start_block, unsigned int *fragment_size){ squashfs_sb_info *msBlk = &s->u.squashfs_sb; unsigned int start_block = msBlk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); squashfs_fragment_entry fragment_entry; if(msBlk->swap) { 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; 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; *fragment_start_block = fragment_entry.start_block; *fragment_size = fragment_entry.size; return 1;}void release_cached_fragment(squashfs_sb_info *msBlk, struct squashfs_fragment_cache *fragment){ down(&msBlk->fragment_mutex); fragment->locked --; wake_up(&msBlk->fragment_wait_queue); up(&msBlk->fragment_mutex);}struct squashfs_fragment_cache *get_cached_fragment(struct super_block *s, unsigned int start_block, int length){ int i, n; squashfs_sb_info *msBlk = &s->u.squashfs_sb; for(;;) { down(&msBlk->fragment_mutex); for(i = 0; i < SQUASHFS_CACHED_FRAGMENTS && msBlk->fragment[i].block != start_block; i++); if(i == SQUASHFS_CACHED_FRAGMENTS) { for(i = msBlk->next_fragment, n = SQUASHFS_CACHED_FRAGMENTS; n && msBlk->fragment[i].locked; n--, i = (i + 1) % SQUASHFS_CACHED_FRAGMENTS); if(n == 0) { up(&msBlk->fragment_mutex); sleep_on(&msBlk->fragment_wait_queue); continue; } msBlk->next_fragment = (msBlk->next_fragment + 1) % SQUASHFS_CACHED_FRAGMENTS; if(msBlk->fragment[i].data == NULL) if(!(msBlk->fragment[i].data = (unsigned char *) kmalloc(SQUASHFS_FILE_MAX_SIZE, GFP_KERNEL))) { ERROR("Failed to allocate fragment cache block\n"); up(&msBlk->fragment_mutex); return NULL; } 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 = start_block; TRACE("New fragment %d, start block %d, locked %d\n", i, msBlk->fragment[i].block, msBlk->fragment[i].locked);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -