2005-03-14 17:43:14

by Phillip Lougher

[permalink] [raw]
Subject: [PATCH][1/2] SquashFS

diff --new-file -urp linux-2.6.11.3/fs/squashfs/inode.c linux-2.6.11.3-squashfs/fs/squashfs/inode.c
--- linux-2.6.11.3/fs/squashfs/inode.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.11.3-squashfs/fs/squashfs/inode.c 2005-03-14 00:53:28.037568088 +0000
@@ -0,0 +1,1784 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005
+ * Phillip Lougher <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * inode.c
+ */
+
+#include <linux/types.h>
+#include <linux/squashfs_fs.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/squashfs_fs_sb.h>
+#include <linux/squashfs_fs_i.h>
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+#include <linux/init.h>
+#include <linux/dcache.h>
+#include <asm/uaccess.h>
+#include <linux/wait.h>
+#include <asm/semaphore.h>
+#include <linux/zlib.h>
+#include <linux/blkdev.h>
+#include <linux/vmalloc.h>
+#include "squashfs.h"
+
+static void squashfs_put_super(struct super_block *);
+static int squashfs_statfs(struct super_block *, 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 void squashfs_put_super(struct super_block *s);
+static struct inode *squashfs_alloc_inode(struct super_block *sb);
+static void squashfs_destroy_inode(struct inode *inode);
+static int init_inodecache(void);
+static void destroy_inodecache(void);
+static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
+ struct nameidata *);
+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode 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 struct super_block *squashfs_get_sb(struct file_system_type *, int,
+ const char *, void *);
+
+DECLARE_MUTEX(read_data_mutex);
+
+static z_stream stream;
+
+static struct file_system_type squashfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "squashfs",
+ .get_sb = squashfs_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV
+ };
+
+static unsigned char squashfs_filetype_table[] = {
+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+static struct super_operations squashfs_ops = {
+ .alloc_inode = squashfs_alloc_inode,
+ .destroy_inode = squashfs_destroy_inode,
+ .statfs = squashfs_statfs,
+ .put_super = squashfs_put_super,
+};
+
+struct address_space_operations squashfs_symlink_aops = {
+ .readpage = squashfs_symlink_readpage
+};
+
+struct address_space_operations squashfs_aops = {
+ .readpage = squashfs_readpage
+};
+
+struct address_space_operations squashfs_aops_4K = {
+ .readpage = squashfs_readpage4K
+};
+
+struct file_operations squashfs_dir_ops = {
+ .read = generic_read_dir,
+ .readdir = squashfs_readdir
+};
+
+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 = (squashfs_sb_info *)s->s_fs_info;
+ 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 char *) &temp)[1] = *((unsigned char *)
+ (bh->b_data + *offset));
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ (bh->b_data + *offset + 1));
+ } else {
+ ((unsigned char *) &temp)[0] = *((unsigned char *)
+ (bh->b_data + *offset));
+ ((unsigned char *) &temp)[1] = *((unsigned char *)
+ (bh->b_data + *offset + 1));
+ }
+ *c_byte = temp;
+ *offset += 2;
+ }
+
+ if (SQUASHFS_CHECK_DATA(msBlk->sBlk.flags)) {
+ if (*offset == msBlk->devblksize) {
+ brelse(bh);
+ if (!(bh = sb_bread(s, ++(*cur_index))))
+ 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;
+}
+
+
+unsigned int squashfs_read_data(struct super_block *s, char *buffer,
+ unsigned int index, unsigned int length,
+ unsigned int *next_index)
+{
+ 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;
+ 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]);
+ if (!buffer_uptodate(bh[k]))
+ goto block_release;
+ memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes);
+ bytes += avail_bytes;
+ offset = 0;
+ brelse(bh[k]);
+ }
+
+ /*
+ * uncompress block
+ */
+ if (compressed) {
+ int zlib_err;
+
+ stream.next_in = c_buffer;
+ stream.avail_in = c_byte;
+ stream.next_out = buffer;
+ stream.avail_out = msBlk->read_size;
+
+ if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) ||
+ ((zlib_err = zlib_inflate(&stream, Z_FINISH))
+ != Z_STREAM_END) || ((zlib_err =
+ zlib_inflateEnd(&stream)) != Z_OK)) {
+ ERROR("zlib_fs returned unexpected result 0x%x\n",
+ zlib_err);
+ bytes = 0;
+ } else
+ bytes = stream.total_out;
+
+ up(&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;
+}
+
+
+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 = (squashfs_sb_info *)s->s_fs_info;
+ 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) {
+ wait_queue_t wait;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&msBlk->waitq, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ up(&msBlk->block_cache_mutex);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&msBlk->waitq, &wait);
+ continue;
+ }
+ msBlk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
+
+ if (msBlk->block_cache[i].block ==
+ SQUASHFS_INVALID_BLK) {
+ if (!(msBlk->block_cache[i].data =
+ (unsigned char *)
+ kmalloc(SQUASHFS_METADATA_SIZE,
+ GFP_KERNEL))) {
+ ERROR("Failed to allocate cache"
+ "block\n");
+ up(&msBlk->block_cache_mutex);
+ return 0;
+ }
+ }
+
+ msBlk->block_cache[i].block = SQUASHFS_USED_BLK;
+ up(&msBlk->block_cache_mutex);
+
+ if (!(msBlk->block_cache[i].length =
+ squashfs_read_data(s,
+ msBlk->block_cache[i].data,
+ block, 0, &next_index))) {
+ ERROR("Unable to read cache block [%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 = (squashfs_sb_info *)s->s_fs_info;
+ 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;
+}
+
+
+static 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);
+}
+
+
+static struct squashfs_fragment_cache *get_cached_fragment(struct super_block
+ *s, unsigned int start_block,
+ int length)
+{
+ int i, n;
+ squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info;
+
+ 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) {
+ wait_queue_t wait;
+
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&msBlk->fragment_wait_queue,
+ &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ up(&msBlk->fragment_mutex);
+ schedule();
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&msBlk->fragment_wait_queue,
+ &wait);
+ continue;
+ }
+ msBlk->next_fragment = (msBlk->next_fragment + 1) %
+ SQUASHFS_CACHED_FRAGMENTS;
+
+ if (msBlk->fragment[i].data == NULL)
+ if (!(msBlk->fragment[i].data =
+ (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 = squashfs_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);
+ return &msBlk->fragment[i];
+ }
+
+ 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];
+ }
+}
+
+
+static struct inode *squashfs_new_inode(struct super_block *s,
+ squashfs_base_inode_header *inodeb, unsigned int ino)
+{
+ squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info;
+ squashfs_super_block *sBlk = &msBlk->sBlk;
+ struct inode *i = new_inode(s);
+
+ if (i) {
+ i->i_ino = ino;
+ i->i_mtime.tv_sec = sBlk->mkfs_time;
+ i->i_atime.tv_sec = sBlk->mkfs_time;
+ i->i_ctime.tv_sec = sBlk->mkfs_time;
+ i->i_uid = msBlk->uid[inodeb->uid];
+ i->i_mode = inodeb->mode;
+ i->i_nlink = 1;
+ i->i_size = 0;
+ if (inodeb->guid == SQUASHFS_GUIDS)
+ i->i_gid = i->i_uid;
+ else
+ i->i_gid = msBlk->guid[inodeb->guid];
+ }
+
+ return i;
+}
+
+
+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode inode)
+{
+ struct inode *i;
+ 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 ino = SQUASHFS_MK_VFS_INODE(block
+ - sBlk->inode_table_start, offset);
+ 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))
+ goto failed_read;
+ SQUASHFS_SWAP_BASE_INODE_HEADER(&inodeb, &sinodeb,
+ sizeof(sinodeb));
+ } else
+ if (!squashfs_get_cached_block(s, (char *) &inodeb, block,
+ offset, sizeof(inodeb), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ switch(inodeb.inode_type) {
+ case SQUASHFS_FILE_TYPE: {
+ squashfs_reg_inode_header inodep;
+ unsigned int frag_blk, frag_size;
+
+ if (msBlk->swap) {
+ squashfs_reg_inode_header sinodep;
+
+ if (!squashfs_get_cached_block(s, (char *)
+ &sinodep, block, offset,
+ sizeof(sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_REG_INODE_HEADER(&inodep,
+ &sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ &inodep, block, offset,
+ sizeof(inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ frag_blk = SQUASHFS_INVALID_BLK;
+ if (inodep.fragment != SQUASHFS_INVALID_BLK &&
+ !get_fragment_location(s,
+ inodep.fragment, &frag_blk, &frag_size))
+ goto failed_read;
+
+ if((i = squashfs_new_inode(s, &inodeb, ino)) == NULL)
+ goto failed_read1;
+
+ i->i_size = inodep.file_size;
+ i->i_fop = &generic_ro_fops;
+ i->i_mode |= S_IFREG;
+ i->i_mtime.tv_sec = inodep.mtime;
+ i->i_atime.tv_sec = inodep.mtime;
+ i->i_ctime.tv_sec = inodep.mtime;
+ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+ i->i_blksize = PAGE_CACHE_SIZE;
+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep.offset;
+ SQUASHFS_I(i)->start_block = inodep.start_block;
+ SQUASHFS_I(i)->block_list_start = next_block;
+ SQUASHFS_I(i)->offset = next_offset;
+ if (sBlk->block_size > 4096)
+ i->i_data.a_ops = &squashfs_aops;
+ else
+ i->i_data.a_ops = &squashfs_aops_4K;
+
+ TRACE("File inode %x:%x, start_block %x, "
+ "block_list_start %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 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))
+ goto failed_read;
+ SQUASHFS_SWAP_DIR_INODE_HEADER(&inodep,
+ &sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ &inodep, block, offset,
+ sizeof(inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ if((i = squashfs_new_inode(s, &inodeb, ino)) == NULL)
+ goto failed_read1;
+
+ 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_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))
+ goto failed_read;
+ SQUASHFS_SWAP_LDIR_INODE_HEADER(&inodep,
+ &sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ &inodep, block, offset,
+ sizeof(inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ if((i = squashfs_new_inode(s, &inodeb, ino)) == NULL)
+ goto failed_read1;
+
+ 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_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);
+ break;
+ }
+ case SQUASHFS_SYMLINK_TYPE: {
+ squashfs_symlink_inode_header inodep;
+
+ if (msBlk->swap) {
+ squashfs_symlink_inode_header sinodep;
+
+ if (!squashfs_get_cached_block(s, (char *)
+ &sinodep, block, offset,
+ sizeof(sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(&inodep,
+ &sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ &inodep, block, offset,
+ sizeof(inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ if((i = squashfs_new_inode(s, &inodeb, ino)) == NULL)
+ goto failed_read1;
+
+ i->i_size = inodep.symlink_size;
+ i->i_op = &page_symlink_inode_operations;
+ i->i_data.a_ops = &squashfs_symlink_aops;
+ i->i_mode |= S_IFLNK;
+ SQUASHFS_I(i)->start_block = next_block;
+ SQUASHFS_I(i)->offset = next_offset;
+
+ TRACE("Symbolic link inode %x:%x, start_block %x, "
+ "offset %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ next_block, next_offset);
+ break;
+ }
+ case SQUASHFS_BLKDEV_TYPE:
+ case SQUASHFS_CHRDEV_TYPE: {
+ squashfs_dev_inode_header inodep;
+
+ if (msBlk->swap) {
+ squashfs_dev_inode_header sinodep;
+
+ if (!squashfs_get_cached_block(s, (char *)
+ &sinodep, block, offset,
+ sizeof(sinodep), &next_block,
+ &next_offset))
+ goto failed_read;
+ SQUASHFS_SWAP_DEV_INODE_HEADER(&inodep,
+ &sinodep);
+ } else
+ if (!squashfs_get_cached_block(s, (char *)
+ &inodep, block, offset,
+ sizeof(inodep), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ if((i = squashfs_new_inode(s, &inodeb, ino)) == NULL)
+ goto failed_read1;
+
+ i->i_mode |= (inodeb.inode_type ==
+ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR :
+ S_IFBLK;
+ init_special_inode(i, i->i_mode,
+ old_decode_dev(inodep.rdev));
+
+ TRACE("Device inode %x:%x, rdev %x\n",
+ SQUASHFS_INODE_BLK(inode), offset,
+ inodep.rdev);
+ break;
+ }
+ case SQUASHFS_FIFO_TYPE:
+ case SQUASHFS_SOCKET_TYPE: {
+ if((i = squashfs_new_inode(s, &inodeb, ino)) == NULL)
+ goto failed_read1;
+ i->i_mode |= (inodeb.inode_type == SQUASHFS_FIFO_TYPE) ?
+ S_IFIFO : S_IFSOCK;
+ init_special_inode(i, i->i_mode, 0);
+ break;
+ }
+ default:
+ ERROR("Unknown inode type %d in squashfs_iget!\n",
+ inodeb.inode_type);
+ goto failed_read1;
+ }
+
+ insert_inode_hash(i);
+ return i;
+
+failed_read:
+ ERROR("Unable to read inode [%x:%x]\n", block, offset);
+
+failed_read1:
+ return NULL;
+}
+
+
+static int supported_squashfs_filesystem(squashfs_sb_info *msBlk, int silent)
+{
+ squashfs_super_block *sBlk = &msBlk->sBlk;
+
+ 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 != 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;
+ } else {
+ msBlk->iget = squashfs_iget;
+ msBlk->read_blocklist = read_blocklist;
+ }
+
+ return 1;
+}
+
+
+static int squashfs_fill_super(struct super_block *s, void *data, int silent)
+{
+ squashfs_sb_info *msBlk;
+ 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))) {
+ ERROR("Failed to allocate superblock\n");
+ return -ENOMEM;
+ }
+ memset(s->s_fs_info, 0, sizeof(squashfs_sb_info));
+ msBlk = (squashfs_sb_info *) s->s_fs_info;
+ sBlk = &msBlk->sBlk;
+
+ msBlk->devblksize = sb_min_blocksize(s, BLOCK_SIZE);
+ msBlk->devblksize_log2 = ffz(~msBlk->devblksize);
+
+ init_MUTEX(&msBlk->read_page_mutex);
+ init_MUTEX(&msBlk->block_cache_mutex);
+ init_MUTEX(&msBlk->fragment_mutex);
+
+ init_waitqueue_head(&msBlk->waitq);
+ init_waitqueue_head(&msBlk->fragment_wait_queue);
+
+ if (!squashfs_read_data(s, (char *) sBlk, SQUASHFS_START,
+ sizeof(squashfs_super_block) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+ SERROR("unable to read superblock\n");
+ goto failed_mount;
+ }
+
+ /* Check it is a SQUASHFS superblock */
+ msBlk->swap = 0;
+ if ((s->s_magic = sBlk->s_magic) != SQUASHFS_MAGIC) {
+ if (sBlk->s_magic == SQUASHFS_MAGIC_SWAP) {
+ squashfs_super_block sblk;
+
+ WARNING("Mounting a different endian SQUASHFS "
+ "filesystem on %s\n", bdevname(s->s_bdev, b));
+
+ SQUASHFS_SWAP_SUPER_BLOCK(&sblk, sBlk);
+ memcpy(sBlk, &sblk, sizeof(squashfs_super_block));
+ msBlk->swap = 1;
+ } else {
+ SERROR("Can't find a SQUASHFS superblock on %s\n",
+ bdevname(s->s_bdev, b));
+ goto failed_mount;
+ }
+ }
+
+ /* Check the MAJOR & MINOR versions */
+ if(!supported_squashfs_filesystem(msBlk, silent))
+ goto failed_mount;
+
+ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
+ TRACE("Inodes are %scompressed\n",
+ SQUASHFS_UNCOMPRESSED_INODES
+ (sBlk->flags) ? "un" : "");
+ TRACE("Data is %scompressed\n",
+ SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags)
+ ? "un" : "");
+ TRACE("Check data is %s present in the filesystem\n",
+ SQUASHFS_CHECK_DATA(sBlk->flags) ?
+ "" : "not");
+ TRACE("Filesystem size %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);
+
+ s->s_flags |= MS_RDONLY;
+ s->s_op = &squashfs_ops;
+
+ /* Init inode_table block pointer array */
+ if (!(msBlk->block_cache = (squashfs_cache *)
+ kmalloc(sizeof(squashfs_cache) *
+ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
+ ERROR("Failed to allocate block cache\n");
+ goto failed_mount;
+ }
+
+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
+ msBlk->block_cache[i].block = SQUASHFS_INVALID_BLK;
+
+ msBlk->next_cache = 0;
+
+ /* Allocate read_data block */
+ msBlk->read_size = (sBlk->block_size < SQUASHFS_METADATA_SIZE) ?
+ SQUASHFS_METADATA_SIZE :
+ sBlk->block_size;
+
+ if (!(msBlk->read_data = (char *) kmalloc(msBlk->read_size,
+ GFP_KERNEL))) {
+ ERROR("Failed to allocate read_data block\n");
+ 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_mount;
+ }
+ } else
+ msBlk->read_page = NULL;
+
+ /* Allocate uid and gid tables */
+ if (!(msBlk->uid = (squashfs_uid *) kmalloc((sBlk->no_uids +
+ sBlk->no_guids) * sizeof(squashfs_uid),
+ GFP_KERNEL))) {
+ ERROR("Failed to allocate uid/gid table\n");
+ goto failed_mount;
+ }
+ msBlk->guid = msBlk->uid + sBlk->no_uids;
+
+ if (msBlk->swap) {
+ squashfs_uid suid[sBlk->no_uids + sBlk->no_guids];
+
+ if (!squashfs_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_mount;
+ }
+
+ SQUASHFS_SWAP_DATA(msBlk->uid, suid, (sBlk->no_uids +
+ sBlk->no_guids), (sizeof(squashfs_uid) * 8));
+ } else
+ if (!squashfs_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_mount;
+ }
+
+
+ if (sBlk->s_major == 1 && squashfs_1_0_supported(msBlk))
+ goto allocate_root;
+
+ if (!(msBlk->fragment = (struct squashfs_fragment_cache *)
+ kmalloc(sizeof(struct
+ squashfs_fragment_cache) *
+ SQUASHFS_CACHED_FRAGMENTS,
+ GFP_KERNEL))) {
+ ERROR("Failed to allocate fragment block cache\n");
+ goto failed_mount;
+ }
+
+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
+ msBlk->fragment[i].locked = 0;
+ msBlk->fragment[i].block = SQUASHFS_INVALID_BLK;
+ msBlk->fragment[i].data = NULL;
+ }
+
+ msBlk->next_fragment = 0;
+
+ /* Allocate fragment index table */
+ if (!(msBlk->fragment_index = (squashfs_fragment_index *)
+ kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES
+ (sBlk->fragments), GFP_KERNEL))) {
+ ERROR("Failed to allocate uid/gid table\n");
+ goto failed_mount;
+ }
+
+ if (SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments) &&
+ !squashfs_read_data(s, (char *)
+ msBlk->fragment_index,
+ sBlk->fragment_table_start,
+ SQUASHFS_FRAGMENT_INDEX_BYTES
+ (sBlk->fragments) |
+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+ SERROR("unable to read fragment index table\n");
+ goto failed_mount;
+ }
+
+ if (msBlk->swap) {
+ int i;
+ squashfs_fragment_index fragment;
+
+ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments);
+ i++) {
+ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
+ &msBlk->fragment_index[i], 1);
+ msBlk->fragment_index[i] = fragment;
+ }
+ }
+
+allocate_root:
+ if ((root = (msBlk->iget)(s, sBlk->root_inode)) == NULL)
+ goto failed_mount;
+
+ if ((s->s_root = d_alloc_root(root)) == NULL) {
+ ERROR("Root inode create failed\n");
+ iput(root);
+ goto failed_mount;
+ }
+
+ TRACE("Leaving squashfs_read_super\n");
+ return 0;
+
+failed_mount:
+ kfree(msBlk->fragment_index);
+ kfree(msBlk->fragment);
+ kfree(msBlk->uid);
+ kfree(msBlk->read_page);
+ kfree(msBlk->read_data);
+ kfree(msBlk->block_cache);
+ kfree(s->s_fs_info);
+ s->s_fs_info = NULL;
+ return -EINVAL;
+}
+
+
+static int squashfs_statfs(struct super_block *s, struct kstatfs *buf)
+{
+ squashfs_super_block *sBlk = &((squashfs_sb_info *)s->s_fs_info)->sBlk;
+
+ TRACE("Entered squashfs_statfs\n");
+
+ buf->f_type = SQUASHFS_MAGIC;
+ buf->f_bsize = sBlk->block_size;
+ buf->f_blocks = ((sBlk->bytes_used - 1) >> sBlk->block_log) + 1;
+ buf->f_bfree = buf->f_bavail = 0;
+ buf->f_files = sBlk->inodes;
+ buf->f_ffree = 0;
+ buf->f_namelen = SQUASHFS_NAME_LEN;
+
+ return 0;
+}
+
+
+static int squashfs_symlink_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
+ unsigned int 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);
+ goto skip_read;
+ }
+ }
+
+ 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);
+
+skip_read:
+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+ kunmap(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+
+ return 0;
+}
+
+
+#define SIZE 256
+
+static unsigned int read_blocklist(struct inode *inode, int index,
+ int readahead_blks, char *block_list,
+ unsigned short **block_p, unsigned int *bsize)
+{
+ squashfs_sb_info *msBlk = (squashfs_sb_info *)inode->i_sb->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;
+
+ for (;;) {
+ int blocks = (index + readahead_blks - i);
+ if (blocks > (SIZE >> 2)) {
+ if ((index - i) <= (SIZE >> 2))
+ blocks = index - i;
+ else
+ blocks = SIZE >> 2;
+ }
+
+ if (msBlk->swap) {
+ unsigned char sblock_list[SIZE];
+
+ if (!squashfs_get_cached_block(inode->i_sb, (char *)
+ sblock_list, block_ptr,
+ offset, blocks << 2, &block_ptr,
+ &offset)) {
+ ERROR("Unable to read block list [%d:%x]\n",
+ block_ptr, offset);
+ return 0;
+ }
+ SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
+ ((unsigned int *)sblock_list), blocks);
+ } else
+ if (!squashfs_get_cached_block(inode->i_sb, (char *)
+ block_list, block_ptr, offset,
+ blocks << 2, &block_ptr,
+ &offset)) {
+ ERROR("Unable to read block list [%d:%x]\n",
+ block_ptr, offset);
+ return 0;
+ }
+
+ for (block_listp = (unsigned int *) block_list; i < index &&
+ blocks; i ++, block_listp ++, blocks --)
+ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
+
+ if (blocks >= readahead_blks)
+ break;
+ }
+
+ *bsize = *block_listp;
+ return block;
+}
+
+
+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_fragment_cache *fragment = NULL;
+ char *data_ptr = msBlk->read_page;
+
+ int mask = (1 << (sBlk->block_log - PAGE_CACHE_SHIFT)) - 1;
+ int start_index = page->index & ~mask;
+ int end_index = start_index | mask;
+
+ TRACE("Entered squashfs_readpage, page index %x, start block %x\n",
+ (unsigned int) page->index,
+ SQUASHFS_I(inode)->start_block);
+
+ if (page->index >= ((inode->i_size + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT)) {
+ 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)
+ goto skip_read;
+
+ down(&msBlk->read_page_mutex);
+
+ if (!(bytes = squashfs_read_data(inode->i_sb, msBlk->read_page,
+ block, bsize, NULL))) {
+ ERROR("Unable to read page, block %x, 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);
+ goto skip_read;
+ }
+ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
+ (inode->i_size & (sBlk->block_size
+ - 1));
+ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
+ data_ptr = fragment->data;
+ }
+
+ for (i = start_index; i <= end_index && byte_offset < bytes;
+ i++, byte_offset += PAGE_CACHE_SIZE) {
+ struct page *push_page;
+ int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
+ PAGE_CACHE_SIZE : bytes - byte_offset;
+
+ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
+ bytes, i, byte_offset, available_bytes);
+
+ if (i == page->index) {
+ 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);
+ 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);
+ else
+ release_cached_fragment(msBlk, fragment);
+
+ return 0;
+
+skip_read:
+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+ kunmap(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+
+ return 0;
+}
+
+
+static int squashfs_readpage4K(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ 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);
+
+ TRACE("Entered squashfs_readpage4K, page index %x, start block %x\n",
+ (unsigned int) page->index,
+ SQUASHFS_I(inode)->start_block);
+
+ if (page->index >= ((inode->i_size + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT)) {
+ 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 = squashfs_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);
+ }
+ }
+
+skip_read:
+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+ kunmap(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+
+ return 0;
+}
+
+
+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)
+{
+ squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info;
+ squashfs_super_block *sBlk = &msBlk->sBlk;
+ int i, length = 0;
+ squashfs_dir_index index;
+
+ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
+ i_count, (unsigned int) f_pos);
+
+ if (f_pos == 0)
+ return 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);
+ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
+ } else
+ squashfs_get_cached_block(s, (char *) &index,
+ index_start, index_offset,
+ sizeof(index), &index_start,
+ &index_offset);
+
+ if (index.index > f_pos)
+ break;
+
+ squashfs_get_cached_block(s, NULL, index_start, index_offset,
+ index.size + 1, &index_start,
+ &index_offset);
+
+ length = index.index;
+ *next_block = index.start_block + sBlk->directory_table_start;
+ }
+
+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+ return length;
+}
+
+
+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)
+{
+ squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info;
+ 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];
+
+ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
+
+ strncpy(str, name, size);
+ str[size] = '\0';
+
+ for (i = 0; i < i_count; i++) {
+ if (msBlk->swap) {
+ 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, index->name, index_start,
+ index_offset, index->size + 1,
+ &index_start, &index_offset);
+
+ index->name[index->size + 1] = '\0';
+
+ if (strcmp(index->name, str) > 0)
+ break;
+
+ length = index->index;
+ *next_block = index->start_block + sBlk->directory_table_start;
+ }
+
+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+ return length;
+}
+
+
+static int squashfs_readdir(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;
+
+ TRACE("Entered squashfs_readdir [%x:%x]\n", next_block, next_offset);
+
+ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_start,
+ SQUASHFS_I(i)->u.s2.directory_index_offset,
+ SQUASHFS_I(i)->u.s2.directory_index_count,
+ file->f_pos);
+
+ while (length < i->i_size) {
+ /* 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))
+ goto failed_read;
+
+ length += sizeof(sdirh);
+ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+ next_block, next_offset, sizeof(dirh),
+ &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(dirh);
+ }
+
+ dir_count = dirh.count + 1;
+ while (dir_count--) {
+ if (msBlk->swap) {
+ squashfs_dir_entry sdire;
+ if (!squashfs_get_cached_block(i->i_sb, (char *)
+ &sdire, next_block, next_offset,
+ sizeof(sdire), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdire);
+ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, (char *)
+ dire, next_block, next_offset,
+ sizeof(*dire), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += sizeof(*dire);
+ }
+
+ if (!squashfs_get_cached_block(i->i_sb, dire->name,
+ next_block, next_offset,
+ dire->size + 1, &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += dire->size + 1;
+
+ if (file->f_pos >= length)
+ continue;
+
+ dire->name[dire->size + 1] = '\0';
+
+ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
+ (unsigned int) dirent, dire->name,
+ dire->size + 1, (int) file->f_pos,
+ dirh.start_block, dire->offset,
+ squashfs_filetype_table[dire->type]);
+
+ if (filldir(dirent, dire->name, dire->size + 1,
+ file->f_pos,
+ SQUASHFS_MK_VFS_INODE(dirh.start_block,
+ dire->offset),
+ squashfs_filetype_table[dire->type])
+ < 0) {
+ TRACE("Filldir returned less than 0\n");
+ return dirs_read;
+ }
+ file->f_pos = length;
+ dirs_read ++;
+ }
+ }
+
+ return dirs_read;
+
+failed_read:
+ ERROR("Unable to read directory block [%x:%x]\n", next_block,
+ next_offset);
+ return 0;
+}
+
+
+static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ const unsigned char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ struct inode *inode = NULL;
+ 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 sorted = sBlk->s_major == 2 && sBlk->s_minor >= 1;
+
+ TRACE("Entered squashfs_lookup [%x:%x]\n", next_block, next_offset);
+
+ 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) {
+ /* 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))
+ goto failed_read;
+
+ length += sizeof(sdirh);
+ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+ next_block, next_offset, sizeof(dirh),
+ &next_block, &next_offset))
+ goto failed_read;
+
+ length += sizeof(dirh);
+ }
+
+ dir_count = dirh.count + 1;
+ while (dir_count--) {
+ if (msBlk->swap) {
+ squashfs_dir_entry sdire;
+ if (!squashfs_get_cached_block(i->i_sb, (char *)
+ &sdire, next_block,next_offset,
+ sizeof(sdire), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += sizeof(sdire);
+ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
+ } else {
+ if (!squashfs_get_cached_block(i->i_sb, (char *)
+ dire, next_block,next_offset,
+ sizeof(*dire), &next_block,
+ &next_offset))
+ goto failed_read;
+
+ length += sizeof(*dire);
+ }
+
+ if (!squashfs_get_cached_block(i->i_sb, dire->name,
+ next_block, next_offset, dire->size + 1,
+ &next_block, &next_offset))
+ goto failed_read;
+
+ length += dire->size + 1;
+
+ if (sorted && name[0] < dire->name[0])
+ goto exit_loop;
+
+ if ((len == dire->size + 1) && !strncmp(name,
+ dire->name, len)) {
+ squashfs_inode 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);
+
+ inode = (msBlk->iget)(i->i_sb, ino);
+
+ goto exit_loop;
+ }
+ }
+ }
+
+exit_loop:
+ d_add(dentry, inode);
+ return ERR_PTR(0);
+
+failed_read:
+ ERROR("Unable to read directory block [%x:%x]\n", next_block,
+ next_offset);
+ goto exit_loop;
+}
+
+
+static void squashfs_put_super(struct super_block *s)
+{
+ int i;
+
+ if (s->s_fs_info) {
+ 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)
+ kfree(sbi->block_cache[i].data);
+ if (sbi->fragment)
+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++)
+ kfree(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(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)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super);
+}
+
+
+static int __init init_squashfs_fs(void)
+{
+ int err = init_inodecache();
+ if (err)
+ return err;
+
+ printk(KERN_INFO "squashfs: version 2.1 (2005/03/14) "
+ "Phillip Lougher\n");
+
+ if (!(stream.workspace = (char *)
+ vmalloc(zlib_inflate_workspacesize()))) {
+ ERROR("Failed to allocate zlib workspace\n");
+ destroy_inodecache();
+ return -ENOMEM;
+ }
+
+ if ((err = register_filesystem(&squashfs_fs_type))) {
+ vfree(stream.workspace);
+ destroy_inodecache();
+ }
+
+ return err;
+}
+
+
+static void __exit exit_squashfs_fs(void)
+{
+ vfree(stream.workspace);
+ unregister_filesystem(&squashfs_fs_type);
+ destroy_inodecache();
+}
+
+
+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);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+
+static void squashfs_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode));
+}
+
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct squashfs_inode_info *ei = (struct squashfs_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(&ei->vfs_inode);
+}
+
+
+static int 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);
+ if (squashfs_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+
+static void destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(squashfs_inode_cachep))
+ printk(KERN_INFO "squashfs_inode_cache: not all structures "
+ "were freed\n");
+}
+
+
+module_init(init_squashfs_fs);
+module_exit(exit_squashfs_fs);
+MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem");
+MODULE_AUTHOR("Phillip Lougher <[email protected]>");
+MODULE_LICENSE("GPL");


Attachments:
patch1 (50.25 kB)

2005-03-15 00:43:49

by Matt Mackall

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

A quick skim...

> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + *
> + * inode.c
> + */
> +
> +#include <linux/types.h>
> +#include <linux/squashfs_fs.h>
> +#include <linux/module.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/fs.h>
> +#include <linux/smp_lock.h>
> +#include <linux/slab.h>
> +#include <linux/squashfs_fs_sb.h>
> +#include <linux/squashfs_fs_i.h>
> +#include <linux/buffer_head.h>
> +#include <linux/vfs.h>
> +#include <linux/init.h>
> +#include <linux/dcache.h>
> +#include <asm/uaccess.h>
> +#include <linux/wait.h>
> +#include <asm/semaphore.h>
> +#include <linux/zlib.h>
> +#include <linux/blkdev.h>
> +#include <linux/vmalloc.h>
> +#include "squashfs.h"

Please put all the asm/ bits after all the linux/ bits.
> +
> +static void squashfs_put_super(struct super_block *);
> +static int squashfs_statfs(struct super_block *, 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 void squashfs_put_super(struct super_block *s);
> +static struct inode *squashfs_alloc_inode(struct super_block *sb);
> +static void squashfs_destroy_inode(struct inode *inode);
> +static int init_inodecache(void);
> +static void destroy_inodecache(void);
> +static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
> + struct nameidata *);
> +static struct inode *squashfs_iget(struct super_block *s, squashfs_inode 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 struct super_block *squashfs_get_sb(struct file_system_type *, int,
> + const char *, void *);

Would be nice to reorder things so that fewer forward declarations
were needed.

> +static z_stream stream;
> +
> +static struct file_system_type squashfs_fs_type = {
> + .owner = THIS_MODULE,
> + .name = "squashfs",
> + .get_sb = squashfs_get_sb,
> + .kill_sb = kill_block_super,
> + .fs_flags = FS_REQUIRES_DEV
> + };
^^^^
Weird whitespace.

> +static struct buffer_head *get_block_length(struct super_block *s,
> + int *cur_index, int *offset, int *c_byte)
> +{
> + squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info;

Needless cast from void *. Mixed case identifiers are discouraged.

> + if (!(bh = sb_bread(s, *cur_index)))
> + return NULL;

Please don't do assignment inside if().

> + 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));

That's rather ugly, what's going on here? There seems to be a lot of
this swapping going on. At the very least, let's use u8.

> +block_release:
> + while (--b >= 0) brelse(bh[b]);

Linebreak.

> + for (i = msBlk->next_cache, n = SQUASHFS_CACHED_BLKS;
> + n ; n --, i = (i + 1) %
> + SQUASHFS_CACHED_BLKS)

Messy. Do n-- (no space), handle i outside the for control structures.
Perhaps break this piece out into a separate function, the indenting
is making things cramped.

> + if (n == 0) {
> + wait_queue_t wait;
> +
> + init_waitqueue_entry(&wait, current);
> + add_wait_queue(&msBlk->waitq, &wait);
> + set_current_state(TASK_UNINTERRUPTIBLE);
> + up(&msBlk->block_cache_mutex);
> + schedule();
> + set_current_state(TASK_RUNNING);
> + remove_wait_queue(&msBlk->waitq, &wait);

I suspect you'll find there's a much cleaner way to do whatever it is you're
trying to do here.

> + if (!(msBlk->block_cache[i].data =
> + (unsigned char *)
> + kmalloc(SQUASHFS_METADATA_SIZE,
> + GFP_KERNEL))) {

Another class of unnecessary cast.

> + msBlk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
> + int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);

Feel free to make these defines a little less unwieldy. So long as
they're internal to Squashfs.

> + for (;;) {

while (1)

> + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
> + msBlk->fragment[i].block != start_block; i++);

';' on its own line. Better is

for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++)
if (msBlk->fragment[i].block != start_block)
break;

> + WARNING("Mounting a different endian SQUASHFS "
> + "filesystem on %s\n", bdevname(s->s_bdev, b));

Can't we just use printk for this stuff?

> + bytes = (inode->i_size - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
> + inode->i_size - length;

min()

> + if (page->index >= ((inode->i_size + PAGE_CACHE_SIZE - 1) >>
> + PAGE_CACHE_SHIFT)) {
> + goto skip_read;
> + }

Skip braces for single statements.

> + block = (msBlk->read_blocklist)(inode, page->index, 1,
> + block_list, NULL, &bsize);

Unnecessary parens around the function pointer.

...

This file could use quite a bit of work in terms of reducing nesting
depth and the like.

--
Mathematics is the supreme nostalgia of our time.

2005-03-15 00:55:41

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

Phillip Lougher <[email protected]> wrote:
>
> Please apply the following two patches which adds SquashFS to the
> kernel.

> +
> +#include <linux/types.h>
> +#include <linux/squashfs_fs.h>
> +#include <linux/module.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/fs.h>
> +#include <linux/smp_lock.h>
> +#include <linux/slab.h>
> +#include <linux/squashfs_fs_sb.h>
> +#include <linux/squashfs_fs_i.h>
> +#include <linux/buffer_head.h>
> +#include <linux/vfs.h>
> +#include <linux/init.h>
> +#include <linux/dcache.h>
> +#include <asm/uaccess.h>
> +#include <linux/wait.h>
> +#include <asm/semaphore.h>
> +#include <linux/zlib.h>
> +#include <linux/blkdev.h>
> +#include <linux/vmalloc.h>
> +#include "squashfs.h"
> +

We normally put aam includes after linux includes:

#include <linux/a.h>
#include <linux/b.h>

#include <asm/a.h>
#Include <asm/b.h>

> +
> +DECLARE_MUTEX(read_data_mutex);
> +

Does this need to have global scope? If so, it needs a less generic name.
`squashfs_read_data_mutex' would suit.

> +static struct file_system_type squashfs_fs_type = {
> + .owner = THIS_MODULE,
> + .name = "squashfs",
> + .get_sb = squashfs_get_sb,
> + .kill_sb = kill_block_super,
> + .fs_flags = FS_REQUIRES_DEV
> + };
> +

The final brace should go in column 1.

> +
> +static struct buffer_head *get_block_length(struct super_block *s,
> + int *cur_index, int *offset, int *c_byte)
> +{
> + squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info;

s_fs_info has type void*. Hence there is no need to typecast when
assigning pointers to or from it. In fact it is a little harmful to do so.

Please search both your patches for all occurrences of s_fs_info and remove
the typecasts. There are many.


> + 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));

All this typecasting looks nasty. Is there a nicer way? Perhaps using a
temporary variable?

Is this code endian-safe?

> + if (msBlk->swap)
> + ((unsigned char *) &temp)[0] = *((unsigned char *)
> + bh->b_data);
> + else
> + ((unsigned char *) &temp)[1] = *((unsigned char *)
> + bh->b_data);
> + *c_byte = temp;
> + *offset = 1;
> + } else {
> + if (msBlk->swap) {
> + ((unsigned char *) &temp)[1] = *((unsigned char *)
> + (bh->b_data + *offset));
> + ((unsigned char *) &temp)[0] = *((unsigned char *)
> + (bh->b_data + *offset + 1));
> + } else {
> + ((unsigned char *) &temp)[0] = *((unsigned char *)
> + (bh->b_data + *offset));
> + ((unsigned char *) &temp)[1] = *((unsigned char *)
> + (bh->b_data + *offset + 1));
> + }

Ditto.

> +
> + 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;

Multiple return statements per function are a maintainability problem,
especially if some of them are deep inside that function's logic. The old
`goto out' is preferred.

(Imagine what would happen if you later wanted to change this function to
kmalloc a bit of temp storage and you don't want it to leak).

> + }
> + (*offset) ++;

whitespace.

> +unsigned int squashfs_read_data(struct super_block *s, char *buffer,
> + unsigned int index, unsigned int length,
> + unsigned int *next_index)
> +{
> + squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info;
> + struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
> + msBlk->devblksize_log2) + 2];

Dynamically sized local storage. Deliberate? What is the upper bound on
its size?

> +block_release:
> + while (--b >= 0) brelse(bh[b]);

while (--b >= 0)
brelse(bh[b]);

please.

> +
> + if (n == 0) {
> + wait_queue_t wait;
> +
> + init_waitqueue_entry(&wait, current);
> + add_wait_queue(&msBlk->waitq, &wait);
> + set_current_state(TASK_UNINTERRUPTIBLE);
> + up(&msBlk->block_cache_mutex);
> + schedule();
> + set_current_state(TASK_RUNNING);
> + remove_wait_queue(&msBlk->waitq, &wait);
> + continue;
> + }

- Use DECLARE_WAITQUEUE (or DEFINE_WAIT)

- schedule() always returns in state TASK_RUNNING.

> + 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))) {

kmalloc() returns void*, hence no cast is needed. Please search both
patches for all instances of kmalloc(), remove the typecasts.

> + if (n == 0) {
> + wait_queue_t wait;
> +
> + init_waitqueue_entry(&wait, current);
> + add_wait_queue(&msBlk->fragment_wait_queue,
> + &wait);
> + set_current_state(TASK_UNINTERRUPTIBLE);
> + up(&msBlk->fragment_mutex);
> + schedule();
> + set_current_state(TASK_RUNNING);
> + remove_wait_queue(&msBlk->fragment_wait_queue,
> + &wait);
> + continue;
> + }

See above.

> +
> +static struct inode *squashfs_iget(struct super_block *s, squashfs_inode inode)
> +{
> + struct inode *i;
> + 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 ino = SQUASHFS_MK_VFS_INODE(block
> + - sBlk->inode_table_start, offset);
> + unsigned int next_block, next_offset;
> + squashfs_base_inode_header inodeb;

How much stack space is being used here? Perhaps you should run
scripts/checkstack.pl across the whole thing.

> + TRACE("Entered squashfs_iget\n");
> +
> + if (msBlk->swap) {
> + squashfs_base_inode_header sinodeb;
> +

More stack

> +
> + switch(inodeb.inode_type) {
> + case SQUASHFS_FILE_TYPE: {
> + squashfs_reg_inode_header inodep;
> + unsigned int frag_blk, frag_size;

And more

> + case SQUASHFS_DIR_TYPE: {
> + squashfs_dir_inode_header inodep;

And more!

> + if (msBlk->swap) {
> + squashfs_dir_inode_header sinodep;

> + }
> + case SQUASHFS_LDIR_TYPE: {
> + squashfs_ldir_inode_header inodep;

> + }
> + case SQUASHFS_SYMLINK_TYPE: {
> + squashfs_symlink_inode_header inodep;

> + case SQUASHFS_BLKDEV_TYPE:
> + case SQUASHFS_CHRDEV_TYPE: {
> + squashfs_dev_inode_header inodep;

More.

> +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;
> + 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);
> + goto skip_read;
> + }
> + }
> +
> + 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;

One should normally use i_size_read(), but I guess thi is OK on symlinks.

> +skip_read:
> + memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
> + kunmap(page);
> + flush_dcache_page(page);
> + SetPageUptodate(page);
> + unlock_page(page);
> +
> + return 0;
> +}

There's no need to run flush_dcache_page() against a symlink - they cannot
be mmapped.

See if you can use kmap_atomic() here - kmap() is slow, theoretically
deadlocky and is deprecated.

> +static unsigned int read_blocklist(struct inode *inode, int index,
> + int readahead_blks, char *block_list,
> + unsigned short **block_p, unsigned int *bsize)
> +{
> ...
> +
> + if (msBlk->swap) {
> + unsigned char sblock_list[SIZE];

How large is `SIZE'?

That seems to be a risky choice of identifier. I guess it's so generic
that nobody else used it, so you're safe ;)

> +
> +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_fragment_cache *fragment = NULL;
> + char *data_ptr = msBlk->read_page;
> +
> + int mask = (1 << (sBlk->block_log - PAGE_CACHE_SHIFT)) - 1;
> + int start_index = page->index & ~mask;
> + int end_index = start_index | mask;
> +
> + TRACE("Entered squashfs_readpage, page index %x, start block %x\n",
> + (unsigned int) page->index,
> + SQUASHFS_I(inode)->start_block);
> +
> + if (page->index >= ((inode->i_size + PAGE_CACHE_SIZE - 1) >>
> + PAGE_CACHE_SHIFT)) {
> + goto skip_read;
> + }

This should use i_size_read(). Please review the patches for all instances
of open-coded i_size reads and writes.

> +
> + 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);
> + page_cache_release(push_page);

kmap_atomic() can be used here.

> +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);

Stack space?

> +
> +
> +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)
> +{
> + squashfs_sb_info *msBlk = (squashfs_sb_info *)s->s_fs_info;
> + 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];
> +
> + TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
> +
> + strncpy(str, name, size);
> + str[size] = '\0';

Are you sure that this strncpy() is always safe?

> + dirs_read ++;

We don't put a space before the `++'. Please review both patches for this.

> +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);

kmem_cache_alloc() returns void*.

> +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
> +{
> + struct squashfs_inode_info *ei = (struct squashfs_inode_info *) foo;

Unneeded typecast.

> +static int init_inodecache(void)

This can be __init.

2005-03-15 01:47:33

by Nick Piggin

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

Matt Mackall wrote:

>>+ for (;;) {
>>
>
>while (1)
>
>

I always thought for (;;) was preferred. Or at least acceptable?


2005-03-15 02:34:20

by Matt Mackall

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

On Tue, Mar 15, 2005 at 12:47:23PM +1100, Nick Piggin wrote:
> Matt Mackall wrote:
>
> >>+ for (;;) {
> >
> >while (1)
>
> I always thought for (;;) was preferred. Or at least acceptable?

The for (;;) form has always struck me as needlessly clever and I've
known it to puzzle coworkers. I try to make my for loops fall into the
mold of simple initialize/test/advance. But no, I'm not aware of any
LKML concensus opinion on this particular point.

The assignment-in-if problem is a bit more serious as it exacerbates
the jammed-up-against-the-right-margin formatting issues.

--
Mathematics is the supreme nostalgia of our time.

2005-03-15 08:50:20

by Paul Jackson

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

In the overall kernel (Linus's bk tree) I count:

733 lines matching 'for *( *; *; *)'
718 lines matching 'while *( *1 *)'

In the kernel/*.c files, I count 15 of the 'for(;;)' style and 1 of the
'while(1)' style.

Certainly the 'for(;;)' style is acceptable, and even slightly to
substantially dominant, depending on which piece of code you're in.

--
I won't rest till it's the best ...
Programmer, Linux Scalability
Paul Jackson <[email protected]> 1.650.933.1373, 1.925.600.0401

2005-03-15 17:05:23

by Phillip Lougher

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

Paul Jackson wrote:
> In the overall kernel (Linus's bk tree) I count:
>
> 733 lines matching 'for *( *; *; *)'
> 718 lines matching 'while *( *1 *)'
>
> In the kernel/*.c files, I count 15 of the 'for(;;)' style and 1 of the
> 'while(1)' style.
>
> Certainly the 'for(;;)' style is acceptable, and even slightly to
> substantially dominant, depending on which piece of code you're in.
>

I prefer the 'while' style, and only used 'for' because that's what I
thought the kernel used.

If no-one objects I'll change it back to while...

Shouldn't issues like this be in the coding style document?

Phillip

2005-03-15 17:31:00

by Matt Mackall

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

On Tue, Mar 15, 2005 at 03:50:26PM +0000, Phillip Lougher wrote:
> Paul Jackson wrote:
> >In the overall kernel (Linus's bk tree) I count:
> >
> > 733 lines matching 'for *( *; *; *)'
> > 718 lines matching 'while *( *1 *)'
> >
> >In the kernel/*.c files, I count 15 of the 'for(;;)' style and 1 of the
> >'while(1)' style.
> >
> >Certainly the 'for(;;)' style is acceptable, and even slightly to
> >substantially dominant, depending on which piece of code you're in.
> >
>
> I prefer the 'while' style, and only used 'for' because that's what I
> thought the kernel used.
>
> If no-one objects I'll change it back to while...
>
> Shouldn't issues like this be in the coding style document?

This particular point is rather trivial. Do whatever suits you.

--
Mathematics is the supreme nostalgia of our time.

2005-03-15 17:38:04

by Phillip Lougher

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

Matt Mackall wrote:
> On Tue, Mar 15, 2005 at 03:50:26PM +0000, Phillip Lougher wrote:
>
>>Paul Jackson wrote:
>>
>>>In the overall kernel (Linus's bk tree) I count:
>>>
>>> 733 lines matching 'for *( *; *; *)'
>>> 718 lines matching 'while *( *1 *)'
>>>
>>>In the kernel/*.c files, I count 15 of the 'for(;;)' style and 1 of the
>>>'while(1)' style.
>>>
>>>Certainly the 'for(;;)' style is acceptable, and even slightly to
>>>substantially dominant, depending on which piece of code you're in.
>>>
>>
>>I prefer the 'while' style, and only used 'for' because that's what I
>>thought the kernel used.
>>
>>If no-one objects I'll change it back to while...
>>
>>Shouldn't issues like this be in the coding style document?
>
>
> This particular point is rather trivial. Do whatever suits you.
>

It's a shame the 'rather trivial' issue got picked up in the first place
then :-) I didn't raise the issue.

2005-03-15 19:17:56

by Paul Jackson

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

> >>Shouldn't issues like this be in the coding style document?

There is not a concensus (nor a King Penguin dictate) between the
"while(1)" and "for(;;)" style to document. If this were a
frequently asked question, I suppose someone would eventually
note in a coding style doc that either style is acceptable.


> It's a shame the 'rather trivial' issue got picked up in the first place

Not a shame at all. Such coding style issues are well known to be a
proper subject for discussion here.

--
I won't rest till it's the best ...
Programmer, Linux Scalability
Paul Jackson <[email protected]> 1.650.933.1373, 1.925.600.0401

2005-03-16 01:50:10

by Junio C Hamano

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

>>>>> "PJ" == Paul Jackson <[email protected]> writes:

PJ> There is not a concensus (nor a King Penguin dictate) between the
PJ> "while(1)" and "for(;;)" style to document.

FWIW, linux-0.01 has four uses of "while (1)" and two uses of
"for (;;)" ;-).

./fs/inode.c: while (1) {
./fs/namei.c: while (1) {
./fs/namei.c: while (1) {
./kernel/sched.c: while (1) {

./init/main.c: for(;;) pause();
./kernel/panic.c: for(;;);

What is interesting here is that the King Penguin used these two
constructs with consistency. The "while (1)" form was used with
normal exit routes with "if (...) break" inside; while the
"for(;;)" form was used only in unusual "the thread of control
should get stuck here forever" cases.

So, Phillip's decision to go back to his original while(1) style
seems to be in line with the style used in the original Linux
kernel ;-).

2005-03-16 07:15:42

by Paul Jackson

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

> the King Penguin used these two constructs with consistency:

Nice distinction - thanks.

--
I won't rest till it's the best ...
Programmer, Linux Scalability
Paul Jackson <[email protected]> 1.650.933.1373, 1.925.600.0401

2005-03-17 20:09:27

by Jerome Lacoste

[permalink] [raw]
Subject: Re: [PATCH][1/2] SquashFS

On Tue, 15 Mar 2005 17:50:02 -0800, Junio C Hamano <[email protected]> wrote:
> >>>>> "PJ" == Paul Jackson <[email protected]> writes:
>
> PJ> There is not a concensus (nor a King Penguin dictate) between the
> PJ> "while(1)" and "for(;;)" style to document.
>
> FWIW, linux-0.01 has four uses of "while (1)" and two uses of
> "for (;;)" ;-).
>
> ./fs/inode.c: while (1) {
> ./fs/namei.c: while (1) {
> ./fs/namei.c: while (1) {
> ./kernel/sched.c: while (1) {
>
> ./init/main.c: for(;;) pause();
> ./kernel/panic.c: for(;;);
>
> What is interesting here is that the King Penguin used these two
> constructs with consistency. The "while (1)" form was used with
> normal exit routes with "if (...) break" inside; while the
> "for(;;)" form was used only in unusual "the thread of control
> should get stuck here forever" cases.
>
> So, Phillip's decision to go back to his original while(1) style
> seems to be in line with the style used in the original Linux
> kernel ;-).

After the Pinguin janitors, now comes the Pinguin archeologists.

This starts to be lemmingesque :)

J

2005-03-19 03:25:37

by Phillip Lougher

[permalink] [raw]
Subject: Kmap_atomic vs Kmap

Andrew Morton wrote in relation to my initial SquashFS patch:
> Phillip Lougher <[email protected]> wrote:
>
>>+skip_read:
>>+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
>>+ kunmap(page);
>>+ flush_dcache_page(page);
>>+ SetPageUptodate(page);
>>+ unlock_page(page);
>>+
>>+ return 0;
>>+}

> See if you can use kmap_atomic() here - kmap() is slow, theoretically
> deadlocky and is deprecated.
>

From some browsing of the kernel source and a useful article on lwn.net
I think I can replace all the readpage_xxx (symlink, readpage &
readpage4k) kmap/kunmaps with kmap_atomic(page, KM_USER0)/kunmap(kaddr,
KM_USER0)...

The lwn.net article mentions that k[un]map_atomic is the new alternative
to kmap (as Andrew Morton mentioned).

I've noticed in asm-ppc/highmem.h the following comment

/*
* The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
* gives a more generic (and caching) interface. But kmap_atomic can
* be used in IRQ contexts, so in some (very limited) cases we need
* it.
*/

Given what Andrew and the lwn.net article says, this comment is rather
confusing. Is it wrong?

Regards

Phillip Lougher

2005-03-19 03:41:58

by Andrew Morton

[permalink] [raw]
Subject: Re: Kmap_atomic vs Kmap

Phillip Lougher <[email protected]> wrote:
>
> Andrew Morton wrote in relation to my initial SquashFS patch:
> > Phillip Lougher <[email protected]> wrote:
> >
> >>+skip_read:
> >>+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
> >>+ kunmap(page);
> >>+ flush_dcache_page(page);
> >>+ SetPageUptodate(page);
> >>+ unlock_page(page);
> >>+
> >>+ return 0;
> >>+}
>
> > See if you can use kmap_atomic() here - kmap() is slow, theoretically
> > deadlocky and is deprecated.
> >
>
> From some browsing of the kernel source and a useful article on lwn.net
> I think I can replace all the readpage_xxx (symlink, readpage &
> readpage4k) kmap/kunmaps with kmap_atomic(page, KM_USER0)/kunmap(kaddr,
> KM_USER0)...

Yes.

Generally the only reason the kernel needs to modify pagecache pages is to
do a quick memset or to copy a string in there or something like that. So
a quick kmap_atomic/memory copy/kunmap_atomic() cycle is a fairly common
pattern.

The one exception is the directory-in-pagecache code where we tend to do a
lot of stuff while holding the kmap. Not that there's any particular
reason why we needed to do it that way.

> The lwn.net article mentions that k[un]map_atomic is the new alternative
> to kmap (as Andrew Morton mentioned).
>
> I've noticed in asm-ppc/highmem.h the following comment
>
> /*
> * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
> * gives a more generic (and caching) interface. But kmap_atomic can
> * be used in IRQ contexts, so in some (very limited) cases we need
> * it.
> */
>
> Given what Andrew and the lwn.net article says, this comment is rather
> confusing. Is it wrong?

I'd say so, yes. For a start, kmap_high() takes a global lock.

2005-03-21 00:50:06

by Phillip Lougher

[permalink] [raw]
Subject: Function stack size usage (was [PATCH][1/2] SquashFS)

Andrew Morton wrote:
> Phillip Lougher <[email protected]> wrote:
>>
>>+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode inode)
>>+{
>>+ struct inode *i;
>>+ 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 ino = SQUASHFS_MK_VFS_INODE(block
>>+ - sBlk->inode_table_start, offset);
>>+ unsigned int next_block, next_offset;
>>+ squashfs_base_inode_header inodeb;
>
>
> How much stack space is being used here? Perhaps you should run
> scripts/checkstack.pl across the whole thing.
>

A lot of the functions use a fair amount of stack (I never thought it
was excessive)... This is the result of running checkstack.pl against
the code on Intel.

0x00003a3c get_dir_index_using_name: 596
0x00000d80 squashfs_iget: 488
0x000044d8 squashfs_lookup: 380
0x00003d00 squashfs_readdir: 372
0x000020fe squashfs_fill_super: 316
0x000031b8 squashfs_readpage: 308
0x00002f5c read_blocklist: 296
0x00003634 squashfs_readpage4K: 284

A couple of these functions show a fair amount of stack use. What is
the maximum acceptable usage, i.e. do any of the above functions need
work to reduce their stack usage?

Thanks

Phillip

2005-03-21 00:59:27

by Andrew Morton

[permalink] [raw]
Subject: Re: Function stack size usage (was [PATCH][1/2] SquashFS)

Phillip Lougher <[email protected]> wrote:
>
> Andrew Morton wrote:
> > Phillip Lougher <[email protected]> wrote:
> >>
> >>+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode inode)
> >>+{
> >>+ struct inode *i;
> >>+ 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 ino = SQUASHFS_MK_VFS_INODE(block
> >>+ - sBlk->inode_table_start, offset);
> >>+ unsigned int next_block, next_offset;
> >>+ squashfs_base_inode_header inodeb;
> >
> >
> > How much stack space is being used here? Perhaps you should run
> > scripts/checkstack.pl across the whole thing.
> >
>
> A lot of the functions use a fair amount of stack (I never thought it
> was excessive)... This is the result of running checkstack.pl against
> the code on Intel.
>
> 0x00003a3c get_dir_index_using_name: 596
> 0x00000d80 squashfs_iget: 488
> 0x000044d8 squashfs_lookup: 380
> 0x00003d00 squashfs_readdir: 372
> 0x000020fe squashfs_fill_super: 316
> 0x000031b8 squashfs_readpage: 308
> 0x00002f5c read_blocklist: 296
> 0x00003634 squashfs_readpage4K: 284
>
> A couple of these functions show a fair amount of stack use. What is
> the maximum acceptable usage,

There's no hard-and-fast rule. The conditions running up to a stack
overrun are necessarily complex, and rare. But you can see that for a
twenty or thirty function deep call stack, 500 bytes is a big bite out of
4k.

> i.e. do any of the above functions need
> work to reduce their stack usage?

I'd say so, yes. If at all possible.