2005-04-17 02:20:36 +04:00
/*
* QNX4 file system , Linux implementation .
*
* Version : 0.2 .1
*
* Using parts of the xiafs filesystem .
*
* History :
*
* 01 - 06 - 1998 by Richard Frowijn : first release .
* 20 - 06 - 1998 by Frank Denis : Linux 2.1 .99 + support , boot signature , misc .
* 30 - 06 - 1998 by Frank Denis : first step to write inodes .
*/
# include <linux/module.h>
# include <linux/init.h>
2009-06-07 17:47:13 +04:00
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
# include <linux/highuid.h>
# include <linux/pagemap.h>
# include <linux/buffer_head.h>
2009-06-07 17:30:08 +04:00
# include <linux/writeback.h>
2009-06-07 17:47:13 +04:00
# include <linux/statfs.h>
# include "qnx4.h"
2005-04-17 02:20:36 +04:00
# define QNX4_VERSION 4
# define QNX4_BMNAME ".bitmap"
2007-02-12 11:55:41 +03:00
static const struct super_operations qnx4_sops ;
2005-04-17 02:20:36 +04:00
static void qnx4_put_super ( struct super_block * sb ) ;
static struct inode * qnx4_alloc_inode ( struct super_block * sb ) ;
static void qnx4_destroy_inode ( struct inode * inode ) ;
static int qnx4_remount ( struct super_block * sb , int * flags , char * data ) ;
2006-06-23 13:02:58 +04:00
static int qnx4_statfs ( struct dentry * , struct kstatfs * ) ;
2005-04-17 02:20:36 +04:00
2007-02-12 11:55:41 +03:00
static const struct super_operations qnx4_sops =
2005-04-17 02:20:36 +04:00
{
. alloc_inode = qnx4_alloc_inode ,
. destroy_inode = qnx4_destroy_inode ,
. put_super = qnx4_put_super ,
. statfs = qnx4_statfs ,
. remount_fs = qnx4_remount ,
} ;
static int qnx4_remount ( struct super_block * sb , int * flags , char * data )
{
struct qnx4_sb_info * qs ;
qs = qnx4_sb ( sb ) ;
qs - > Version = QNX4_VERSION ;
* flags | = MS_RDONLY ;
return 0 ;
}
static struct buffer_head * qnx4_getblk ( struct inode * inode , int nr ,
int create )
{
struct buffer_head * result = NULL ;
if ( nr > = 0 )
nr = qnx4_block_map ( inode , nr ) ;
if ( nr ) {
result = sb_getblk ( inode - > i_sb , nr ) ;
return result ;
}
2009-12-16 03:48:23 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
struct buffer_head * qnx4_bread ( struct inode * inode , int block , int create )
{
struct buffer_head * bh ;
bh = qnx4_getblk ( inode , block , create ) ;
if ( ! bh | | buffer_uptodate ( bh ) ) {
return bh ;
}
ll_rw_block ( READ , 1 , & bh ) ;
wait_on_buffer ( bh ) ;
if ( buffer_uptodate ( bh ) ) {
return bh ;
}
brelse ( bh ) ;
return NULL ;
}
static int qnx4_get_block ( struct inode * inode , sector_t iblock , struct buffer_head * bh , int create )
{
unsigned long phys ;
2009-09-26 22:15:09 +04:00
QNX4DEBUG ( ( KERN_INFO " qnx4: qnx4_get_block inode=[%ld] iblock=[%ld] \n " , inode - > i_ino , iblock ) ) ;
2005-04-17 02:20:36 +04:00
phys = qnx4_block_map ( inode , iblock ) ;
if ( phys ) {
// logical block is before EOF
map_bh ( bh , inode - > i_sb , phys ) ;
}
return 0 ;
}
unsigned long qnx4_block_map ( struct inode * inode , long iblock )
{
int ix ;
long offset , i_xblk ;
unsigned long block = 0 ;
struct buffer_head * bh = NULL ;
struct qnx4_xblk * xblk = NULL ;
struct qnx4_inode_entry * qnx4_inode = qnx4_raw_inode ( inode ) ;
2005-06-25 00:52:52 +04:00
u16 nxtnt = le16_to_cpu ( qnx4_inode - > di_num_xtnts ) ;
2005-04-17 02:20:36 +04:00
if ( iblock < le32_to_cpu ( qnx4_inode - > di_first_xtnt . xtnt_size ) ) {
// iblock is in the first extent. This is easy.
block = le32_to_cpu ( qnx4_inode - > di_first_xtnt . xtnt_blk ) + iblock - 1 ;
} else {
// iblock is beyond first extent. We have to follow the extent chain.
i_xblk = le32_to_cpu ( qnx4_inode - > di_xblk ) ;
offset = iblock - le32_to_cpu ( qnx4_inode - > di_first_xtnt . xtnt_size ) ;
ix = 0 ;
while ( - - nxtnt > 0 ) {
if ( ix = = 0 ) {
// read next xtnt block.
bh = sb_bread ( inode - > i_sb , i_xblk - 1 ) ;
if ( ! bh ) {
2009-09-26 22:15:09 +04:00
QNX4DEBUG ( ( KERN_ERR " qnx4: I/O error reading xtnt block [%ld]) \n " , i_xblk - 1 ) ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
xblk = ( struct qnx4_xblk * ) bh - > b_data ;
if ( memcmp ( xblk - > xblk_signature , " IamXblk " , 7 ) ) {
2009-09-26 22:15:09 +04:00
QNX4DEBUG ( ( KERN_ERR " qnx4: block at %ld is not a valid xtnt \n " , qnx4_inode - > i_xblk ) ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
}
if ( offset < le32_to_cpu ( xblk - > xblk_xtnts [ ix ] . xtnt_size ) ) {
// got it!
block = le32_to_cpu ( xblk - > xblk_xtnts [ ix ] . xtnt_blk ) + offset - 1 ;
break ;
}
offset - = le32_to_cpu ( xblk - > xblk_xtnts [ ix ] . xtnt_size ) ;
if ( + + ix > = xblk - > xblk_num_xtnts ) {
i_xblk = le32_to_cpu ( xblk - > xblk_next_xblk ) ;
ix = 0 ;
brelse ( bh ) ;
bh = NULL ;
}
}
if ( bh )
brelse ( bh ) ;
}
2009-09-26 22:15:09 +04:00
QNX4DEBUG ( ( KERN_INFO " qnx4: mapping block %ld of inode %ld = %ld \n " , iblock , inode - > i_ino , block ) ) ;
2005-04-17 02:20:36 +04:00
return block ;
}
2006-06-23 13:02:58 +04:00
static int qnx4_statfs ( struct dentry * dentry , struct kstatfs * buf )
2005-04-17 02:20:36 +04:00
{
2006-06-23 13:02:58 +04:00
struct super_block * sb = dentry - > d_sb ;
2009-04-03 03:59:40 +04:00
u64 id = huge_encode_dev ( sb - > s_bdev - > bd_dev ) ;
2006-06-23 13:02:58 +04:00
2005-04-17 02:20:36 +04:00
buf - > f_type = sb - > s_magic ;
buf - > f_bsize = sb - > s_blocksize ;
buf - > f_blocks = le32_to_cpu ( qnx4_sb ( sb ) - > BitMap - > di_size ) * 8 ;
buf - > f_bfree = qnx4_count_free_blocks ( sb ) ;
buf - > f_bavail = buf - > f_bfree ;
buf - > f_namelen = QNX4_NAME_MAX ;
2009-04-03 03:59:40 +04:00
buf - > f_fsid . val [ 0 ] = ( u32 ) id ;
buf - > f_fsid . val [ 1 ] = ( u32 ) ( id > > 32 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Check the root directory of the filesystem to make sure
* it really _is_ a qnx4 filesystem , and to check the size
* of the directory entry .
*/
static const char * qnx4_checkroot ( struct super_block * sb )
{
struct buffer_head * bh ;
struct qnx4_inode_entry * rootdir ;
int rd , rl ;
int i , j ;
int found = 0 ;
if ( * ( qnx4_sb ( sb ) - > sb - > RootDir . di_fname ) ! = ' / ' ) {
return " no qnx4 filesystem (no root dir). " ;
} else {
2009-09-26 22:15:09 +04:00
QNX4DEBUG ( ( KERN_NOTICE " QNX4 filesystem found on dev %s. \n " , sb - > s_id ) ) ;
2005-04-17 02:20:36 +04:00
rd = le32_to_cpu ( qnx4_sb ( sb ) - > sb - > RootDir . di_first_xtnt . xtnt_blk ) - 1 ;
rl = le32_to_cpu ( qnx4_sb ( sb ) - > sb - > RootDir . di_first_xtnt . xtnt_size ) ;
for ( j = 0 ; j < rl ; j + + ) {
bh = sb_bread ( sb , rd + j ) ; /* root dir, first block */
if ( bh = = NULL ) {
return " unable to read root entry. " ;
}
for ( i = 0 ; i < QNX4_INODES_PER_BLOCK ; i + + ) {
rootdir = ( struct qnx4_inode_entry * ) ( bh - > b_data + i * QNX4_DIR_ENTRY_SIZE ) ;
if ( rootdir - > di_fname ! = NULL ) {
2009-09-26 22:15:09 +04:00
QNX4DEBUG ( ( KERN_INFO " rootdir entry found : [%s] \n " , rootdir - > di_fname ) ) ;
2009-11-13 01:08:36 +03:00
if ( ! strcmp ( rootdir - > di_fname ,
QNX4_BMNAME ) ) {
2005-04-17 02:20:36 +04:00
found = 1 ;
qnx4_sb ( sb ) - > BitMap = kmalloc ( sizeof ( struct qnx4_inode_entry ) , GFP_KERNEL ) ;
if ( ! qnx4_sb ( sb ) - > BitMap ) {
brelse ( bh ) ;
return " not enough memory for bitmap inode " ;
}
memcpy ( qnx4_sb ( sb ) - > BitMap , rootdir , sizeof ( struct qnx4_inode_entry ) ) ; /* keep bitmap inode known */
break ;
}
}
}
brelse ( bh ) ;
if ( found ! = 0 ) {
break ;
}
}
if ( found = = 0 ) {
return " bitmap file not found. " ;
}
}
return NULL ;
}
static int qnx4_fill_super ( struct super_block * s , void * data , int silent )
{
struct buffer_head * bh ;
struct inode * root ;
const char * errmsg ;
struct qnx4_sb_info * qs ;
2008-02-07 11:15:45 +03:00
int ret = - EINVAL ;
2005-04-17 02:20:36 +04:00
2006-09-27 12:49:37 +04:00
qs = kzalloc ( sizeof ( struct qnx4_sb_info ) , GFP_KERNEL ) ;
2010-10-21 17:53:15 +04:00
if ( ! qs )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
s - > s_fs_info = qs ;
sb_set_blocksize ( s , QNX4_BLOCK_SIZE ) ;
/* Check the superblock signature. Since the qnx4 code is
dangerous , we should leave as quickly as possible
if we don ' t belong here . . . */
bh = sb_bread ( s , 1 ) ;
if ( ! bh ) {
2009-09-26 22:15:09 +04:00
printk ( KERN_ERR " qnx4: unable to read the superblock \n " ) ;
2005-04-17 02:20:36 +04:00
goto outnobh ;
}
2005-06-25 00:52:52 +04:00
if ( le32_to_cpup ( ( __le32 * ) bh - > b_data ) ! = QNX4_SUPER_MAGIC ) {
2005-04-17 02:20:36 +04:00
if ( ! silent )
2009-09-26 22:15:09 +04:00
printk ( KERN_ERR " qnx4: wrong fsid in superblock. \n " ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
s - > s_op = & qnx4_sops ;
s - > s_magic = QNX4_SUPER_MAGIC ;
s - > s_flags | = MS_RDONLY ; /* Yup, read-only yet */
qnx4_sb ( s ) - > sb_buf = bh ;
qnx4_sb ( s ) - > sb = ( struct qnx4_super_block * ) bh - > b_data ;
/* check before allocating dentries, inodes, .. */
errmsg = qnx4_checkroot ( s ) ;
if ( errmsg ! = NULL ) {
if ( ! silent )
2009-09-26 22:15:09 +04:00
printk ( KERN_ERR " qnx4: %s \n " , errmsg ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
/* does root not have inode number QNX4_ROOT_INO ?? */
2008-02-07 11:15:45 +03:00
root = qnx4_iget ( s , QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK ) ;
if ( IS_ERR ( root ) ) {
2009-09-26 22:15:09 +04:00
printk ( KERN_ERR " qnx4: get inode failed \n " ) ;
2008-02-07 11:15:45 +03:00
ret = PTR_ERR ( root ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2008-02-07 11:15:45 +03:00
ret = - ENOMEM ;
2005-04-17 02:20:36 +04:00
s - > s_root = d_alloc_root ( root ) ;
if ( s - > s_root = = NULL )
goto outi ;
brelse ( bh ) ;
return 0 ;
outi :
iput ( root ) ;
out :
brelse ( bh ) ;
outnobh :
kfree ( qs ) ;
s - > s_fs_info = NULL ;
2008-02-07 11:15:45 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
static void qnx4_put_super ( struct super_block * sb )
{
struct qnx4_sb_info * qs = qnx4_sb ( sb ) ;
kfree ( qs - > BitMap ) ;
kfree ( qs ) ;
sb - > s_fs_info = NULL ;
return ;
}
static int qnx4_writepage ( struct page * page , struct writeback_control * wbc )
{
return block_write_full_page ( page , qnx4_get_block , wbc ) ;
}
2007-10-16 12:25:12 +04:00
2005-04-17 02:20:36 +04:00
static int qnx4_readpage ( struct file * file , struct page * page )
{
return block_read_full_page ( page , qnx4_get_block ) ;
}
2007-10-16 12:25:12 +04:00
static int qnx4_write_begin ( struct file * file , struct address_space * mapping ,
loff_t pos , unsigned len , unsigned flags ,
struct page * * pagep , void * * fsdata )
2005-04-17 02:20:36 +04:00
{
2007-10-16 12:25:12 +04:00
struct qnx4_inode_info * qnx4_inode = qnx4_i ( mapping - > host ) ;
2010-06-04 13:29:55 +04:00
int ret ;
2007-10-16 12:25:12 +04:00
* pagep = NULL ;
2010-06-04 13:29:55 +04:00
ret = cont_write_begin ( file , mapping , pos , len , flags , pagep , fsdata ,
2007-10-16 12:25:12 +04:00
qnx4_get_block ,
& qnx4_inode - > mmu_private ) ;
2010-06-04 13:29:55 +04:00
if ( unlikely ( ret ) ) {
loff_t isize = mapping - > host - > i_size ;
if ( pos + len > isize )
vmtruncate ( mapping - > host , isize ) ;
}
return ret ;
2005-04-17 02:20:36 +04:00
}
static sector_t qnx4_bmap ( struct address_space * mapping , sector_t block )
{
return generic_block_bmap ( mapping , block , qnx4_get_block ) ;
}
2006-06-28 15:26:44 +04:00
static const struct address_space_operations qnx4_aops = {
2005-04-17 02:20:36 +04:00
. readpage = qnx4_readpage ,
. writepage = qnx4_writepage ,
2007-10-16 12:25:12 +04:00
. write_begin = qnx4_write_begin ,
. write_end = generic_write_end ,
2005-04-17 02:20:36 +04:00
. bmap = qnx4_bmap
} ;
2008-02-07 11:15:45 +03:00
struct inode * qnx4_iget ( struct super_block * sb , unsigned long ino )
2005-04-17 02:20:36 +04:00
{
struct buffer_head * bh ;
struct qnx4_inode_entry * raw_inode ;
2008-02-07 11:15:45 +03:00
int block ;
struct qnx4_inode_entry * qnx4_inode ;
struct inode * inode ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:45 +03:00
inode = iget_locked ( sb , ino ) ;
if ( ! inode )
return ERR_PTR ( - ENOMEM ) ;
if ( ! ( inode - > i_state & I_NEW ) )
return inode ;
qnx4_inode = qnx4_raw_inode ( inode ) ;
2005-04-17 02:20:36 +04:00
inode - > i_mode = 0 ;
2009-09-26 22:15:09 +04:00
QNX4DEBUG ( ( KERN_INFO " reading inode : [%d] \n " , ino ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! ino ) {
2008-02-07 11:15:45 +03:00
printk ( KERN_ERR " qnx4: bad inode number on dev %s: %lu is "
" out of range \n " ,
2005-04-17 02:20:36 +04:00
sb - > s_id , ino ) ;
2008-02-07 11:15:45 +03:00
iget_failed ( inode ) ;
return ERR_PTR ( - EIO ) ;
2005-04-17 02:20:36 +04:00
}
block = ino / QNX4_INODES_PER_BLOCK ;
if ( ! ( bh = sb_bread ( sb , block ) ) ) {
2009-09-26 22:15:09 +04:00
printk ( KERN_ERR " qnx4: major problem: unable to read inode from dev "
2005-04-17 02:20:36 +04:00
" %s \n " , sb - > s_id ) ;
2008-02-07 11:15:45 +03:00
iget_failed ( inode ) ;
return ERR_PTR ( - EIO ) ;
2005-04-17 02:20:36 +04:00
}
raw_inode = ( ( struct qnx4_inode_entry * ) bh - > b_data ) +
( ino % QNX4_INODES_PER_BLOCK ) ;
inode - > i_mode = le16_to_cpu ( raw_inode - > di_mode ) ;
inode - > i_uid = ( uid_t ) le16_to_cpu ( raw_inode - > di_uid ) ;
inode - > i_gid = ( gid_t ) le16_to_cpu ( raw_inode - > di_gid ) ;
inode - > i_nlink = le16_to_cpu ( raw_inode - > di_nlink ) ;
inode - > i_size = le32_to_cpu ( raw_inode - > di_size ) ;
inode - > i_mtime . tv_sec = le32_to_cpu ( raw_inode - > di_mtime ) ;
inode - > i_mtime . tv_nsec = 0 ;
inode - > i_atime . tv_sec = le32_to_cpu ( raw_inode - > di_atime ) ;
inode - > i_atime . tv_nsec = 0 ;
inode - > i_ctime . tv_sec = le32_to_cpu ( raw_inode - > di_ctime ) ;
inode - > i_ctime . tv_nsec = 0 ;
inode - > i_blocks = le32_to_cpu ( raw_inode - > di_first_xtnt . xtnt_size ) ;
memcpy ( qnx4_inode , raw_inode , QNX4_DIR_ENTRY_SIZE ) ;
if ( S_ISREG ( inode - > i_mode ) ) {
2009-09-23 03:43:59 +04:00
inode - > i_fop = & generic_ro_fops ;
2005-04-17 02:20:36 +04:00
inode - > i_mapping - > a_ops = & qnx4_aops ;
qnx4_i ( inode ) - > mmu_private = inode - > i_size ;
} else if ( S_ISDIR ( inode - > i_mode ) ) {
inode - > i_op = & qnx4_dir_inode_operations ;
inode - > i_fop = & qnx4_dir_operations ;
} else if ( S_ISLNK ( inode - > i_mode ) ) {
inode - > i_op = & page_symlink_inode_operations ;
inode - > i_mapping - > a_ops = & qnx4_aops ;
qnx4_i ( inode ) - > mmu_private = inode - > i_size ;
2008-02-07 11:15:45 +03:00
} else {
printk ( KERN_ERR " qnx4: bad inode %lu on dev %s \n " ,
ino , sb - > s_id ) ;
iget_failed ( inode ) ;
brelse ( bh ) ;
return ERR_PTR ( - EIO ) ;
}
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
2008-02-07 11:15:45 +03:00
unlock_new_inode ( inode ) ;
return inode ;
2005-04-17 02:20:36 +04:00
}
2006-12-07 07:33:20 +03:00
static struct kmem_cache * qnx4_inode_cachep ;
2005-04-17 02:20:36 +04:00
static struct inode * qnx4_alloc_inode ( struct super_block * sb )
{
struct qnx4_inode_info * ei ;
2006-12-07 07:33:17 +03:00
ei = kmem_cache_alloc ( qnx4_inode_cachep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ei )
return NULL ;
return & ei - > vfs_inode ;
}
2011-01-07 09:49:49 +03:00
static void qnx4_i_callback ( struct rcu_head * head )
2005-04-17 02:20:36 +04:00
{
2011-01-07 09:49:49 +03:00
struct inode * inode = container_of ( head , struct inode , i_rcu ) ;
INIT_LIST_HEAD ( & inode - > i_dentry ) ;
2005-04-17 02:20:36 +04:00
kmem_cache_free ( qnx4_inode_cachep , qnx4_i ( inode ) ) ;
}
2011-01-07 09:49:49 +03:00
static void qnx4_destroy_inode ( struct inode * inode )
{
call_rcu ( & inode - > i_rcu , qnx4_i_callback ) ;
}
2008-07-26 06:45:34 +04:00
static void init_once ( void * foo )
2005-04-17 02:20:36 +04:00
{
struct qnx4_inode_info * ei = ( struct qnx4_inode_info * ) foo ;
2007-05-17 09:10:57 +04:00
inode_init_once ( & ei - > vfs_inode ) ;
2005-04-17 02:20:36 +04:00
}
static int init_inodecache ( void )
{
qnx4_inode_cachep = kmem_cache_create ( " qnx4_inode_cache " ,
sizeof ( struct qnx4_inode_info ) ,
2006-03-24 14:16:06 +03:00
0 , ( SLAB_RECLAIM_ACCOUNT |
SLAB_MEM_SPREAD ) ,
2007-07-20 05:11:58 +04:00
init_once ) ;
2005-04-17 02:20:36 +04:00
if ( qnx4_inode_cachep = = NULL )
return - ENOMEM ;
return 0 ;
}
static void destroy_inodecache ( void )
{
2006-09-27 12:49:40 +04:00
kmem_cache_destroy ( qnx4_inode_cachep ) ;
2005-04-17 02:20:36 +04:00
}
2010-07-25 00:46:55 +04:00
static struct dentry * qnx4_mount ( struct file_system_type * fs_type ,
int flags , const char * dev_name , void * data )
2005-04-17 02:20:36 +04:00
{
2010-07-25 00:46:55 +04:00
return mount_bdev ( fs_type , flags , dev_name , data , qnx4_fill_super ) ;
2005-04-17 02:20:36 +04:00
}
static struct file_system_type qnx4_fs_type = {
. owner = THIS_MODULE ,
. name = " qnx4 " ,
2010-07-25 00:46:55 +04:00
. mount = qnx4_mount ,
2005-04-17 02:20:36 +04:00
. kill_sb = kill_block_super ,
. fs_flags = FS_REQUIRES_DEV ,
} ;
static int __init init_qnx4_fs ( void )
{
int err ;
err = init_inodecache ( ) ;
if ( err )
return err ;
err = register_filesystem ( & qnx4_fs_type ) ;
if ( err ) {
destroy_inodecache ( ) ;
return err ;
}
2009-09-26 22:15:09 +04:00
printk ( KERN_INFO " QNX4 filesystem 0.2.3 registered. \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit exit_qnx4_fs ( void )
{
unregister_filesystem ( & qnx4_fs_type ) ;
destroy_inodecache ( ) ;
}
module_init ( init_qnx4_fs )
module_exit ( exit_qnx4_fs )
MODULE_LICENSE ( " GPL " ) ;