initial commit

This commit is contained in:
longpanda 2020-04-05 00:07:50 +08:00
parent 2090c6fa97
commit 05a1b863a6
487 changed files with 114253 additions and 0 deletions

View file

@ -0,0 +1,3 @@
Squashfs is now in mainline at www.kernel.org.
These files are obsolete and not updated.

View file

@ -0,0 +1,11 @@
#
# Makefile for the linux squashfs routines.
#
O_TARGET := squashfs.o
obj-y := inode.o squashfs2_0.o
obj-m := $(O_TARGET)
include $(TOPDIR)/Rules.make

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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.h
*/
#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
#endif
#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...) do { \
if (!silent) \
printk(KERN_ERR "SQUASHFS error: "s, ## args);\
} while(0)
#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args)
#define SQUASHFS_I(INO) (&INO->u.squashfs_i)
#define i_size_read(INO) (INO->i_size)
#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
#define SQSH_EXTERN
extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
long long index, unsigned int length,
long long *next_index);
extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
long long block, unsigned int offset,
int length, long long *next_block,
unsigned int *next_offset);
extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
squashfs_fragment_cache *fragment);
extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
*s, long long start_block,
int length);
extern struct address_space_operations squashfs_symlink_aops;
extern struct address_space_operations squashfs_aops;
extern struct address_space_operations squashfs_aops_4K;
extern struct file_operations squashfs_dir_ops;
extern struct inode_operations squashfs_dir_inode_ops;
#else
#define SQSH_EXTERN static
#endif
#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
#else
static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
{
return 0;
}
#endif
#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
#else
static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
{
return 0;
}
#endif

View file

@ -0,0 +1,751 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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.
*
* squashfs2_0.c
*/
#include <linux/types.h>
#include <linux/squashfs_fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <linux/locks.h>
#include <linux/init.h>
#include <linux/dcache.h>
#include <linux/wait.h>
#include <linux/zlib.h>
#include <linux/blkdev.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include "squashfs.h"
static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry);
static struct file_operations squashfs_dir_ops_2 = {
.read = generic_read_dir,
.readdir = squashfs_readdir_2
};
static struct inode_operations squashfs_dir_inode_ops_2 = {
.lookup = squashfs_lookup_2
};
static unsigned char squashfs_filetype_table[] = {
DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
};
static int read_fragment_index_table_2(struct super_block *s)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
(sblk->fragments), GFP_KERNEL))) {
ERROR("Failed to allocate uid/gid table\n");
return 0;
}
if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
!squashfs_read_data(s, (char *)
msblk->fragment_index_2,
sblk->fragment_table_start,
SQUASHFS_FRAGMENT_INDEX_BYTES_2
(sblk->fragments) |
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
ERROR("unable to read fragment index table\n");
return 0;
}
if (msblk->swap) {
int i;
unsigned int fragment;
for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
i++) {
SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
&msblk->fragment_index_2[i], 1);
msblk->fragment_index_2[i] = fragment;
}
}
return 1;
}
static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
long long *fragment_start_block,
unsigned int *fragment_size)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
long long start_block =
msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
struct squashfs_fragment_entry_2 fragment_entry;
if (msblk->swap) {
struct squashfs_fragment_entry_2 sfragment_entry;
if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
start_block, offset,
sizeof(sfragment_entry), &start_block,
&offset))
goto out;
SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
} else
if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
start_block, offset,
sizeof(fragment_entry), &start_block,
&offset))
goto out;
*fragment_start_block = fragment_entry.start_block;
*fragment_size = fragment_entry.size;
return 1;
out:
return 0;
}
static struct inode *squashfs_new_inode(struct super_block *s,
struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
struct inode *i = new_inode(s);
if (i) {
i->i_ino = ino;
i->i_mtime = sblk->mkfs_time;
i->i_atime = sblk->mkfs_time;
i->i_ctime = 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_2(struct super_block *s, squashfs_inode_t inode)
{
struct inode *i;
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
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);
long long next_block;
unsigned int next_offset;
union squashfs_inode_header_2 id, sid;
struct squashfs_base_inode_header_2 *inodeb = &id.base,
*sinodeb = &sid.base;
TRACE("Entered squashfs_iget\n");
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
offset, sizeof(*sinodeb), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_BASE_INODE_HEADER_2(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: {
struct squashfs_reg_inode_header_2 *inodep = &id.reg;
struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
long long frag_blk;
unsigned int frag_size;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
} else
if (!squashfs_get_cached_block(s, (char *)
inodep, block, offset,
sizeof(*inodep), &next_block,
&next_offset))
goto failed_read;
frag_blk = SQUASHFS_INVALID_BLK;
if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
!get_fragment_location_2(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 = inodep->mtime;
i->i_atime = inodep->mtime;
i->i_ctime = inodep->mtime;
i->i_blocks = ((i->i_size - 1) >> 9) + 1;
i->i_blksize = PAGE_CACHE_SIZE;
SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
SQUASHFS_I(i)->start_block = inodep->start_block;
SQUASHFS_I(i)->u.s1.block_list_start = next_block;
SQUASHFS_I(i)->offset = next_offset;
if (sblk->block_size > 4096)
i->i_data.a_ops = &squashfs_aops;
else
i->i_data.a_ops = &squashfs_aops_4K;
TRACE("File inode %x:%x, start_block %x, "
"block_list_start %llx, offset %x\n",
SQUASHFS_INODE_BLK(inode), offset,
inodep->start_block, next_block,
next_offset);
break;
}
case SQUASHFS_DIR_TYPE: {
struct squashfs_dir_inode_header_2 *inodep = &id.dir;
struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_DIR_INODE_HEADER_2(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_2;
i->i_fop = &squashfs_dir_ops_2;
i->i_mode |= S_IFDIR;
i->i_mtime = inodep->mtime;
i->i_atime = inodep->mtime;
i->i_ctime = 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;
SQUASHFS_I(i)->u.s2.parent_inode = 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: {
struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_LDIR_INODE_HEADER_2(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_2;
i->i_fop = &squashfs_dir_ops_2;
i->i_mode |= S_IFDIR;
i->i_mtime = inodep->mtime;
i->i_atime = inodep->mtime;
i->i_ctime = 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;
SQUASHFS_I(i)->u.s2.parent_inode = 0;
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: {
struct squashfs_symlink_inode_header_2 *inodep =
&id.symlink;
struct squashfs_symlink_inode_header_2 *sinodep =
&sid.symlink;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(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 %llx, "
"offset %x\n",
SQUASHFS_INODE_BLK(inode), offset,
next_block, next_offset);
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE: {
struct squashfs_dev_inode_header_2 *inodep = &id.dev;
struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
if (msblk->swap) {
if (!squashfs_get_cached_block(s, (char *)
sinodep, block, offset,
sizeof(*sinodep), &next_block,
&next_offset))
goto failed_read;
SQUASHFS_SWAP_DEV_INODE_HEADER_2(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, 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 get_dir_index_using_offset(struct super_block *s, long long
*next_block, unsigned int *next_offset,
long long index_start,
unsigned int index_offset, int i_count,
long long f_pos)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
int i, length = 0;
struct squashfs_dir_index_2 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)
goto finish;
for (i = 0; i < i_count; i++) {
if (msblk->swap) {
struct squashfs_dir_index_2 sindex;
squashfs_get_cached_block(s, (char *) &sindex,
index_start, index_offset,
sizeof(sindex), &index_start,
&index_offset);
SQUASHFS_SWAP_DIR_INDEX_2(&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;
finish:
return length;
}
static int get_dir_index_using_name(struct super_block *s, long long
*next_block, unsigned int *next_offset,
long long index_start,
unsigned int index_offset, int i_count,
const char *name, int size)
{
struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
int i, length = 0;
char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1];
struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer;
char str[SQUASHFS_NAME_LEN + 1];
TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
strncpy(str, name, size);
str[size] = '\0';
for (i = 0; i < i_count; i++) {
if (msblk->swap) {
struct squashfs_dir_index_2 sindex;
squashfs_get_cached_block(s, (char *) &sindex,
index_start, index_offset,
sizeof(sindex), &index_start,
&index_offset);
SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
} else
squashfs_get_cached_block(s, (char *) index,
index_start, index_offset,
sizeof(struct squashfs_dir_index_2),
&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_2(struct file *file, void *dirent, filldir_t filldir)
{
struct inode *i = file->f_dentry->d_inode;
struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
long long next_block = SQUASHFS_I(i)->start_block +
sblk->directory_table_start;
int next_offset = SQUASHFS_I(i)->offset, length = 0,
dir_count;
struct squashfs_dir_header_2 dirh;
char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1];
struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
TRACE("Entered squashfs_readdir_2 [%llx:%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_size_read(i)) {
/* read directory header */
if (msblk->swap) {
struct squashfs_dir_header_2 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_2(&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) {
struct squashfs_dir_entry_2 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_2(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");
goto finish;
}
file->f_pos = length;
}
}
finish:
return 0;
failed_read:
ERROR("Unable to read directory block [%llx:%x]\n", next_block,
next_offset);
return 0;
}
static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry)
{
const unsigned char *name = dentry->d_name.name;
int len = dentry->d_name.len;
struct inode *inode = NULL;
struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb;
struct squashfs_super_block *sblk = &msblk->sblk;
long long next_block = SQUASHFS_I(i)->start_block +
sblk->directory_table_start;
int next_offset = SQUASHFS_I(i)->offset, length = 0,
dir_count;
struct squashfs_dir_header_2 dirh;
char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN];
struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
if (len > SQUASHFS_NAME_LEN)
goto exit_loop;
length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
SQUASHFS_I(i)->u.s2.directory_index_start,
SQUASHFS_I(i)->u.s2.directory_index_offset,
SQUASHFS_I(i)->u.s2.directory_index_count, name,
len);
while (length < i_size_read(i)) {
/* read directory header */
if (msblk->swap) {
struct squashfs_dir_header_2 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_2(&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) {
struct squashfs_dir_entry_2 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_2(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_t ino =
SQUASHFS_MKINODE(dirh.start_block,
dire->offset);
TRACE("calling squashfs_iget for directory "
"entry %s, inode %x:%x, %d\n", name,
dirh.start_block, dire->offset, ino);
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 [%llx:%x]\n", next_block,
next_offset);
goto exit_loop;
}
int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
{
struct squashfs_super_block *sblk = &msblk->sblk;
msblk->iget = squashfs_iget_2;
msblk->read_fragment_index_table = read_fragment_index_table_2;
sblk->bytes_used = sblk->bytes_used_2;
sblk->uid_start = sblk->uid_start_2;
sblk->guid_start = sblk->guid_start_2;
sblk->inode_table_start = sblk->inode_table_start_2;
sblk->directory_table_start = sblk->directory_table_start_2;
sblk->fragment_table_start = sblk->fragment_table_start_2;
return 1;
}

View file

@ -0,0 +1,915 @@
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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_fs.h
*/
#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
#endif
#ifdef CONFIG_SQUASHFS_VMALLOC
#define SQUASHFS_ALLOC(a) vmalloc(a)
#define SQUASHFS_FREE(a) vfree(a)
#else
#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL)
#define SQUASHFS_FREE(a) kfree(a)
#endif
#ifdef CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#else
#define SQUASHFS_CACHED_FRAGMENTS 3
#endif
#define SQUASHFS_MAJOR 3
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#define SQUASHFS_MAGIC_SWAP 0x68737173
#define SQUASHFS_START 0
/* size of metadata (inode and directory) blocks */
#define SQUASHFS_METADATA_SIZE 8192
#define SQUASHFS_METADATA_LOG 13
/* default size of data blocks */
#define SQUASHFS_FILE_SIZE 65536
#define SQUASHFS_FILE_LOG 16
#define SQUASHFS_FILE_MAX_SIZE 65536
/* Max number of uids and gids */
#define SQUASHFS_UIDS 256
#define SQUASHFS_GUIDS 255
/* Max length of filename (not 255) */
#define SQUASHFS_NAME_LEN 256
#define SQUASHFS_INVALID ((long long) 0xffffffffffff)
#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff)
#define SQUASHFS_INVALID_BLK ((long long) -1)
#define SQUASHFS_USED_BLK ((long long) -2)
/* Filesystem flags */
#define SQUASHFS_NOI 0
#define SQUASHFS_NOD 1
#define SQUASHFS_CHECK 2
#define SQUASHFS_NOF 3
#define SQUASHFS_NO_FRAG 4
#define SQUASHFS_ALWAYS_FRAG 5
#define SQUASHFS_DUPLICATE 6
#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_CHECK)
#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
duplicate_checking) (noi | (nod << 1) | (check_data << 2) \
| (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
(duplicate_checking << 6))
/* Max number of types and file types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_FILE_TYPE 2
#define SQUASHFS_SYMLINK_TYPE 3
#define SQUASHFS_BLKDEV_TYPE 4
#define SQUASHFS_CHRDEV_TYPE 5
#define SQUASHFS_FIFO_TYPE 6
#define SQUASHFS_SOCKET_TYPE 7
#define SQUASHFS_LDIR_TYPE 8
#define SQUASHFS_LREG_TYPE 9
/* 1.0 filesystem type definitions */
#define SQUASHFS_TYPES 5
#define SQUASHFS_IPC_TYPE 0
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16))
#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\
<< 16) + (B)))
/* Compute 32 bit VFS inode number from squashfs inode number */
#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \
((b) >> 2) + 1))
/* XXX */
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(a) ((a) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) (A * sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(long long))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 31
#define SQUASHFS_META_NUMBER 8
#define SQUASHFS_SLOTS 4
struct meta_entry {
long long data_block;
unsigned int index_block;
unsigned short offset;
unsigned short pad;
};
struct meta_index {
unsigned int inode_number;
unsigned int offset;
unsigned short entries;
unsigned short skip;
unsigned short locked;
unsigned short pad;
struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
};
/*
* definitions for structures on disk
*/
typedef long long squashfs_block_t;
typedef long long squashfs_inode_t;
struct squashfs_super_block {
unsigned int s_magic;
unsigned int inodes;
unsigned int bytes_used_2;
unsigned int uid_start_2;
unsigned int guid_start_2;
unsigned int inode_table_start_2;
unsigned int directory_table_start_2;
unsigned int s_major:16;
unsigned int s_minor:16;
unsigned int block_size_1:16;
unsigned int block_log:16;
unsigned int flags:8;
unsigned int no_uids:8;
unsigned int no_guids:8;
unsigned int mkfs_time /* time of filesystem creation */;
squashfs_inode_t root_inode;
unsigned int block_size;
unsigned int fragments;
unsigned int fragment_table_start_2;
long long bytes_used;
long long uid_start;
long long guid_start;
long long inode_table_start;
long long directory_table_start;
long long fragment_table_start;
long long unused;
} __attribute__ ((packed));
struct squashfs_dir_index {
unsigned int index;
unsigned int start_block;
unsigned char size;
unsigned char name[0];
} __attribute__ ((packed));
#define SQUASHFS_BASE_INODE_HEADER \
unsigned int inode_type:4; \
unsigned int mode:12; \
unsigned int uid:8; \
unsigned int guid:8; \
unsigned int mtime; \
unsigned int inode_number;
struct squashfs_base_inode_header {
SQUASHFS_BASE_INODE_HEADER;
} __attribute__ ((packed));
struct squashfs_ipc_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
} __attribute__ ((packed));
struct squashfs_dev_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header {
SQUASHFS_BASE_INODE_HEADER;
squashfs_block_t start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_lreg_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
squashfs_block_t start_block;
unsigned int fragment;
unsigned int offset;
long long file_size;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
unsigned int file_size:19;
unsigned int offset:13;
unsigned int start_block;
unsigned int parent_inode;
} __attribute__ ((packed));
struct squashfs_ldir_inode_header {
SQUASHFS_BASE_INODE_HEADER;
unsigned int nlink;
unsigned int file_size:27;
unsigned int offset:13;
unsigned int start_block;
unsigned int i_count:16;
unsigned int parent_inode;
struct squashfs_dir_index index[0];
} __attribute__ ((packed));
union squashfs_inode_header {
struct squashfs_base_inode_header base;
struct squashfs_dev_inode_header dev;
struct squashfs_symlink_inode_header symlink;
struct squashfs_reg_inode_header reg;
struct squashfs_lreg_inode_header lreg;
struct squashfs_dir_inode_header dir;
struct squashfs_ldir_inode_header ldir;
struct squashfs_ipc_inode_header ipc;
};
struct squashfs_dir_entry {
unsigned int offset:13;
unsigned int type:3;
unsigned int size:8;
int inode_number:16;
char name[0];
} __attribute__ ((packed));
struct squashfs_dir_header {
unsigned int count:8;
unsigned int start_block;
unsigned int inode_number;
} __attribute__ ((packed));
struct squashfs_fragment_entry {
long long start_block;
unsigned int size;
unsigned int unused;
} __attribute__ ((packed));
extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
extern int squashfs_uncompress_init(void);
extern int squashfs_uncompress_exit(void);
/*
* macros to convert each packed bitfield structure from little endian to big
* endian and vice versa. These are needed when creating or using a filesystem
* on a machine with different byte ordering to the target architecture.
*
*/
#define SQUASHFS_SWAP_START \
int bits;\
int b_pos;\
unsigned long long val;\
unsigned char *s;\
unsigned char *d;
#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
SQUASHFS_SWAP((s)->flags, d, 288, 8);\
SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
SQUASHFS_SWAP((s)->unused, d, 888, 64);\
}
#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 8);\
SQUASHFS_SWAP((s)->guid, d, 24, 8);\
SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_ipc_inode_header))\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
}
#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_dev_inode_header)); \
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_symlink_inode_header));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_reg_inode_header));\
SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
SQUASHFS_SWAP((s)->offset, d, 192, 32);\
SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
}
#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_lreg_inode_header));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
SQUASHFS_SWAP((s)->offset, d, 224, 32);\
SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_dir_inode_header));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
SQUASHFS_SWAP((s)->offset, d, 147, 13);\
SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
}
#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
sizeof(struct squashfs_ldir_inode_header));\
SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
SQUASHFS_SWAP((s)->offset, d, 155, 13);\
SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
}
#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
SQUASHFS_SWAP((s)->index, d, 0, 32);\
SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
SQUASHFS_SWAP((s)->size, d, 64, 8);\
}
#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
SQUASHFS_SWAP((s)->count, d, 0, 8);\
SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
}
#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
SQUASHFS_SWAP((s)->offset, d, 0, 13);\
SQUASHFS_SWAP((s)->type, d, 13, 3);\
SQUASHFS_SWAP((s)->size, d, 16, 8);\
SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
}
#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
SQUASHFS_SWAP((s)->size, d, 64, 32);\
}
#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 2);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
16)\
SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
}
#define SQUASHFS_SWAP_INTS(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 4);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
32)\
SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
}
#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * 8);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
64)\
SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
}
#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
int entry;\
int bit_position;\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, n * bits / 8);\
for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
bits)\
SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
}
#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
struct squashfs_base_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int type:4;
unsigned int offset:4;
} __attribute__ ((packed));
struct squashfs_dev_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int mtime;
unsigned int start_block;
unsigned int file_size:32;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_1 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:4; /* index into uid table */
unsigned int guid:4; /* index into guid table */
unsigned int file_size:19;
unsigned int offset:13;
unsigned int mtime;
unsigned int start_block:24;
} __attribute__ ((packed));
#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 4);\
SQUASHFS_SWAP((s)->guid, d, 20, 4);
#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_ipc_inode_header_1));\
SQUASHFS_SWAP((s)->type, d, 24, 4);\
SQUASHFS_SWAP((s)->offset, d, 28, 4);\
}
#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_dev_inode_header_1));\
SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_symlink_inode_header_1));\
SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_reg_inode_header_1));\
SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
sizeof(struct squashfs_dir_inode_header_1));\
SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
SQUASHFS_SWAP((s)->offset, d, 43, 13);\
SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
}
#endif
#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
struct squashfs_dir_index_2 {
unsigned int index:27;
unsigned int start_block:29;
unsigned char size;
unsigned char name[0];
} __attribute__ ((packed));
struct squashfs_base_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_ipc_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
} __attribute__ ((packed));
struct squashfs_dev_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned short rdev;
} __attribute__ ((packed));
struct squashfs_symlink_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned short symlink_size;
char symlink[0];
} __attribute__ ((packed));
struct squashfs_reg_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int mtime;
unsigned int start_block;
unsigned int fragment;
unsigned int offset;
unsigned int file_size:32;
unsigned short block_list[0];
} __attribute__ ((packed));
struct squashfs_dir_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int file_size:19;
unsigned int offset:13;
unsigned int mtime;
unsigned int start_block:24;
} __attribute__ ((packed));
struct squashfs_ldir_inode_header_2 {
unsigned int inode_type:4;
unsigned int mode:12; /* protection */
unsigned int uid:8; /* index into uid table */
unsigned int guid:8; /* index into guid table */
unsigned int file_size:27;
unsigned int offset:13;
unsigned int mtime;
unsigned int start_block:24;
unsigned int i_count:16;
struct squashfs_dir_index_2 index[0];
} __attribute__ ((packed));
union squashfs_inode_header_2 {
struct squashfs_base_inode_header_2 base;
struct squashfs_dev_inode_header_2 dev;
struct squashfs_symlink_inode_header_2 symlink;
struct squashfs_reg_inode_header_2 reg;
struct squashfs_dir_inode_header_2 dir;
struct squashfs_ldir_inode_header_2 ldir;
struct squashfs_ipc_inode_header_2 ipc;
};
struct squashfs_dir_header_2 {
unsigned int count:8;
unsigned int start_block:24;
} __attribute__ ((packed));
struct squashfs_dir_entry_2 {
unsigned int offset:13;
unsigned int type:3;
unsigned int size:8;
char name[0];
} __attribute__ ((packed));
struct squashfs_fragment_entry_2 {
unsigned int start_block;
unsigned int size;
} __attribute__ ((packed));
#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
SQUASHFS_MEMSET(s, d, n);\
SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
SQUASHFS_SWAP((s)->mode, d, 4, 12);\
SQUASHFS_SWAP((s)->uid, d, 16, 8);\
SQUASHFS_SWAP((s)->guid, d, 24, 8);\
#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
}
#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_dev_inode_header_2)); \
SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
}
#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_symlink_inode_header_2));\
SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
}
#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_reg_inode_header_2));\
SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
SQUASHFS_SWAP((s)->offset, d, 128, 32);\
SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
}
#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_dir_inode_header_2));\
SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
SQUASHFS_SWAP((s)->offset, d, 51, 13);\
SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
}
#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
sizeof(struct squashfs_ldir_inode_header_2));\
SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
SQUASHFS_SWAP((s)->offset, d, 59, 13);\
SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
}
#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
SQUASHFS_SWAP((s)->index, d, 0, 27);\
SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
SQUASHFS_SWAP((s)->size, d, 56, 8);\
}
#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
SQUASHFS_SWAP((s)->count, d, 0, 8);\
SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
}
#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
SQUASHFS_SWAP((s)->offset, d, 0, 13);\
SQUASHFS_SWAP((s)->type, d, 13, 3);\
SQUASHFS_SWAP((s)->size, d, 16, 8);\
}
#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
SQUASHFS_SWAP_START\
SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
SQUASHFS_SWAP((s)->size, d, 32, 32);\
}
#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2))
#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
sizeof(int))
#endif
#ifdef __KERNEL__
/*
* macros used to swap each structure entry, taking into account
* bitfields and different bitfield placing conventions on differing
* architectures
*/
#include <asm/byteorder.h>
#ifdef __BIG_ENDIAN
/* convert from little endian to big endian */
#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
tbits, b_pos)
#else
/* convert from big endian to little endian */
#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
tbits, 64 - tbits - b_pos)
#endif
#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
b_pos = pos % 8;\
val = 0;\
s = (unsigned char *)p + (pos / 8);\
d = ((unsigned char *) &val) + 7;\
for(bits = 0; bits < (tbits + b_pos); bits += 8) \
*d-- = *s++;\
value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
}
#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n);
#endif
#endif

View file

@ -0,0 +1,44 @@
#ifndef SQUASHFS_FS_I
#define SQUASHFS_FS_I
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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_fs_i.h
*/
struct squashfs_inode_info {
long long start_block;
unsigned int offset;
union {
struct {
long long fragment_start_block;
unsigned int fragment_size;
unsigned int fragment_offset;
long long block_list_start;
} s1;
struct {
long long directory_index_start;
unsigned int directory_index_offset;
unsigned int directory_index_count;
unsigned int parent_inode;
} s2;
} u;
};
#endif

View file

@ -0,0 +1,76 @@
#ifndef SQUASHFS_FS_SB
#define SQUASHFS_FS_SB
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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_fs_sb.h
*/
#include <linux/squashfs_fs.h>
#include <linux/zlib.h>
struct squashfs_cache {
long long block;
int length;
long long next_index;
char *data;
};
struct squashfs_fragment_cache {
long long block;
int length;
unsigned int locked;
char *data;
};
struct squashfs_sb_info {
struct squashfs_super_block sblk;
int devblksize;
int devblksize_log2;
int swap;
struct squashfs_cache *block_cache;
struct squashfs_fragment_cache *fragment;
int next_cache;
int next_fragment;
int next_meta_index;
unsigned int *uid;
unsigned int *guid;
long long *fragment_index;
unsigned int *fragment_index_2;
unsigned int read_size;
char *read_data;
char *read_page;
struct semaphore read_data_mutex;
struct semaphore read_page_mutex;
struct semaphore block_cache_mutex;
struct semaphore fragment_mutex;
struct semaphore meta_index_mutex;
wait_queue_head_t waitq;
wait_queue_head_t fragment_wait_queue;
struct meta_index *meta_index;
z_stream stream;
struct inode *(*iget)(struct super_block *s, squashfs_inode_t
inode);
long long (*read_blocklist)(struct inode *inode, int
index, int readahead_blks, char *block_list,
unsigned short **block_p, unsigned int *bsize);
int (*read_fragment_index_table)(struct super_block *s);
};
#endif

View file

@ -0,0 +1,225 @@
SQUASHFS 4.0 FILESYSTEM
=======================
Squashfs is a compressed read-only filesystem for Linux.
It uses zlib compression to compress files, inodes and directories.
Inodes in the system are very small and all blocks are packed to minimise
data overhead. Block sizes greater than 4K are supported up to a maximum
of 1Mbytes (default block size 128K).
Squashfs is intended for general read-only filesystem use, for archival
use (i.e. in cases where a .tar.gz file may be used), and in constrained
block device/memory systems (e.g. embedded systems) where low overhead is
needed.
Mailing list: squashfs-devel@lists.sourceforge.net
Web site: www.squashfs.org
1. FILESYSTEM FEATURES
----------------------
Squashfs filesystem features versus Cramfs:
Squashfs Cramfs
Max filesystem size: 2^64 16 MiB
Max file size: ~ 2 TiB 16 MiB
Max files: unlimited unlimited
Max directories: unlimited unlimited
Max entries per directory: unlimited unlimited
Max block size: 1 MiB 4 KiB
Metadata compression: yes no
Directory indexes: yes no
Sparse file support: yes no
Tail-end packing (fragments): yes no
Exportable (NFS etc.): yes no
Hard link support: yes no
"." and ".." in readdir: yes no
Real inode numbers: yes no
32-bit uids/gids: yes no
File creation time: yes no
Xattr and ACL support: no no
Squashfs compresses data, inodes and directories. In addition, inode and
directory data are highly compacted, and packed on byte boundaries. Each
compressed inode is on average 8 bytes in length (the exact length varies on
file type, i.e. regular file, directory, symbolic link, and block/char device
inodes have different sizes).
2. USING SQUASHFS
-----------------
As squashfs is a read-only filesystem, the mksquashfs program must be used to
create populated squashfs filesystems. This and other squashfs utilities
can be obtained from http://www.squashfs.org. Usage instructions can be
obtained from this site also.
3. SQUASHFS FILESYSTEM DESIGN
-----------------------------
A squashfs filesystem consists of seven parts, packed together on a byte
alignment:
---------------
| superblock |
|---------------|
| datablocks |
| & fragments |
|---------------|
| inode table |
|---------------|
| directory |
| table |
|---------------|
| fragment |
| table |
|---------------|
| export |
| table |
|---------------|
| uid/gid |
| lookup table |
---------------
Compressed data blocks are written to the filesystem as files are read from
the source directory, and checked for duplicates. Once all file data has been
written the completed inode, directory, fragment, export and uid/gid lookup
tables are written.
3.1 Inodes
----------
Metadata (inodes and directories) are compressed in 8Kbyte blocks. Each
compressed block is prefixed by a two byte length, the top bit is set if the
block is uncompressed. A block will be uncompressed if the -noI option is set,
or if the compressed block was larger than the uncompressed block.
Inodes are packed into the metadata blocks, and are not aligned to block
boundaries, therefore inodes overlap compressed blocks. Inodes are identified
by a 48-bit number which encodes the location of the compressed metadata block
containing the inode, and the byte offset into that block where the inode is
placed (<block, offset>).
To maximise compression there are different inodes for each file type
(regular file, directory, device, etc.), the inode contents and length
varying with the type.
To further maximise compression, two types of regular file inode and
directory inode are defined: inodes optimised for frequently occurring
regular files and directories, and extended types where extra
information has to be stored.
3.2 Directories
---------------
Like inodes, directories are packed into compressed metadata blocks, stored
in a directory table. Directories are accessed using the start address of
the metablock containing the directory and the offset into the
decompressed block (<block, offset>).
Directories are organised in a slightly complex way, and are not simply
a list of file names. The organisation takes advantage of the
fact that (in most cases) the inodes of the files will be in the same
compressed metadata block, and therefore, can share the start block.
Directories are therefore organised in a two level list, a directory
header containing the shared start block value, and a sequence of directory
entries, each of which share the shared start block. A new directory header
is written once/if the inode start block changes. The directory
header/directory entry list is repeated as many times as necessary.
Directories are sorted, and can contain a directory index to speed up
file lookup. Directory indexes store one entry per metablock, each entry
storing the index/filename mapping to the first directory header
in each metadata block. Directories are sorted in alphabetical order,
and at lookup the index is scanned linearly looking for the first filename
alphabetically larger than the filename being looked up. At this point the
location of the metadata block the filename is in has been found.
The general idea of the index is ensure only one metadata block needs to be
decompressed to do a lookup irrespective of the length of the directory.
This scheme has the advantage that it doesn't require extra memory overhead
and doesn't require much extra storage on disk.
3.3 File data
-------------
Regular files consist of a sequence of contiguous compressed blocks, and/or a
compressed fragment block (tail-end packed block). The compressed size
of each datablock is stored in a block list contained within the
file inode.
To speed up access to datablocks when reading 'large' files (256 Mbytes or
larger), the code implements an index cache that caches the mapping from
block index to datablock location on disk.
The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
retaining a simple and space-efficient block list on disk. The cache
is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
The index cache is designed to be memory efficient, and by default uses
16 KiB.
3.4 Fragment lookup table
-------------------------
Regular files can contain a fragment index which is mapped to a fragment
location on disk and compressed size using a fragment lookup table. This
fragment lookup table is itself stored compressed into metadata blocks.
A second index table is used to locate these. This second index table for
speed of access (and because it is small) is read at mount time and cached
in memory.
3.5 Uid/gid lookup table
------------------------
For space efficiency regular files store uid and gid indexes, which are
converted to 32-bit uids/gids using an id look up table. This table is
stored compressed into metadata blocks. A second index table is used to
locate these. This second index table for speed of access (and because it
is small) is read at mount time and cached in memory.
3.6 Export table
----------------
To enable Squashfs filesystems to be exportable (via NFS etc.) filesystems
can optionally (disabled with the -no-exports Mksquashfs option) contain
an inode number to inode disk location lookup table. This is required to
enable Squashfs to map inode numbers passed in filehandles to the inode
location on disk, which is necessary when the export code reinstantiates
expired/flushed inodes.
This table is stored compressed into metadata blocks. A second index table is
used to locate these. This second index table for speed of access (and because
it is small) is read at mount time and cached in memory.
4. TODOS AND OUTSTANDING ISSUES
-------------------------------
4.1 Todo list
-------------
Implement Xattr and ACL support. The Squashfs 4.0 filesystem layout has hooks
for these but the code has not been written. Once the code has been written
the existing layout should not require modification.
4.2 Squashfs internal cache
---------------------------
Blocks in Squashfs are compressed. To avoid repeatedly decompressing
recently accessed data Squashfs uses two small metadata and fragment caches.
The cache is not used for file datablocks, these are decompressed and cached in
the page-cache in the normal way. The cache is used to temporarily cache
fragment and metadata blocks which have been read as a result of a metadata
(i.e. inode or directory) or fragment access. Because metadata and fragments
are packed together into blocks (to gain greater compression) the read of a
particular piece of metadata or fragment will retrieve other metadata/fragments
which have been packed with it, these because of locality-of-reference may be
read in the near future. Temporarily caching them ensures they are available
for near future access without requiring an additional read and decompress.
In the future this internal cache may be replaced with an implementation which
uses the kernel page cache. Because the page cache operates on page sized
units this may introduce additional complexity in terms of locking and
associated race conditions.

View file

@ -0,0 +1,8 @@
#
# Makefile for the linux squashfs routines.
#
obj-$(CONFIG_SQUASHFS) += squashfs.o
squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
squashfs-y += namei.o super.o symlink.o
#squashfs-y += squashfs2_0.o

View file

@ -0,0 +1,274 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* block.c
*/
/*
* This file implements the low-level routines to read and decompress
* datablocks and metadata blocks.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/buffer_head.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Read the metadata block length, this is stored in the first two
* bytes of the metadata block.
*/
static struct buffer_head *get_block_length(struct super_block *sb,
u64 *cur_index, int *offset, int *length)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
struct buffer_head *bh;
bh = sb_bread(sb, *cur_index);
if (bh == NULL)
return NULL;
if (msblk->devblksize - *offset == 1) {
*length = (unsigned char) bh->b_data[*offset];
put_bh(bh);
bh = sb_bread(sb, ++(*cur_index));
if (bh == NULL)
return NULL;
*length |= (unsigned char) bh->b_data[0] << 8;
*offset = 1;
} else {
*length = (unsigned char) bh->b_data[*offset] |
(unsigned char) bh->b_data[*offset + 1] << 8;
*offset += 2;
}
return bh;
}
/*
* Read and decompress a metadata block or datablock. Length is non-zero
* if a datablock is being read (the size is stored elsewhere in the
* filesystem), otherwise the length is obtained from the first two bytes of
* the metadata block. A bit in the length field indicates if the block
* is stored uncompressed in the filesystem (usually because compression
* generated a larger block - this does occasionally happen with zlib).
*/
int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
int length, u64 *next_index, int srclength)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
struct buffer_head **bh;
int offset = index & ((1 << msblk->devblksize_log2) - 1);
u64 cur_index = index >> msblk->devblksize_log2;
int bytes, compressed, b = 0, k = 0, page = 0, avail;
bh = kcalloc((msblk->block_size >> msblk->devblksize_log2) + 1,
sizeof(*bh), GFP_KERNEL);
if (bh == NULL)
return -ENOMEM;
if (length) {
/*
* Datablock.
*/
bytes = -offset;
compressed = SQUASHFS_COMPRESSED_BLOCK(length);
length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
if (next_index)
*next_index = index + length;
TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
index, compressed ? "" : "un", length, srclength);
if (length < 0 || length > srclength ||
(index + length) > msblk->bytes_used)
goto read_failure;
for (b = 0; bytes < length; b++, cur_index++) {
bh[b] = sb_getblk(sb, cur_index);
if (bh[b] == NULL)
goto block_release;
bytes += msblk->devblksize;
}
ll_rw_block(READ, b, bh);
} else {
/*
* Metadata block.
*/
if ((index + 2) > msblk->bytes_used)
goto read_failure;
bh[0] = get_block_length(sb, &cur_index, &offset, &length);
if (bh[0] == NULL)
goto read_failure;
b = 1;
bytes = msblk->devblksize - offset;
compressed = SQUASHFS_COMPRESSED(length);
length = SQUASHFS_COMPRESSED_SIZE(length);
if (next_index)
*next_index = index + length + 2;
TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
compressed ? "" : "un", length);
if (length < 0 || length > srclength ||
(index + length) > msblk->bytes_used)
goto block_release;
for (; bytes < length; b++) {
bh[b] = sb_getblk(sb, ++cur_index);
if (bh[b] == NULL)
goto block_release;
bytes += msblk->devblksize;
}
ll_rw_block(READ, b - 1, bh + 1);
}
if (compressed) {
int zlib_err = 0, zlib_init = 0;
/*
* Uncompress block.
*/
mutex_lock(&msblk->read_data_mutex);
msblk->stream.avail_out = 0;
msblk->stream.avail_in = 0;
bytes = length;
do {
if (msblk->stream.avail_in == 0 && k < b) {
avail = min(bytes, msblk->devblksize - offset);
bytes -= avail;
wait_on_buffer(bh[k]);
if (!buffer_uptodate(bh[k]))
goto release_mutex;
if (avail == 0) {
offset = 0;
put_bh(bh[k++]);
continue;
}
msblk->stream.next_in = bh[k]->b_data + offset;
msblk->stream.avail_in = avail;
offset = 0;
}
if (msblk->stream.avail_out == 0) {
msblk->stream.next_out = buffer[page++];
msblk->stream.avail_out = PAGE_CACHE_SIZE;
}
if (!zlib_init) {
zlib_err = zlib_inflateInit(&msblk->stream);
if (zlib_err != Z_OK) {
ERROR("zlib_inflateInit returned"
" unexpected result 0x%x,"
" srclength %d\n", zlib_err,
srclength);
goto release_mutex;
}
zlib_init = 1;
}
zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
if (msblk->stream.avail_in == 0 && k < b)
put_bh(bh[k++]);
} while (zlib_err == Z_OK);
if (zlib_err != Z_STREAM_END) {
ERROR("zlib_inflate returned unexpected result"
" 0x%x, srclength %d, avail_in %d,"
" avail_out %d\n", zlib_err, srclength,
msblk->stream.avail_in,
msblk->stream.avail_out);
goto release_mutex;
}
zlib_err = zlib_inflateEnd(&msblk->stream);
if (zlib_err != Z_OK) {
ERROR("zlib_inflateEnd returned unexpected result 0x%x,"
" srclength %d\n", zlib_err, srclength);
goto release_mutex;
}
length = msblk->stream.total_out;
mutex_unlock(&msblk->read_data_mutex);
} else {
/*
* Block is uncompressed.
*/
int i, in, pg_offset = 0;
for (i = 0; i < b; i++) {
wait_on_buffer(bh[i]);
if (!buffer_uptodate(bh[i]))
goto block_release;
}
for (bytes = length; k < b; k++) {
in = min(bytes, msblk->devblksize - offset);
bytes -= in;
while (in) {
if (pg_offset == PAGE_CACHE_SIZE) {
page++;
pg_offset = 0;
}
avail = min_t(int, in, PAGE_CACHE_SIZE -
pg_offset);
memcpy(buffer[page] + pg_offset,
bh[k]->b_data + offset, avail);
in -= avail;
pg_offset += avail;
offset += avail;
}
offset = 0;
put_bh(bh[k]);
}
}
kfree(bh);
return length;
release_mutex:
mutex_unlock(&msblk->read_data_mutex);
block_release:
for (; k < b; k++)
put_bh(bh[k]);
read_failure:
ERROR("sb_bread failed reading block 0x%llx\n", cur_index);
kfree(bh);
return -EIO;
}

View file

@ -0,0 +1,412 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* cache.c
*/
/*
* Blocks in Squashfs are compressed. To avoid repeatedly decompressing
* recently accessed data Squashfs uses two small metadata and fragment caches.
*
* This file implements a generic cache implementation used for both caches,
* plus functions layered ontop of the generic cache implementation to
* access the metadata and fragment caches.
*
* To avoid out of memory and fragmentation isssues with vmalloc the cache
* uses sequences of kmalloced PAGE_CACHE_SIZE buffers.
*
* It should be noted that the cache is not used for file datablocks, these
* are decompressed and cached in the page-cache in the normal way. The
* cache is only used to temporarily cache fragment and metadata blocks
* which have been read as as a result of a metadata (i.e. inode or
* directory) or fragment access. Because metadata and fragments are packed
* together into blocks (to gain greater compression) the read of a particular
* piece of metadata or fragment will retrieve other metadata/fragments which
* have been packed with it, these because of locality-of-reference may be read
* in the near future. Temporarily caching them ensures they are available for
* near future access without requiring an additional read and decompress.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/zlib.h>
#include <linux/pagemap.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Look-up block in cache, and increment usage count. If not in cache, read
* and decompress it from disk.
*/
struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
struct squashfs_cache *cache, u64 block, int length)
{
int i, n;
struct squashfs_cache_entry *entry;
spin_lock(&cache->lock);
while (1) {
for (i = 0; i < cache->entries; i++)
if (cache->entry[i].block == block)
break;
if (i == cache->entries) {
/*
* Block not in cache, if all cache entries are used
* go to sleep waiting for one to become available.
*/
if (cache->unused == 0) {
cache->num_waiters++;
spin_unlock(&cache->lock);
wait_event(cache->wait_queue, cache->unused);
spin_lock(&cache->lock);
cache->num_waiters--;
continue;
}
/*
* At least one unused cache entry. A simple
* round-robin strategy is used to choose the entry to
* be evicted from the cache.
*/
i = cache->next_blk;
for (n = 0; n < cache->entries; n++) {
if (cache->entry[i].refcount == 0)
break;
i = (i + 1) % cache->entries;
}
cache->next_blk = (i + 1) % cache->entries;
entry = &cache->entry[i];
/*
* Initialise choosen cache entry, and fill it in from
* disk.
*/
cache->unused--;
entry->block = block;
entry->refcount = 1;
entry->pending = 1;
entry->num_waiters = 0;
entry->error = 0;
spin_unlock(&cache->lock);
entry->length = squashfs_read_data(sb, entry->data,
block, length, &entry->next_index,
cache->block_size);
spin_lock(&cache->lock);
if (entry->length < 0)
entry->error = entry->length;
entry->pending = 0;
/*
* While filling this entry one or more other processes
* have looked it up in the cache, and have slept
* waiting for it to become available.
*/
if (entry->num_waiters) {
spin_unlock(&cache->lock);
wake_up_all(&entry->wait_queue);
} else
spin_unlock(&cache->lock);
goto out;
}
/*
* Block already in cache. Increment refcount so it doesn't
* get reused until we're finished with it, if it was
* previously unused there's one less cache entry available
* for reuse.
*/
entry = &cache->entry[i];
if (entry->refcount == 0)
cache->unused--;
entry->refcount++;
/*
* If the entry is currently being filled in by another process
* go to sleep waiting for it to become available.
*/
if (entry->pending) {
entry->num_waiters++;
spin_unlock(&cache->lock);
wait_event(entry->wait_queue, !entry->pending);
} else
spin_unlock(&cache->lock);
goto out;
}
out:
TRACE("Got %s %d, start block %lld, refcount %d, error %d\n",
cache->name, i, entry->block, entry->refcount, entry->error);
if (entry->error)
ERROR("Unable to read %s cache entry [%llx]\n", cache->name,
block);
return entry;
}
/*
* Release cache entry, once usage count is zero it can be reused.
*/
void squashfs_cache_put(struct squashfs_cache_entry *entry)
{
struct squashfs_cache *cache = entry->cache;
spin_lock(&cache->lock);
entry->refcount--;
if (entry->refcount == 0) {
cache->unused++;
/*
* If there's any processes waiting for a block to become
* available, wake one up.
*/
if (cache->num_waiters) {
spin_unlock(&cache->lock);
wake_up(&cache->wait_queue);
return;
}
}
spin_unlock(&cache->lock);
}
/*
* Delete cache reclaiming all kmalloced buffers.
*/
void squashfs_cache_delete(struct squashfs_cache *cache)
{
int i, j;
if (cache == NULL)
return;
for (i = 0; i < cache->entries; i++) {
if (cache->entry[i].data) {
for (j = 0; j < cache->pages; j++)
kfree(cache->entry[i].data[j]);
kfree(cache->entry[i].data);
}
}
kfree(cache->entry);
kfree(cache);
}
/*
* Initialise cache allocating the specified number of entries, each of
* size block_size. To avoid vmalloc fragmentation issues each entry
* is allocated as a sequence of kmalloced PAGE_CACHE_SIZE buffers.
*/
struct squashfs_cache *squashfs_cache_init(char *name, int entries,
int block_size)
{
int i, j;
struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);
if (cache == NULL) {
ERROR("Failed to allocate %s cache\n", name);
return NULL;
}
cache->entry = kcalloc(entries, sizeof(*(cache->entry)), GFP_KERNEL);
if (cache->entry == NULL) {
ERROR("Failed to allocate %s cache\n", name);
goto cleanup;
}
cache->next_blk = 0;
cache->unused = entries;
cache->entries = entries;
cache->block_size = block_size;
cache->pages = block_size >> PAGE_CACHE_SHIFT;
cache->name = name;
cache->num_waiters = 0;
spin_lock_init(&cache->lock);
init_waitqueue_head(&cache->wait_queue);
for (i = 0; i < entries; i++) {
struct squashfs_cache_entry *entry = &cache->entry[i];
init_waitqueue_head(&cache->entry[i].wait_queue);
entry->cache = cache;
entry->block = SQUASHFS_INVALID_BLK;
entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
if (entry->data == NULL) {
ERROR("Failed to allocate %s cache entry\n", name);
goto cleanup;
}
for (j = 0; j < cache->pages; j++) {
entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
if (entry->data[j] == NULL) {
ERROR("Failed to allocate %s buffer\n", name);
goto cleanup;
}
}
}
return cache;
cleanup:
squashfs_cache_delete(cache);
return NULL;
}
/*
* Copy upto length bytes from cache entry to buffer starting at offset bytes
* into the cache entry. If there's not length bytes then copy the number of
* bytes available. In all cases return the number of bytes copied.
*/
int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
int offset, int length)
{
int remaining = length;
if (length == 0)
return 0;
else if (buffer == NULL)
return min(length, entry->length - offset);
while (offset < entry->length) {
void *buff = entry->data[offset / PAGE_CACHE_SIZE]
+ (offset % PAGE_CACHE_SIZE);
int bytes = min_t(int, entry->length - offset,
PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE));
if (bytes >= remaining) {
memcpy(buffer, buff, remaining);
remaining = 0;
break;
}
memcpy(buffer, buff, bytes);
buffer += bytes;
remaining -= bytes;
offset += bytes;
}
return length - remaining;
}
/*
* Read length bytes from metadata position <block, offset> (block is the
* start of the compressed block on disk, and offset is the offset into
* the block once decompressed). Data is packed into consecutive blocks,
* and length bytes may require reading more than one block.
*/
int squashfs_read_metadata(struct super_block *sb, void *buffer,
u64 *block, int *offset, int length)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int bytes, copied = length;
struct squashfs_cache_entry *entry;
TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset);
while (length) {
entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0);
if (entry->error)
return entry->error;
else if (*offset >= entry->length)
return -EIO;
bytes = squashfs_copy_data(buffer, entry, *offset, length);
if (buffer)
buffer += bytes;
length -= bytes;
*offset += bytes;
if (*offset == entry->length) {
*block = entry->next_index;
*offset = 0;
}
squashfs_cache_put(entry);
}
return copied;
}
/*
* Look-up in the fragmment cache the fragment located at <start_block> in the
* filesystem. If necessary read and decompress it from disk.
*/
struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *sb,
u64 start_block, int length)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
return squashfs_cache_get(sb, msblk->fragment_cache, start_block,
length);
}
/*
* Read and decompress the datablock located at <start_block> in the
* filesystem. The cache is used here to avoid duplicating locking and
* read/decompress code.
*/
struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,
u64 start_block, int length)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
return squashfs_cache_get(sb, msblk->read_page, start_block, length);
}
/*
* Read a filesystem table (uncompressed sequence of bytes) from disk
*/
int squashfs_read_table(struct super_block *sb, void *buffer, u64 block,
int length)
{
int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
int i, res;
void **data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
data[i] = buffer;
res = squashfs_read_data(sb, data, block, length |
SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length);
kfree(data);
return res;
}

View file

@ -0,0 +1,235 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* dir.c
*/
/*
* This file implements code to read directories from disk.
*
* See namei.c for a description of directory organisation on disk.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
static const unsigned char squashfs_filetype_table[] = {
DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
};
/*
* Lookup offset (f_pos) in the directory index, returning the
* metadata block containing it.
*
* If we get an error reading the index then return the part of the index
* (if any) we have managed to read - the index isn't essential, just
* quicker.
*/
static int get_dir_index_using_offset(struct super_block *sb,
u64 *next_block, int *next_offset, u64 index_start, int index_offset,
int i_count, u64 f_pos)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int err, i, index, length = 0;
struct squashfs_dir_index dir_index;
TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
i_count, f_pos);
/*
* Translate from external f_pos to the internal f_pos. This
* is offset by 3 because we invent "." and ".." entries which are
* not actually stored in the directory.
*/
if (f_pos < 3)
return f_pos;
f_pos -= 3;
for (i = 0; i < i_count; i++) {
err = squashfs_read_metadata(sb, &dir_index, &index_start,
&index_offset, sizeof(dir_index));
if (err < 0)
break;
index = le32_to_cpu(dir_index.index);
if (index > f_pos)
/*
* Found the index we're looking for.
*/
break;
err = squashfs_read_metadata(sb, NULL, &index_start,
&index_offset, le32_to_cpu(dir_index.size) + 1);
if (err < 0)
break;
length = index;
*next_block = le32_to_cpu(dir_index.start_block) +
msblk->directory_table;
}
*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
/*
* Translate back from internal f_pos to external f_pos.
*/
return length + 3;
}
static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
{
struct inode *inode = file->f_dentry->d_inode;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
u64 block = squashfs_i(inode)->start + msblk->directory_table;
int offset = squashfs_i(inode)->offset, length = 0, dir_count, size,
type, err;
unsigned int inode_number;
struct squashfs_dir_header dirh;
struct squashfs_dir_entry *dire;
TRACE("Entered squashfs_readdir [%llx:%x]\n", block, offset);
dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
if (dire == NULL) {
ERROR("Failed to allocate squashfs_dir_entry\n");
goto finish;
}
/*
* Return "." and ".." entries as the first two filenames in the
* directory. To maximise compression these two entries are not
* stored in the directory, and so we invent them here.
*
* It also means that the external f_pos is offset by 3 from the
* on-disk directory f_pos.
*/
while (file->f_pos < 3) {
char *name;
int i_ino;
if (file->f_pos == 0) {
name = ".";
size = 1;
i_ino = inode->i_ino;
} else {
name = "..";
size = 2;
i_ino = squashfs_i(inode)->parent;
}
TRACE("Calling filldir(%p, %s, %d, %lld, %d, %d)\n",
dirent, name, size, file->f_pos, i_ino,
squashfs_filetype_table[1]);
if (filldir(dirent, name, size, file->f_pos, i_ino,
squashfs_filetype_table[1]) < 0) {
TRACE("Filldir returned less than 0\n");
goto finish;
}
file->f_pos += size;
}
length = get_dir_index_using_offset(inode->i_sb, &block, &offset,
squashfs_i(inode)->dir_idx_start,
squashfs_i(inode)->dir_idx_offset,
squashfs_i(inode)->dir_idx_cnt,
file->f_pos);
while (length < i_size_read(inode)) {
/*
* Read directory header
*/
err = squashfs_read_metadata(inode->i_sb, &dirh, &block,
&offset, sizeof(dirh));
if (err < 0)
goto failed_read;
length += sizeof(dirh);
dir_count = le32_to_cpu(dirh.count) + 1;
while (dir_count--) {
/*
* Read directory entry.
*/
err = squashfs_read_metadata(inode->i_sb, dire, &block,
&offset, sizeof(*dire));
if (err < 0)
goto failed_read;
size = le16_to_cpu(dire->size) + 1;
err = squashfs_read_metadata(inode->i_sb, dire->name,
&block, &offset, size);
if (err < 0)
goto failed_read;
length += sizeof(*dire) + size;
if (file->f_pos >= length)
continue;
dire->name[size] = '\0';
inode_number = le32_to_cpu(dirh.inode_number) +
((short) le16_to_cpu(dire->inode_number));
type = le16_to_cpu(dire->type);
TRACE("Calling filldir(%p, %s, %d, %lld, %x:%x, %d, %d)"
"\n", dirent, dire->name, size,
file->f_pos,
le32_to_cpu(dirh.start_block),
le16_to_cpu(dire->offset),
inode_number,
squashfs_filetype_table[type]);
if (filldir(dirent, dire->name, size, file->f_pos,
inode_number,
squashfs_filetype_table[type]) < 0) {
TRACE("Filldir returned less than 0\n");
goto finish;
}
file->f_pos = length;
}
}
finish:
kfree(dire);
return 0;
failed_read:
ERROR("Unable to read directory block [%llx:%x]\n", block, offset);
kfree(dire);
return 0;
}
const struct file_operations squashfs_dir_ops = {
.read = generic_read_dir,
.readdir = squashfs_readdir
};

View file

@ -0,0 +1,155 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* export.c
*/
/*
* This file implements code to make Squashfs filesystems exportable (NFS etc.)
*
* The export code uses an inode lookup table to map inode numbers passed in
* filehandles to an inode location on disk. This table is stored compressed
* into metadata blocks. A second index table is used to locate these. This
* second index table for speed of access (and because it is small) is read at
* mount time and cached in memory.
*
* The inode lookup table is used only by the export code, inode disk
* locations are directly encoded in directories, enabling direct access
* without an intermediate lookup for all operations except the export ops.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/dcache.h>
#include <linux/exportfs.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Look-up inode number (ino) in table, returning the inode location.
*/
static long long squashfs_inode_lookup(struct super_block *sb, int ino_num)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int blk = SQUASHFS_LOOKUP_BLOCK(ino_num - 1);
int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino_num - 1);
u64 start = le64_to_cpu(msblk->inode_lookup_table[blk]);
__le64 ino;
int err;
TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino_num);
err = squashfs_read_metadata(sb, &ino, &start, &offset, sizeof(ino));
if (err < 0)
return err;
TRACE("squashfs_inode_lookup, inode = 0x%llx\n",
(u64) le64_to_cpu(ino));
return le64_to_cpu(ino);
}
static struct dentry *squashfs_export_iget(struct super_block *sb,
unsigned int ino_num)
{
long long ino;
struct dentry *dentry = ERR_PTR(-ENOENT);
TRACE("Entered squashfs_export_iget\n");
ino = squashfs_inode_lookup(sb, ino_num);
if (ino >= 0)
dentry = d_obtain_alias(squashfs_iget(sb, ino, ino_num));
return dentry;
}
static struct dentry *squashfs_fh_to_dentry(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type)
{
if ((fh_type != FILEID_INO32_GEN && fh_type != FILEID_INO32_GEN_PARENT)
|| fh_len < 2)
return NULL;
return squashfs_export_iget(sb, fid->i32.ino);
}
static struct dentry *squashfs_fh_to_parent(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type)
{
if (fh_type != FILEID_INO32_GEN_PARENT || fh_len < 4)
return NULL;
return squashfs_export_iget(sb, fid->i32.parent_ino);
}
static struct dentry *squashfs_get_parent(struct dentry *child)
{
struct inode *inode = child->d_inode;
unsigned int parent_ino = squashfs_i(inode)->parent;
return squashfs_export_iget(inode->i_sb, parent_ino);
}
/*
* Read uncompressed inode lookup table indexes off disk into memory
*/
__le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
u64 lookup_table_start, unsigned int inodes)
{
unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
__le64 *inode_lookup_table;
int err;
TRACE("In read_inode_lookup_table, length %d\n", length);
/* Allocate inode lookup table indexes */
inode_lookup_table = kmalloc(length, GFP_KERNEL);
if (inode_lookup_table == NULL) {
ERROR("Failed to allocate inode lookup table\n");
return ERR_PTR(-ENOMEM);
}
err = squashfs_read_table(sb, inode_lookup_table, lookup_table_start,
length);
if (err < 0) {
ERROR("unable to read inode lookup table\n");
kfree(inode_lookup_table);
return ERR_PTR(err);
}
return inode_lookup_table;
}
const struct export_operations squashfs_export_ops = {
.fh_to_dentry = squashfs_fh_to_dentry,
.fh_to_parent = squashfs_fh_to_parent,
.get_parent = squashfs_get_parent
};

View file

@ -0,0 +1,502 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* file.c
*/
/*
* This file contains code for handling regular files. A regular file
* consists of a sequence of contiguous compressed blocks, and/or a
* compressed fragment block (tail-end packed block). The compressed size
* of each datablock is stored in a block list contained within the
* file inode (itself stored in one or more compressed metadata blocks).
*
* To speed up access to datablocks when reading 'large' files (256 Mbytes or
* larger), the code implements an index cache that caches the mapping from
* block index to datablock location on disk.
*
* The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
* retaining a simple and space-efficient block list on disk. The cache
* is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
* Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
* The index cache is designed to be memory efficient, and by default uses
* 16 KiB.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mutex.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Locate cache slot in range [offset, index] for specified inode. If
* there's more than one return the slot closest to index.
*/
static struct meta_index *locate_meta_index(struct inode *inode, int offset,
int index)
{
struct meta_index *meta = NULL;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
int i;
mutex_lock(&msblk->meta_index_mutex);
TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
if (msblk->meta_index == NULL)
goto not_allocated;
for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
if (msblk->meta_index[i].inode_number == inode->i_ino &&
msblk->meta_index[i].offset >= offset &&
msblk->meta_index[i].offset <= index &&
msblk->meta_index[i].locked == 0) {
TRACE("locate_meta_index: entry %d, offset %d\n", i,
msblk->meta_index[i].offset);
meta = &msblk->meta_index[i];
offset = meta->offset;
}
}
if (meta)
meta->locked = 1;
not_allocated:
mutex_unlock(&msblk->meta_index_mutex);
return meta;
}
/*
* Find and initialise an empty cache slot for index offset.
*/
static struct meta_index *empty_meta_index(struct inode *inode, int offset,
int skip)
{
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
struct meta_index *meta = NULL;
int i;
mutex_lock(&msblk->meta_index_mutex);
TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
if (msblk->meta_index == NULL) {
/*
* First time cache index has been used, allocate and
* initialise. The cache index could be allocated at
* mount time but doing it here means it is allocated only
* if a 'large' file is read.
*/
msblk->meta_index = kcalloc(SQUASHFS_META_SLOTS,
sizeof(*(msblk->meta_index)), GFP_KERNEL);
if (msblk->meta_index == NULL) {
ERROR("Failed to allocate meta_index\n");
goto failed;
}
for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
msblk->meta_index[i].inode_number = 0;
msblk->meta_index[i].locked = 0;
}
msblk->next_meta_index = 0;
}
for (i = SQUASHFS_META_SLOTS; i &&
msblk->meta_index[msblk->next_meta_index].locked; i--)
msblk->next_meta_index = (msblk->next_meta_index + 1) %
SQUASHFS_META_SLOTS;
if (i == 0) {
TRACE("empty_meta_index: failed!\n");
goto failed;
}
TRACE("empty_meta_index: returned meta entry %d, %p\n",
msblk->next_meta_index,
&msblk->meta_index[msblk->next_meta_index]);
meta = &msblk->meta_index[msblk->next_meta_index];
msblk->next_meta_index = (msblk->next_meta_index + 1) %
SQUASHFS_META_SLOTS;
meta->inode_number = inode->i_ino;
meta->offset = offset;
meta->skip = skip;
meta->entries = 0;
meta->locked = 1;
failed:
mutex_unlock(&msblk->meta_index_mutex);
return meta;
}
static void release_meta_index(struct inode *inode, struct meta_index *meta)
{
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
mutex_lock(&msblk->meta_index_mutex);
meta->locked = 0;
mutex_unlock(&msblk->meta_index_mutex);
}
/*
* Read the next n blocks from the block list, starting from
* metadata block <start_block, offset>.
*/
static long long read_indexes(struct super_block *sb, int n,
u64 *start_block, int *offset)
{
int err, i;
long long block = 0;
__le32 *blist = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
if (blist == NULL) {
ERROR("read_indexes: Failed to allocate block_list\n");
return -ENOMEM;
}
while (n) {
int blocks = min_t(int, n, PAGE_CACHE_SIZE >> 2);
err = squashfs_read_metadata(sb, blist, start_block,
offset, blocks << 2);
if (err < 0) {
ERROR("read_indexes: reading block [%llx:%x]\n",
*start_block, *offset);
goto failure;
}
for (i = 0; i < blocks; i++) {
int size = le32_to_cpu(blist[i]);
block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
}
n -= blocks;
}
kfree(blist);
return block;
failure:
kfree(blist);
return err;
}
/*
* Each cache index slot has SQUASHFS_META_ENTRIES, each of which
* can cache one index -> datablock/blocklist-block mapping. We wish
* to distribute these over the length of the file, entry[0] maps index x,
* entry[1] maps index x + skip, entry[2] maps index x + 2 * skip, and so on.
* The larger the file, the greater the skip factor. The skip factor is
* limited to the size of the metadata cache (SQUASHFS_CACHED_BLKS) to ensure
* the number of metadata blocks that need to be read fits into the cache.
* If the skip factor is limited in this way then the file will use multiple
* slots.
*/
static inline int calculate_skip(int blocks)
{
int skip = blocks / ((SQUASHFS_META_ENTRIES + 1)
* SQUASHFS_META_INDEXES);
return min(SQUASHFS_CACHED_BLKS - 1, skip + 1);
}
/*
* Search and grow the index cache for the specified inode, returning the
* on-disk locations of the datablock and block list metadata block
* <index_block, index_offset> for index (scaled to nearest cache index).
*/
static int fill_meta_index(struct inode *inode, int index,
u64 *index_block, int *index_offset, u64 *data_block)
{
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
int skip = calculate_skip(i_size_read(inode) >> msblk->block_log);
int offset = 0;
struct meta_index *meta;
struct meta_entry *meta_entry;
u64 cur_index_block = squashfs_i(inode)->block_list_start;
int cur_offset = squashfs_i(inode)->offset;
u64 cur_data_block = squashfs_i(inode)->start;
int err, i;
/*
* Scale index to cache index (cache slot entry)
*/
index /= SQUASHFS_META_INDEXES * skip;
while (offset < index) {
meta = locate_meta_index(inode, offset + 1, index);
if (meta == NULL) {
meta = empty_meta_index(inode, offset + 1, skip);
if (meta == NULL)
goto all_done;
} else {
offset = index < meta->offset + meta->entries ? index :
meta->offset + meta->entries - 1;
meta_entry = &meta->meta_entry[offset - meta->offset];
cur_index_block = meta_entry->index_block +
msblk->inode_table;
cur_offset = meta_entry->offset;
cur_data_block = meta_entry->data_block;
TRACE("get_meta_index: offset %d, meta->offset %d, "
"meta->entries %d\n", offset, meta->offset,
meta->entries);
TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
" data_block 0x%llx\n", cur_index_block,
cur_offset, cur_data_block);
}
/*
* If necessary grow cache slot by reading block list. Cache
* slot is extended up to index or to the end of the slot, in
* which case further slots will be used.
*/
for (i = meta->offset + meta->entries; i <= index &&
i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
int blocks = skip * SQUASHFS_META_INDEXES;
long long res = read_indexes(inode->i_sb, blocks,
&cur_index_block, &cur_offset);
if (res < 0) {
if (meta->entries == 0)
/*
* Don't leave an empty slot on read
* error allocated to this inode...
*/
meta->inode_number = 0;
err = res;
goto failed;
}
cur_data_block += res;
meta_entry = &meta->meta_entry[i - meta->offset];
meta_entry->index_block = cur_index_block -
msblk->inode_table;
meta_entry->offset = cur_offset;
meta_entry->data_block = cur_data_block;
meta->entries++;
offset++;
}
TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
meta->offset, meta->entries);
release_meta_index(inode, meta);
}
all_done:
*index_block = cur_index_block;
*index_offset = cur_offset;
*data_block = cur_data_block;
/*
* Scale cache index (cache slot entry) to index
*/
return offset * SQUASHFS_META_INDEXES * skip;
failed:
release_meta_index(inode, meta);
return err;
}
/*
* Get the on-disk location and compressed size of the datablock
* specified by index. Fill_meta_index() does most of the work.
*/
static int read_blocklist(struct inode *inode, int index, u64 *block)
{
u64 start;
long long blks;
int offset;
__le32 size;
int res = fill_meta_index(inode, index, &start, &offset, block);
TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset"
" 0x%x, block 0x%llx\n", res, index, start, offset,
*block);
if (res < 0)
return res;
/*
* res contains the index of the mapping returned by fill_meta_index(),
* this will likely be less than the desired index (because the
* meta_index cache works at a higher granularity). Read any
* extra block indexes needed.
*/
if (res < index) {
blks = read_indexes(inode->i_sb, index - res, &start, &offset);
if (blks < 0)
return (int) blks;
*block += blks;
}
/*
* Read length of block specified by index.
*/
res = squashfs_read_metadata(inode->i_sb, &size, &start, &offset,
sizeof(size));
if (res < 0)
return res;
return le32_to_cpu(size);
}
static int squashfs_readpage(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
int bytes, i, offset = 0, sparse = 0;
struct squashfs_cache_entry *buffer = NULL;
void *pageaddr;
int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
int start_index = page->index & ~mask;
int end_index = start_index | mask;
int file_end = i_size_read(inode) >> msblk->block_log;
TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
page->index, squashfs_i(inode)->start);
if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
PAGE_CACHE_SHIFT))
goto out;
if (index < file_end || squashfs_i(inode)->fragment_block ==
SQUASHFS_INVALID_BLK) {
/*
* Reading a datablock from disk. Need to read block list
* to get location and block size.
*/
u64 block = 0;
int bsize = read_blocklist(inode, index, &block);
if (bsize < 0)
goto error_out;
if (bsize == 0) { /* hole */
bytes = index == file_end ?
(i_size_read(inode) & (msblk->block_size - 1)) :
msblk->block_size;
sparse = 1;
} else {
/*
* Read and decompress datablock.
*/
buffer = squashfs_get_datablock(inode->i_sb,
block, bsize);
if (buffer->error) {
ERROR("Unable to read page, block %llx, size %x"
"\n", block, bsize);
squashfs_cache_put(buffer);
goto error_out;
}
bytes = buffer->length;
}
} else {
/*
* Datablock is stored inside a fragment (tail-end packed
* block).
*/
buffer = squashfs_get_fragment(inode->i_sb,
squashfs_i(inode)->fragment_block,
squashfs_i(inode)->fragment_size);
if (buffer->error) {
ERROR("Unable to read page, block %llx, size %x\n",
squashfs_i(inode)->fragment_block,
squashfs_i(inode)->fragment_size);
squashfs_cache_put(buffer);
goto error_out;
}
bytes = i_size_read(inode) & (msblk->block_size - 1);
offset = squashfs_i(inode)->fragment_offset;
}
/*
* Loop copying datablock into pages. As the datablock likely covers
* many PAGE_CACHE_SIZE pages (default block size is 128 KiB) explicitly
* grab the pages from the page cache, except for the page that we've
* been called to fill.
*/
for (i = start_index; i <= end_index && bytes > 0; i++,
bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
struct page *push_page;
int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
push_page = (i == page->index) ? page :
grab_cache_page_nowait(page->mapping, i);
if (!push_page)
continue;
if (PageUptodate(push_page))
goto skip_page;
pageaddr = kmap_atomic(push_page, KM_USER0);
squashfs_copy_data(pageaddr, buffer, offset, avail);
memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
kunmap_atomic(pageaddr, KM_USER0);
flush_dcache_page(push_page);
SetPageUptodate(push_page);
skip_page:
unlock_page(push_page);
if (i != page->index)
page_cache_release(push_page);
}
if (!sparse)
squashfs_cache_put(buffer);
return 0;
error_out:
SetPageError(page);
out:
pageaddr = kmap_atomic(page, KM_USER0);
memset(pageaddr, 0, PAGE_CACHE_SIZE);
kunmap_atomic(pageaddr, KM_USER0);
flush_dcache_page(page);
if (!PageError(page))
SetPageUptodate(page);
unlock_page(page);
return 0;
}
const struct address_space_operations squashfs_aops = {
.readpage = squashfs_readpage
};

View file

@ -0,0 +1,98 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* fragment.c
*/
/*
* This file implements code to handle compressed fragments (tail-end packed
* datablocks).
*
* Regular files contain a fragment index which is mapped to a fragment
* location on disk and compressed size using a fragment lookup table.
* Like everything in Squashfs this fragment lookup table is itself stored
* compressed into metadata blocks. A second index table is used to locate
* these. This second index table for speed of access (and because it
* is small) is read at mount time and cached in memory.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Look-up fragment using the fragment index table. Return the on disk
* location of the fragment and its compressed size
*/
int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment,
u64 *fragment_block)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int block = SQUASHFS_FRAGMENT_INDEX(fragment);
int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
u64 start_block = le64_to_cpu(msblk->fragment_index[block]);
struct squashfs_fragment_entry fragment_entry;
int size;
size = squashfs_read_metadata(sb, &fragment_entry, &start_block,
&offset, sizeof(fragment_entry));
if (size < 0)
return size;
*fragment_block = le64_to_cpu(fragment_entry.start_block);
size = le32_to_cpu(fragment_entry.size);
return size;
}
/*
* Read the uncompressed fragment lookup table indexes off disk into memory
*/
__le64 *squashfs_read_fragment_index_table(struct super_block *sb,
u64 fragment_table_start, unsigned int fragments)
{
unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments);
__le64 *fragment_index;
int err;
/* Allocate fragment lookup table indexes */
fragment_index = kmalloc(length, GFP_KERNEL);
if (fragment_index == NULL) {
ERROR("Failed to allocate fragment index table\n");
return ERR_PTR(-ENOMEM);
}
err = squashfs_read_table(sb, fragment_index, fragment_table_start,
length);
if (err < 0) {
ERROR("unable to read fragment index table\n");
kfree(fragment_index);
return ERR_PTR(err);
}
return fragment_index;
}

View file

@ -0,0 +1,94 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* id.c
*/
/*
* This file implements code to handle uids and gids.
*
* For space efficiency regular files store uid and gid indexes, which are
* converted to 32-bit uids/gids using an id look up table. This table is
* stored compressed into metadata blocks. A second index table is used to
* locate these. This second index table for speed of access (and because it
* is small) is read at mount time and cached in memory.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Map uid/gid index into real 32-bit uid/gid using the id look up table
*/
int squashfs_get_id(struct super_block *sb, unsigned int index,
unsigned int *id)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int block = SQUASHFS_ID_BLOCK(index);
int offset = SQUASHFS_ID_BLOCK_OFFSET(index);
u64 start_block = le64_to_cpu(msblk->id_table[block]);
__le32 disk_id;
int err;
err = squashfs_read_metadata(sb, &disk_id, &start_block, &offset,
sizeof(disk_id));
if (err < 0)
return err;
*id = le32_to_cpu(disk_id);
return 0;
}
/*
* Read uncompressed id lookup table indexes from disk into memory
*/
__le64 *squashfs_read_id_index_table(struct super_block *sb,
u64 id_table_start, unsigned short no_ids)
{
unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids);
__le64 *id_table;
int err;
TRACE("In read_id_index_table, length %d\n", length);
/* Allocate id lookup table indexes */
id_table = kmalloc(length, GFP_KERNEL);
if (id_table == NULL) {
ERROR("Failed to allocate id index table\n");
return ERR_PTR(-ENOMEM);
}
err = squashfs_read_table(sb, id_table, id_table_start, length);
if (err < 0) {
ERROR("unable to read id index table\n");
kfree(id_table);
return ERR_PTR(err);
}
return id_table;
}

View file

@ -0,0 +1,346 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* inode.c
*/
/*
* This file implements code to create and read inodes from disk.
*
* Inodes in Squashfs are identified by a 48-bit inode which encodes the
* location of the compressed metadata block containing the inode, and the byte
* offset into that block where the inode is placed (<block, offset>).
*
* To maximise compression there are different inodes for each file type
* (regular file, directory, device, etc.), the inode contents and length
* varying with the type.
*
* To further maximise compression, two types of regular file inode and
* directory inode are defined: inodes optimised for frequently occurring
* regular files and directories, and extended types where extra
* information has to be stored.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Initialise VFS inode with the base inode information common to all
* Squashfs inode types. Sqsh_ino contains the unswapped base inode
* off disk.
*/
static int squashfs_new_inode(struct super_block *sb, struct inode *inode,
struct squashfs_base_inode *sqsh_ino)
{
int err;
err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->uid), &inode->i_uid);
if (err)
return err;
err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->guid), &inode->i_gid);
if (err)
return err;
inode->i_ino = le32_to_cpu(sqsh_ino->inode_number);
inode->i_mtime.tv_sec = le32_to_cpu(sqsh_ino->mtime);
inode->i_atime.tv_sec = inode->i_mtime.tv_sec;
inode->i_ctime.tv_sec = inode->i_mtime.tv_sec;
inode->i_mode = le16_to_cpu(sqsh_ino->mode);
inode->i_size = 0;
return err;
}
struct inode *squashfs_iget(struct super_block *sb, long long ino,
unsigned int ino_number)
{
struct inode *inode = iget_locked(sb, ino_number);
int err;
TRACE("Entered squashfs_iget\n");
if (!inode)
return ERR_PTR(-ENOMEM);
if (!(inode->i_state & I_NEW))
return inode;
err = squashfs_read_inode(inode, ino);
if (err) {
iget_failed(inode);
return ERR_PTR(err);
}
unlock_new_inode(inode);
return inode;
}
/*
* Initialise VFS inode by reading inode from inode table (compressed
* metadata). The format and amount of data read depends on type.
*/
int squashfs_read_inode(struct inode *inode, long long ino)
{
struct super_block *sb = inode->i_sb;
struct squashfs_sb_info *msblk = sb->s_fs_info;
u64 block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
int err, type, offset = SQUASHFS_INODE_OFFSET(ino);
union squashfs_inode squashfs_ino;
struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
TRACE("Entered squashfs_read_inode\n");
/*
* Read inode base common to all inode types.
*/
err = squashfs_read_metadata(sb, sqshb_ino, &block,
&offset, sizeof(*sqshb_ino));
if (err < 0)
goto failed_read;
err = squashfs_new_inode(sb, inode, sqshb_ino);
if (err)
goto failed_read;
block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
offset = SQUASHFS_INODE_OFFSET(ino);
type = le16_to_cpu(sqshb_ino->inode_type);
switch (type) {
case SQUASHFS_REG_TYPE: {
unsigned int frag_offset, frag_size, frag;
u64 frag_blk;
struct squashfs_reg_inode *sqsh_ino = &squashfs_ino.reg;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
frag = le32_to_cpu(sqsh_ino->fragment);
if (frag != SQUASHFS_INVALID_FRAG) {
frag_offset = le32_to_cpu(sqsh_ino->offset);
frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
if (frag_size < 0) {
err = frag_size;
goto failed_read;
}
} else {
frag_blk = SQUASHFS_INVALID_BLK;
frag_size = 0;
frag_offset = 0;
}
inode->i_nlink = 1;
inode->i_size = le32_to_cpu(sqsh_ino->file_size);
inode->i_fop = &generic_ro_fops;
inode->i_mode |= S_IFREG;
inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
squashfs_i(inode)->fragment_block = frag_blk;
squashfs_i(inode)->fragment_size = frag_size;
squashfs_i(inode)->fragment_offset = frag_offset;
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
squashfs_i(inode)->block_list_start = block;
squashfs_i(inode)->offset = offset;
inode->i_data.a_ops = &squashfs_aops;
TRACE("File inode %x:%x, start_block %llx, block_list_start "
"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
offset, squashfs_i(inode)->start, block, offset);
break;
}
case SQUASHFS_LREG_TYPE: {
unsigned int frag_offset, frag_size, frag;
u64 frag_blk;
struct squashfs_lreg_inode *sqsh_ino = &squashfs_ino.lreg;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
frag = le32_to_cpu(sqsh_ino->fragment);
if (frag != SQUASHFS_INVALID_FRAG) {
frag_offset = le32_to_cpu(sqsh_ino->offset);
frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
if (frag_size < 0) {
err = frag_size;
goto failed_read;
}
} else {
frag_blk = SQUASHFS_INVALID_BLK;
frag_size = 0;
frag_offset = 0;
}
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
inode->i_size = le64_to_cpu(sqsh_ino->file_size);
inode->i_fop = &generic_ro_fops;
inode->i_mode |= S_IFREG;
inode->i_blocks = ((inode->i_size -
le64_to_cpu(sqsh_ino->sparse) - 1) >> 9) + 1;
squashfs_i(inode)->fragment_block = frag_blk;
squashfs_i(inode)->fragment_size = frag_size;
squashfs_i(inode)->fragment_offset = frag_offset;
squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block);
squashfs_i(inode)->block_list_start = block;
squashfs_i(inode)->offset = offset;
inode->i_data.a_ops = &squashfs_aops;
TRACE("File inode %x:%x, start_block %llx, block_list_start "
"%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
offset, squashfs_i(inode)->start, block, offset);
break;
}
case SQUASHFS_DIR_TYPE: {
struct squashfs_dir_inode *sqsh_ino = &squashfs_ino.dir;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
inode->i_size = le16_to_cpu(sqsh_ino->file_size);
inode->i_op = &squashfs_dir_inode_ops;
inode->i_fop = &squashfs_dir_ops;
inode->i_mode |= S_IFDIR;
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
squashfs_i(inode)->dir_idx_cnt = 0;
squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
TRACE("Directory inode %x:%x, start_block %llx, offset %x\n",
SQUASHFS_INODE_BLK(ino), offset,
squashfs_i(inode)->start,
le16_to_cpu(sqsh_ino->offset));
break;
}
case SQUASHFS_LDIR_TYPE: {
struct squashfs_ldir_inode *sqsh_ino = &squashfs_ino.ldir;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
inode->i_size = le32_to_cpu(sqsh_ino->file_size);
inode->i_op = &squashfs_dir_inode_ops;
inode->i_fop = &squashfs_dir_ops;
inode->i_mode |= S_IFDIR;
squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
squashfs_i(inode)->dir_idx_start = block;
squashfs_i(inode)->dir_idx_offset = offset;
squashfs_i(inode)->dir_idx_cnt = le16_to_cpu(sqsh_ino->i_count);
squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
TRACE("Long directory inode %x:%x, start_block %llx, offset "
"%x\n", SQUASHFS_INODE_BLK(ino), offset,
squashfs_i(inode)->start,
le16_to_cpu(sqsh_ino->offset));
break;
}
case SQUASHFS_SYMLINK_TYPE:
case SQUASHFS_LSYMLINK_TYPE: {
struct squashfs_symlink_inode *sqsh_ino = &squashfs_ino.symlink;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
inode->i_op = &page_symlink_inode_operations;
inode->i_data.a_ops = &squashfs_symlink_aops;
inode->i_mode |= S_IFLNK;
squashfs_i(inode)->start = block;
squashfs_i(inode)->offset = offset;
TRACE("Symbolic link inode %x:%x, start_block %llx, offset "
"%x\n", SQUASHFS_INODE_BLK(ino), offset,
block, offset);
break;
}
case SQUASHFS_BLKDEV_TYPE:
case SQUASHFS_CHRDEV_TYPE:
case SQUASHFS_LBLKDEV_TYPE:
case SQUASHFS_LCHRDEV_TYPE: {
struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev;
unsigned int rdev;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
if (type == SQUASHFS_CHRDEV_TYPE)
inode->i_mode |= S_IFCHR;
else
inode->i_mode |= S_IFBLK;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
rdev = le32_to_cpu(sqsh_ino->rdev);
init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));
TRACE("Device inode %x:%x, rdev %x\n",
SQUASHFS_INODE_BLK(ino), offset, rdev);
break;
}
case SQUASHFS_FIFO_TYPE:
case SQUASHFS_SOCKET_TYPE:
case SQUASHFS_LFIFO_TYPE:
case SQUASHFS_LSOCKET_TYPE: {
struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;
err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
sizeof(*sqsh_ino));
if (err < 0)
goto failed_read;
if (type == SQUASHFS_FIFO_TYPE)
inode->i_mode |= S_IFIFO;
else
inode->i_mode |= S_IFSOCK;
inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
init_special_inode(inode, inode->i_mode, 0);
break;
}
default:
ERROR("Unknown inode type %d in squashfs_iget!\n", type);
return -EINVAL;
}
return 0;
failed_read:
ERROR("Unable to read inode 0x%llx\n", ino);
return err;
}

View file

@ -0,0 +1,242 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* namei.c
*/
/*
* This file implements code to do filename lookup in directories.
*
* Like inodes, directories are packed into compressed metadata blocks, stored
* in a directory table. Directories are accessed using the start address of
* the metablock containing the directory and the offset into the
* decompressed block (<block, offset>).
*
* Directories are organised in a slightly complex way, and are not simply
* a list of file names. The organisation takes advantage of the
* fact that (in most cases) the inodes of the files will be in the same
* compressed metadata block, and therefore, can share the start block.
* Directories are therefore organised in a two level list, a directory
* header containing the shared start block value, and a sequence of directory
* entries, each of which share the shared start block. A new directory header
* is written once/if the inode start block changes. The directory
* header/directory entry list is repeated as many times as necessary.
*
* Directories are sorted, and can contain a directory index to speed up
* file lookup. Directory indexes store one entry per metablock, each entry
* storing the index/filename mapping to the first directory header
* in each metadata block. Directories are sorted in alphabetical order,
* and at lookup the index is scanned linearly looking for the first filename
* alphabetically larger than the filename being looked up. At this point the
* location of the metadata block the filename is in has been found.
* The general idea of the index is ensure only one metadata block needs to be
* decompressed to do a lookup irrespective of the length of the directory.
* This scheme has the advantage that it doesn't require extra memory overhead
* and doesn't require much extra storage on disk.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/dcache.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
/*
* Lookup name in the directory index, returning the location of the metadata
* block containing it, and the directory index this represents.
*
* If we get an error reading the index then return the part of the index
* (if any) we have managed to read - the index isn't essential, just
* quicker.
*/
static int get_dir_index_using_name(struct super_block *sb,
u64 *next_block, int *next_offset, u64 index_start,
int index_offset, int i_count, const char *name,
int len)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
int i, size, length = 0, err;
struct squashfs_dir_index *index;
char *str;
TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
index = kmalloc(sizeof(*index) + SQUASHFS_NAME_LEN * 2 + 2, GFP_KERNEL);
if (index == NULL) {
ERROR("Failed to allocate squashfs_dir_index\n");
goto out;
}
str = &index->name[SQUASHFS_NAME_LEN + 1];
strncpy(str, name, len);
str[len] = '\0';
for (i = 0; i < i_count; i++) {
err = squashfs_read_metadata(sb, index, &index_start,
&index_offset, sizeof(*index));
if (err < 0)
break;
size = le32_to_cpu(index->size) + 1;
err = squashfs_read_metadata(sb, index->name, &index_start,
&index_offset, size);
if (err < 0)
break;
index->name[size] = '\0';
if (strcmp(index->name, str) > 0)
break;
length = le32_to_cpu(index->index);
*next_block = le32_to_cpu(index->start_block) +
msblk->directory_table;
}
*next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
kfree(index);
out:
/*
* Return index (f_pos) of the looked up metadata block. Translate
* from internal f_pos to external f_pos which is offset by 3 because
* we invent "." and ".." entries which are not actually stored in the
* directory.
*/
return length + 3;
}
static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
const unsigned char *name = dentry->d_name.name;
int len = dentry->d_name.len;
struct inode *inode = NULL;
struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info;
struct squashfs_dir_header dirh;
struct squashfs_dir_entry *dire;
u64 block = squashfs_i(dir)->start + msblk->directory_table;
int offset = squashfs_i(dir)->offset;
int err, length = 0, dir_count, size;
TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset);
dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
if (dire == NULL) {
ERROR("Failed to allocate squashfs_dir_entry\n");
return ERR_PTR(-ENOMEM);
}
if (len > SQUASHFS_NAME_LEN) {
err = -ENAMETOOLONG;
goto failed;
}
length = get_dir_index_using_name(dir->i_sb, &block, &offset,
squashfs_i(dir)->dir_idx_start,
squashfs_i(dir)->dir_idx_offset,
squashfs_i(dir)->dir_idx_cnt, name, len);
while (length < i_size_read(dir)) {
/*
* Read directory header.
*/
err = squashfs_read_metadata(dir->i_sb, &dirh, &block,
&offset, sizeof(dirh));
if (err < 0)
goto read_failure;
length += sizeof(dirh);
dir_count = le32_to_cpu(dirh.count) + 1;
while (dir_count--) {
/*
* Read directory entry.
*/
err = squashfs_read_metadata(dir->i_sb, dire, &block,
&offset, sizeof(*dire));
if (err < 0)
goto read_failure;
size = le16_to_cpu(dire->size) + 1;
err = squashfs_read_metadata(dir->i_sb, dire->name,
&block, &offset, size);
if (err < 0)
goto read_failure;
length += sizeof(*dire) + size;
if (name[0] < dire->name[0])
goto exit_lookup;
if (len == size && !strncmp(name, dire->name, len)) {
unsigned int blk, off, ino_num;
long long ino;
blk = le32_to_cpu(dirh.start_block);
off = le16_to_cpu(dire->offset);
ino_num = le32_to_cpu(dirh.inode_number) +
(short) le16_to_cpu(dire->inode_number);
ino = SQUASHFS_MKINODE(blk, off);
TRACE("calling squashfs_iget for directory "
"entry %s, inode %x:%x, %d\n", name,
blk, off, ino_num);
inode = squashfs_iget(dir->i_sb, ino, ino_num);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
goto failed;
}
goto exit_lookup;
}
}
}
exit_lookup:
kfree(dire);
if (inode)
return d_splice_alias(inode, dentry);
d_add(dentry, inode);
return ERR_PTR(0);
read_failure:
ERROR("Unable to read directory block [%llx:%x]\n",
squashfs_i(dir)->start + msblk->directory_table,
squashfs_i(dir)->offset);
failed:
kfree(dire);
return ERR_PTR(err);
}
const struct inode_operations squashfs_dir_inode_ops = {
.lookup = squashfs_lookup
};

View file

@ -0,0 +1,90 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs.h
*/
#define TRACE(s, args...) pr_debug("SQUASHFS: "s, ## args)
#define ERROR(s, args...) pr_err("SQUASHFS error: "s, ## args)
#define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args)
static inline struct squashfs_inode_info *squashfs_i(struct inode *inode)
{
return list_entry(inode, struct squashfs_inode_info, vfs_inode);
}
/* block.c */
extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *,
int);
/* cache.c */
extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
extern void squashfs_cache_delete(struct squashfs_cache *);
extern struct squashfs_cache_entry *squashfs_cache_get(struct super_block *,
struct squashfs_cache *, u64, int);
extern void squashfs_cache_put(struct squashfs_cache_entry *);
extern int squashfs_copy_data(void *, struct squashfs_cache_entry *, int, int);
extern int squashfs_read_metadata(struct super_block *, void *, u64 *,
int *, int);
extern struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *,
u64, int);
extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *,
u64, int);
extern int squashfs_read_table(struct super_block *, void *, u64, int);
/* export.c */
extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,
unsigned int);
/* fragment.c */
extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *);
extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
u64, unsigned int);
/* id.c */
extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
extern __le64 *squashfs_read_id_index_table(struct super_block *, u64,
unsigned short);
/* inode.c */
extern struct inode *squashfs_iget(struct super_block *, long long,
unsigned int);
extern int squashfs_read_inode(struct inode *, long long);
/*
* Inodes and files operations
*/
/* dir.c */
extern const struct file_operations squashfs_dir_ops;
/* export.c */
extern const struct export_operations squashfs_export_ops;
/* file.c */
extern const struct address_space_operations squashfs_aops;
/* namei.c */
extern const struct inode_operations squashfs_dir_inode_ops;
/* symlink.c */
extern const struct address_space_operations squashfs_symlink_aops;

View file

@ -0,0 +1,381 @@
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs.h
*/
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_MAJOR 4
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#define SQUASHFS_START 0
/* size of metadata (inode and directory) blocks */
#define SQUASHFS_METADATA_SIZE 8192
#define SQUASHFS_METADATA_LOG 13
/* default size of data blocks */
#define SQUASHFS_FILE_SIZE 131072
#define SQUASHFS_FILE_LOG 17
#define SQUASHFS_FILE_MAX_SIZE 1048576
#define SQUASHFS_FILE_MAX_LOG 20
/* Max number of uids and gids */
#define SQUASHFS_IDS 65536
/* Max length of filename (not 255) */
#define SQUASHFS_NAME_LEN 256
#define SQUASHFS_INVALID_FRAG (0xffffffffU)
#define SQUASHFS_INVALID_BLK (-1LL)
/* Filesystem flags */
#define SQUASHFS_NOI 0
#define SQUASHFS_NOD 1
#define SQUASHFS_NOF 3
#define SQUASHFS_NO_FRAG 4
#define SQUASHFS_ALWAYS_FRAG 5
#define SQUASHFS_DUPLICATE 6
#define SQUASHFS_EXPORT 7
#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
SQUASHFS_EXPORT)
/* Max number of types and file types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_REG_TYPE 2
#define SQUASHFS_SYMLINK_TYPE 3
#define SQUASHFS_BLKDEV_TYPE 4
#define SQUASHFS_CHRDEV_TYPE 5
#define SQUASHFS_FIFO_TYPE 6
#define SQUASHFS_SOCKET_TYPE 7
#define SQUASHFS_LDIR_TYPE 8
#define SQUASHFS_LREG_TYPE 9
#define SQUASHFS_LSYMLINK_TYPE 10
#define SQUASHFS_LBLKDEV_TYPE 11
#define SQUASHFS_LCHRDEV_TYPE 12
#define SQUASHFS_LFIFO_TYPE 13
#define SQUASHFS_LSOCKET_TYPE 14
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(A) ((unsigned int) ((A) >> 16))
#define SQUASHFS_INODE_OFFSET(A) ((unsigned int) ((A) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((long long)(((long long) (A)\
<< 16) + (B)))
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(A) ((A) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) \
((A) * sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(u64))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(u64))
#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(u64))
/* uid/gid lookup table defines */
#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int))
#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(u64))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE (1LL << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 127
#define SQUASHFS_META_SLOTS 8
struct meta_entry {
u64 data_block;
unsigned int index_block;
unsigned short offset;
unsigned short pad;
};
struct meta_index {
unsigned int inode_number;
unsigned int offset;
unsigned short entries;
unsigned short skip;
unsigned short locked;
unsigned short pad;
struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
};
/*
* definitions for structures on disk
*/
#define ZLIB_COMPRESSION 1
struct squashfs_super_block {
__le32 s_magic;
__le32 inodes;
__le32 mkfs_time;
__le32 block_size;
__le32 fragments;
__le16 compression;
__le16 block_log;
__le16 flags;
__le16 no_ids;
__le16 s_major;
__le16 s_minor;
__le64 root_inode;
__le64 bytes_used;
__le64 id_table_start;
__le64 xattr_table_start;
__le64 inode_table_start;
__le64 directory_table_start;
__le64 fragment_table_start;
__le64 lookup_table_start;
};
struct squashfs_dir_index {
__le32 index;
__le32 start_block;
__le32 size;
unsigned char name[0];
};
struct squashfs_base_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
};
struct squashfs_ipc_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
};
struct squashfs_dev_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 rdev;
};
struct squashfs_symlink_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 symlink_size;
char symlink[0];
};
struct squashfs_reg_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 start_block;
__le32 fragment;
__le32 offset;
__le32 file_size;
__le16 block_list[0];
};
struct squashfs_lreg_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le64 start_block;
__le64 file_size;
__le64 sparse;
__le32 nlink;
__le32 fragment;
__le32 offset;
__le32 xattr;
__le16 block_list[0];
};
struct squashfs_dir_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 start_block;
__le32 nlink;
__le16 file_size;
__le16 offset;
__le32 parent_inode;
};
struct squashfs_ldir_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 file_size;
__le32 start_block;
__le32 parent_inode;
__le16 i_count;
__le16 offset;
__le32 xattr;
struct squashfs_dir_index index[0];
};
union squashfs_inode {
struct squashfs_base_inode base;
struct squashfs_dev_inode dev;
struct squashfs_symlink_inode symlink;
struct squashfs_reg_inode reg;
struct squashfs_lreg_inode lreg;
struct squashfs_dir_inode dir;
struct squashfs_ldir_inode ldir;
struct squashfs_ipc_inode ipc;
};
struct squashfs_dir_entry {
__le16 offset;
__le16 inode_number;
__le16 type;
__le16 size;
char name[0];
};
struct squashfs_dir_header {
__le32 count;
__le32 start_block;
__le32 inode_number;
};
struct squashfs_fragment_entry {
__le64 start_block;
__le32 size;
unsigned int unused;
};
#endif

View file

@ -0,0 +1,45 @@
#ifndef SQUASHFS_FS_I
#define SQUASHFS_FS_I
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_i.h
*/
struct squashfs_inode_info {
u64 start;
int offset;
union {
struct {
u64 fragment_block;
int fragment_size;
int fragment_offset;
u64 block_list_start;
};
struct {
u64 dir_idx_start;
int dir_idx_offset;
int dir_idx_cnt;
int parent;
};
};
struct inode vfs_inode;
};
#endif

View file

@ -0,0 +1,76 @@
#ifndef SQUASHFS_FS_SB
#define SQUASHFS_FS_SB
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_sb.h
*/
#include "squashfs_fs.h"
struct squashfs_cache {
char *name;
int entries;
int next_blk;
int num_waiters;
int unused;
int block_size;
int pages;
spinlock_t lock;
wait_queue_head_t wait_queue;
struct squashfs_cache_entry *entry;
};
struct squashfs_cache_entry {
u64 block;
int length;
int refcount;
u64 next_index;
int pending;
int error;
int num_waiters;
wait_queue_head_t wait_queue;
struct squashfs_cache *cache;
void **data;
};
struct squashfs_sb_info {
int devblksize;
int devblksize_log2;
struct squashfs_cache *block_cache;
struct squashfs_cache *fragment_cache;
struct squashfs_cache *read_page;
int next_meta_index;
__le64 *id_table;
__le64 *fragment_index;
unsigned int *fragment_index_2;
struct mutex read_data_mutex;
struct mutex meta_index_mutex;
struct meta_index *meta_index;
z_stream stream;
__le64 *inode_lookup_table;
u64 inode_table;
u64 directory_table;
unsigned int block_size;
unsigned short block_log;
long long bytes_used;
unsigned int inodes;
};
#endif

View file

@ -0,0 +1,440 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* super.c
*/
/*
* This file implements code to read the superblock, read and initialise
* in-memory structures at mount time, and all the VFS glue code to register
* the filesystem.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
static struct file_system_type squashfs_fs_type;
static struct super_operations squashfs_super_ops;
static int supported_squashfs_filesystem(short major, short minor, short comp)
{
if (major < SQUASHFS_MAJOR) {
ERROR("Major/Minor mismatch, older Squashfs %d.%d "
"filesystems are unsupported\n", major, minor);
return -EINVAL;
} else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) {
ERROR("Major/Minor mismatch, trying to mount newer "
"%d.%d filesystem\n", major, minor);
ERROR("Please update your kernel\n");
return -EINVAL;
}
if (comp != ZLIB_COMPRESSION)
return -EINVAL;
return 0;
}
static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct squashfs_sb_info *msblk;
struct squashfs_super_block *sblk = NULL;
char b[BDEVNAME_SIZE];
struct inode *root;
long long root_inode;
unsigned short flags;
unsigned int fragments;
u64 lookup_table_start;
int err;
TRACE("Entered squashfs_fill_superblock\n");
sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL);
if (sb->s_fs_info == NULL) {
ERROR("Failed to allocate squashfs_sb_info\n");
return -ENOMEM;
}
msblk = sb->s_fs_info;
msblk->stream.workspace = kmalloc(zlib_inflate_workspacesize(),
GFP_KERNEL);
if (msblk->stream.workspace == NULL) {
ERROR("Failed to allocate zlib workspace\n");
goto failure;
}
sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
if (sblk == NULL) {
ERROR("Failed to allocate squashfs_super_block\n");
goto failure;
}
msblk->devblksize = sb_min_blocksize(sb, BLOCK_SIZE);
msblk->devblksize_log2 = ffz(~msblk->devblksize);
mutex_init(&msblk->read_data_mutex);
mutex_init(&msblk->meta_index_mutex);
/*
* msblk->bytes_used is checked in squashfs_read_table to ensure reads
* are not beyond filesystem end. But as we're using
* squashfs_read_table here to read the superblock (including the value
* of bytes_used) we need to set it to an initial sensible dummy value
*/
msblk->bytes_used = sizeof(*sblk);
err = squashfs_read_table(sb, sblk, SQUASHFS_START, sizeof(*sblk));
if (err < 0) {
ERROR("unable to read squashfs_super_block\n");
goto failed_mount;
}
/* Check it is a SQUASHFS superblock */
sb->s_magic = le32_to_cpu(sblk->s_magic);
if (sb->s_magic != SQUASHFS_MAGIC) {
if (!silent)
ERROR("Can't find a SQUASHFS superblock on %s\n",
bdevname(sb->s_bdev, b));
err = -EINVAL;
goto failed_mount;
}
/* Check the MAJOR & MINOR versions and compression type */
err = supported_squashfs_filesystem(le16_to_cpu(sblk->s_major),
le16_to_cpu(sblk->s_minor),
le16_to_cpu(sblk->compression));
if (err < 0)
goto failed_mount;
err = -EINVAL;
/*
* Check if there's xattrs in the filesystem. These are not
* supported in this version, so warn that they will be ignored.
*/
if (le64_to_cpu(sblk->xattr_table_start) != SQUASHFS_INVALID_BLK)
ERROR("Xattrs in filesystem, these will be ignored\n");
/* Check the filesystem does not extend beyond the end of the
block device */
msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
if (msblk->bytes_used < 0 || msblk->bytes_used >
i_size_read(sb->s_bdev->bd_inode))
goto failed_mount;
/* Check block size for sanity */
msblk->block_size = le32_to_cpu(sblk->block_size);
if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE)
goto failed_mount;
msblk->block_log = le16_to_cpu(sblk->block_log);
if (msblk->block_log > SQUASHFS_FILE_MAX_LOG)
goto failed_mount;
/* Check the root inode for sanity */
root_inode = le64_to_cpu(sblk->root_inode);
if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE)
goto failed_mount;
msblk->inode_table = le64_to_cpu(sblk->inode_table_start);
msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
msblk->inodes = le32_to_cpu(sblk->inodes);
flags = le16_to_cpu(sblk->flags);
TRACE("Found valid superblock on %s\n", bdevname(sb->s_bdev, b));
TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
? "un" : "");
TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags)
? "un" : "");
TRACE("Filesystem size %lld bytes\n", msblk->bytes_used);
TRACE("Block size %d\n", msblk->block_size);
TRACE("Number of inodes %d\n", msblk->inodes);
TRACE("Number of fragments %d\n", le32_to_cpu(sblk->fragments));
TRACE("Number of ids %d\n", le16_to_cpu(sblk->no_ids));
TRACE("sblk->inode_table_start %llx\n", msblk->inode_table);
TRACE("sblk->directory_table_start %llx\n", msblk->directory_table);
TRACE("sblk->fragment_table_start %llx\n",
(u64) le64_to_cpu(sblk->fragment_table_start));
TRACE("sblk->id_table_start %llx\n",
(u64) le64_to_cpu(sblk->id_table_start));
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_flags |= MS_RDONLY;
sb->s_op = &squashfs_super_ops;
err = -ENOMEM;
msblk->block_cache = squashfs_cache_init("metadata",
SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
if (msblk->block_cache == NULL)
goto failed_mount;
/* Allocate read_page block */
msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
if (msblk->read_page == NULL) {
ERROR("Failed to allocate read_page block\n");
goto failed_mount;
}
/* Allocate and read id index table */
msblk->id_table = squashfs_read_id_index_table(sb,
le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids));
if (IS_ERR(msblk->id_table)) {
err = PTR_ERR(msblk->id_table);
msblk->id_table = NULL;
goto failed_mount;
}
fragments = le32_to_cpu(sblk->fragments);
if (fragments == 0)
goto allocate_lookup_table;
msblk->fragment_cache = squashfs_cache_init("fragment",
SQUASHFS_CACHED_FRAGMENTS, msblk->block_size);
if (msblk->fragment_cache == NULL) {
err = -ENOMEM;
goto failed_mount;
}
/* Allocate and read fragment index table */
msblk->fragment_index = squashfs_read_fragment_index_table(sb,
le64_to_cpu(sblk->fragment_table_start), fragments);
if (IS_ERR(msblk->fragment_index)) {
err = PTR_ERR(msblk->fragment_index);
msblk->fragment_index = NULL;
goto failed_mount;
}
allocate_lookup_table:
lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
if (lookup_table_start == SQUASHFS_INVALID_BLK)
goto allocate_root;
/* Allocate and read inode lookup table */
msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
lookup_table_start, msblk->inodes);
if (IS_ERR(msblk->inode_lookup_table)) {
err = PTR_ERR(msblk->inode_lookup_table);
msblk->inode_lookup_table = NULL;
goto failed_mount;
}
sb->s_export_op = &squashfs_export_ops;
allocate_root:
root = new_inode(sb);
if (!root) {
err = -ENOMEM;
goto failed_mount;
}
err = squashfs_read_inode(root, root_inode);
if (err) {
iget_failed(root);
goto failed_mount;
}
insert_inode_hash(root);
sb->s_root = d_alloc_root(root);
if (sb->s_root == NULL) {
ERROR("Root inode create failed\n");
err = -ENOMEM;
iput(root);
goto failed_mount;
}
TRACE("Leaving squashfs_fill_super\n");
kfree(sblk);
return 0;
failed_mount:
squashfs_cache_delete(msblk->block_cache);
squashfs_cache_delete(msblk->fragment_cache);
squashfs_cache_delete(msblk->read_page);
kfree(msblk->inode_lookup_table);
kfree(msblk->fragment_index);
kfree(msblk->id_table);
kfree(msblk->stream.workspace);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
kfree(sblk);
return err;
failure:
kfree(msblk->stream.workspace);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
return -ENOMEM;
}
static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
TRACE("Entered squashfs_statfs\n");
buf->f_type = SQUASHFS_MAGIC;
buf->f_bsize = msblk->block_size;
buf->f_blocks = ((msblk->bytes_used - 1) >> msblk->block_log) + 1;
buf->f_bfree = buf->f_bavail = 0;
buf->f_files = msblk->inodes;
buf->f_ffree = 0;
buf->f_namelen = SQUASHFS_NAME_LEN;
return 0;
}
static int squashfs_remount(struct super_block *sb, int *flags, char *data)
{
*flags |= MS_RDONLY;
return 0;
}
static void squashfs_put_super(struct super_block *sb)
{
if (sb->s_fs_info) {
struct squashfs_sb_info *sbi = sb->s_fs_info;
squashfs_cache_delete(sbi->block_cache);
squashfs_cache_delete(sbi->fragment_cache);
squashfs_cache_delete(sbi->read_page);
kfree(sbi->id_table);
kfree(sbi->fragment_index);
kfree(sbi->meta_index);
kfree(sbi->stream.workspace);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
}
}
static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data,
struct vfsmount *mnt)
{
return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
mnt);
}
static struct kmem_cache *squashfs_inode_cachep;
static void init_once(void *foo)
{
struct squashfs_inode_info *ei = foo;
inode_init_once(&ei->vfs_inode);
}
static int __init init_inodecache(void)
{
squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
sizeof(struct squashfs_inode_info), 0,
SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once);
return squashfs_inode_cachep ? 0 : -ENOMEM;
}
static void destroy_inodecache(void)
{
kmem_cache_destroy(squashfs_inode_cachep);
}
static int __init init_squashfs_fs(void)
{
int err = init_inodecache();
if (err)
return err;
err = register_filesystem(&squashfs_fs_type);
if (err) {
destroy_inodecache();
return err;
}
printk(KERN_INFO "squashfs: version 4.0 (2009/01/03) "
"Phillip Lougher\n");
return 0;
}
static void __exit exit_squashfs_fs(void)
{
unregister_filesystem(&squashfs_fs_type);
destroy_inodecache();
}
static struct inode *squashfs_alloc_inode(struct super_block *sb)
{
struct squashfs_inode_info *ei =
kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
return ei ? &ei->vfs_inode : NULL;
}
static void squashfs_destroy_inode(struct inode *inode)
{
kmem_cache_free(squashfs_inode_cachep, squashfs_i(inode));
}
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 struct super_operations squashfs_super_ops = {
.alloc_inode = squashfs_alloc_inode,
.destroy_inode = squashfs_destroy_inode,
.statfs = squashfs_statfs,
.put_super = squashfs_put_super,
.remount_fs = squashfs_remount
};
module_init(init_squashfs_fs);
module_exit(exit_squashfs_fs);
MODULE_DESCRIPTION("squashfs 4.0, a compressed read-only filesystem");
MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,118 @@
/*
* Squashfs - a compressed read only filesystem for Linux
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* symlink.c
*/
/*
* This file implements code to handle symbolic links.
*
* The data contents of symbolic links are stored inside the symbolic
* link inode within the inode table. This allows the normally small symbolic
* link to be compressed as part of the inode table, achieving much greater
* compression than if the symbolic link was compressed individually.
*/
#include <linux/fs.h>
#include <linux/vfs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/zlib.h>
#include "squashfs_fs.h"
#include "squashfs_fs_sb.h"
#include "squashfs_fs_i.h"
#include "squashfs.h"
static int squashfs_symlink_readpage(struct file *file, struct page *page)
{
struct inode *inode = page->mapping->host;
struct super_block *sb = inode->i_sb;
struct squashfs_sb_info *msblk = sb->s_fs_info;
int index = page->index << PAGE_CACHE_SHIFT;
u64 block = squashfs_i(inode)->start;
int offset = squashfs_i(inode)->offset;
int length = min_t(int, i_size_read(inode) - index, PAGE_CACHE_SIZE);
int bytes, copied;
void *pageaddr;
struct squashfs_cache_entry *entry;
TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
"%llx, offset %x\n", page->index, block, offset);
/*
* Skip index bytes into symlink metadata.
*/
if (index) {
bytes = squashfs_read_metadata(sb, NULL, &block, &offset,
index);
if (bytes < 0) {
ERROR("Unable to read symlink [%llx:%x]\n",
squashfs_i(inode)->start,
squashfs_i(inode)->offset);
goto error_out;
}
}
/*
* Read length bytes from symlink metadata. Squashfs_read_metadata
* is not used here because it can sleep and we want to use
* kmap_atomic to map the page. Instead call the underlying
* squashfs_cache_get routine. As length bytes may overlap metadata
* blocks, we may need to call squashfs_cache_get multiple times.
*/
for (bytes = 0; bytes < length; offset = 0, bytes += copied) {
entry = squashfs_cache_get(sb, msblk->block_cache, block, 0);
if (entry->error) {
ERROR("Unable to read symlink [%llx:%x]\n",
squashfs_i(inode)->start,
squashfs_i(inode)->offset);
squashfs_cache_put(entry);
goto error_out;
}
pageaddr = kmap_atomic(page, KM_USER0);
copied = squashfs_copy_data(pageaddr + bytes, entry, offset,
length - bytes);
if (copied == length - bytes)
memset(pageaddr + length, 0, PAGE_CACHE_SIZE - length);
else
block = entry->next_index;
kunmap_atomic(pageaddr, KM_USER0);
squashfs_cache_put(entry);
}
flush_dcache_page(page);
SetPageUptodate(page);
unlock_page(page);
return 0;
error_out:
SetPageError(page);
unlock_page(page);
return 0;
}
const struct address_space_operations squashfs_symlink_aops = {
.readpage = squashfs_symlink_readpage
};

View file

@ -0,0 +1,380 @@
#ifndef SQUASHFS_FS
#define SQUASHFS_FS
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs.h
*/
#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
#define SQUASHFS_MAJOR 4
#define SQUASHFS_MINOR 0
#define SQUASHFS_MAGIC 0x73717368
#define SQUASHFS_START 0
/* size of metadata (inode and directory) blocks */
#define SQUASHFS_METADATA_SIZE 8192
#define SQUASHFS_METADATA_LOG 13
/* default size of data blocks */
#define SQUASHFS_FILE_SIZE 131072
#define SQUASHFS_FILE_LOG 17
#define SQUASHFS_FILE_MAX_SIZE 1048576
/* Max number of uids and gids */
#define SQUASHFS_IDS 65536
/* Max length of filename (not 255) */
#define SQUASHFS_NAME_LEN 256
#define SQUASHFS_INVALID_FRAG (0xffffffffU)
#define SQUASHFS_INVALID_BLK (-1LL)
/* Filesystem flags */
#define SQUASHFS_NOI 0
#define SQUASHFS_NOD 1
#define SQUASHFS_NOF 3
#define SQUASHFS_NO_FRAG 4
#define SQUASHFS_ALWAYS_FRAG 5
#define SQUASHFS_DUPLICATE 6
#define SQUASHFS_EXPORT 7
#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOI)
#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOD)
#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NOF)
#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_NO_FRAG)
#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
SQUASHFS_ALWAYS_FRAG)
#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
SQUASHFS_DUPLICATE)
#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
SQUASHFS_EXPORT)
/* Max number of types and file types */
#define SQUASHFS_DIR_TYPE 1
#define SQUASHFS_REG_TYPE 2
#define SQUASHFS_SYMLINK_TYPE 3
#define SQUASHFS_BLKDEV_TYPE 4
#define SQUASHFS_CHRDEV_TYPE 5
#define SQUASHFS_FIFO_TYPE 6
#define SQUASHFS_SOCKET_TYPE 7
#define SQUASHFS_LDIR_TYPE 8
#define SQUASHFS_LREG_TYPE 9
#define SQUASHFS_LSYMLINK_TYPE 10
#define SQUASHFS_LBLKDEV_TYPE 11
#define SQUASHFS_LCHRDEV_TYPE 12
#define SQUASHFS_LFIFO_TYPE 13
#define SQUASHFS_LSOCKET_TYPE 14
/* Flag whether block is compressed or uncompressed, bit is set if block is
* uncompressed */
#define SQUASHFS_COMPRESSED_BIT (1 << 15)
#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
(B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \
~SQUASHFS_COMPRESSED_BIT_BLOCK)
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
/*
* Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block
*/
#define SQUASHFS_INODE_BLK(A) ((unsigned int) ((A) >> 16))
#define SQUASHFS_INODE_OFFSET(A) ((unsigned int) ((A) & 0xffff))
#define SQUASHFS_MKINODE(A, B) ((long long)(((long long) (A)\
<< 16) + (B)))
/* Translate between VFS mode and squashfs mode */
#define SQUASHFS_MODE(A) ((A) & 0xfff)
/* fragment and fragment table defines */
#define SQUASHFS_FRAGMENT_BYTES(A) \
((A) * sizeof(struct squashfs_fragment_entry))
#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
sizeof(long long))
/* inode lookup table defines */
#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(long long))
#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
sizeof(long long))
/* uid/gid lookup table defines */
#define SQUASHFS_ID_BYTES(A) ((A) * sizeof(unsigned int))
#define SQUASHFS_ID_BLOCK(A) (SQUASHFS_ID_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_OFFSET(A) (SQUASHFS_ID_BYTES(A) % \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCKS(A) ((SQUASHFS_ID_BYTES(A) + \
SQUASHFS_METADATA_SIZE - 1) / \
SQUASHFS_METADATA_SIZE)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(long long))
/* cached data constants for filesystem */
#define SQUASHFS_CACHED_BLKS 8
#define SQUASHFS_MAX_FILE_SIZE_LOG 64
#define SQUASHFS_MAX_FILE_SIZE (1LL << \
(SQUASHFS_MAX_FILE_SIZE_LOG - 2))
#define SQUASHFS_MARKER_BYTE 0xff
/* meta index cache */
#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
#define SQUASHFS_META_ENTRIES 127
#define SQUASHFS_META_SLOTS 8
struct meta_entry {
long long data_block;
unsigned int index_block;
unsigned short offset;
unsigned short pad;
};
struct meta_index {
unsigned int inode_number;
unsigned int offset;
unsigned short entries;
unsigned short skip;
unsigned short locked;
unsigned short pad;
struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
};
/*
* definitions for structures on disk
*/
#define ZLIB_COMPRESSION 1
struct squashfs_super_block {
__le32 s_magic;
__le32 inodes;
__le32 mkfs_time;
__le32 block_size;
__le32 fragments;
__le16 compression;
__le16 block_log;
__le16 flags;
__le16 no_ids;
__le16 s_major;
__le16 s_minor;
__le64 root_inode;
__le64 bytes_used;
__le64 id_table_start;
__le64 xattr_table_start;
__le64 inode_table_start;
__le64 directory_table_start;
__le64 fragment_table_start;
__le64 lookup_table_start;
};
struct squashfs_dir_index {
__le32 index;
__le32 start_block;
__le32 size;
unsigned char name[0];
};
struct squashfs_base_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
};
struct squashfs_ipc_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
};
struct squashfs_dev_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 rdev;
};
struct squashfs_symlink_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 symlink_size;
char symlink[0];
};
struct squashfs_reg_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 start_block;
__le32 fragment;
__le32 offset;
__le32 file_size;
__le16 block_list[0];
};
struct squashfs_lreg_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le64 start_block;
__le64 file_size;
__le64 sparse;
__le32 nlink;
__le32 fragment;
__le32 offset;
__le32 xattr;
__le16 block_list[0];
};
struct squashfs_dir_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 start_block;
__le32 nlink;
__le16 file_size;
__le16 offset;
__le32 parent_inode;
};
struct squashfs_ldir_inode {
__le16 inode_type;
__le16 mode;
__le16 uid;
__le16 guid;
__le32 mtime;
__le32 inode_number;
__le32 nlink;
__le32 file_size;
__le32 start_block;
__le32 parent_inode;
__le16 i_count;
__le16 offset;
__le32 xattr;
struct squashfs_dir_index index[0];
};
union squashfs_inode {
struct squashfs_base_inode base;
struct squashfs_dev_inode dev;
struct squashfs_symlink_inode symlink;
struct squashfs_reg_inode reg;
struct squashfs_lreg_inode lreg;
struct squashfs_dir_inode dir;
struct squashfs_ldir_inode ldir;
struct squashfs_ipc_inode ipc;
};
struct squashfs_dir_entry {
__le16 offset;
__le16 inode_number;
__le16 type;
__le16 size;
char name[0];
};
struct squashfs_dir_header {
__le32 count;
__le32 start_block;
__le32 inode_number;
};
struct squashfs_fragment_entry {
__le64 start_block;
__le32 size;
unsigned int unused;
};
#endif

View file

@ -0,0 +1,45 @@
#ifndef SQUASHFS_FS_I
#define SQUASHFS_FS_I
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_i.h
*/
struct squashfs_inode_info {
long long start;
int offset;
union {
struct {
long long fragment_block;
int fragment_size;
int fragment_offset;
long long block_list_start;
};
struct {
long long dir_idx_start;
int dir_idx_offset;
int dir_idx_cnt;
int parent;
};
};
struct inode vfs_inode;
};
#endif

View file

@ -0,0 +1,76 @@
#ifndef SQUASHFS_FS_SB
#define SQUASHFS_FS_SB
/*
* Squashfs
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
* Phillip Lougher <phillip@lougher.demon.co.uk>
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* squashfs_fs_sb.h
*/
#include "squashfs_fs.h"
struct squashfs_cache_entry {
long long block;
int length;
int locked;
long long next_index;
char pending;
char error;
int waiting;
wait_queue_head_t wait_queue;
char *data;
};
struct squashfs_cache {
char *name;
int entries;
int block_size;
int next_blk;
int waiting;
int unused;
int use_vmalloc;
spinlock_t lock;
wait_queue_head_t wait_queue;
struct squashfs_cache_entry entry[0];
};
struct squashfs_sb_info {
int devblksize;
int devblksize_log2;
struct squashfs_cache *block_cache;
struct squashfs_cache *fragment_cache;
int next_meta_index;
__le64 *id_table;
__le64 *fragment_index;
unsigned int *fragment_index_2;
char *read_page;
struct mutex read_data_mutex;
struct mutex read_page_mutex;
struct mutex meta_index_mutex;
struct meta_index *meta_index;
z_stream stream;
__le64 *inode_lookup_table;
long long inode_table;
long long directory_table;
unsigned int block_size;
unsigned short block_log;
long long bytes_used;
unsigned int inodes;
};
#endif