2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
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 struct inode * qnx4_alloc_inode ( struct super_block * sb ) ;
2019-04-16 03:29:32 +03:00
static void qnx4_free_inode ( struct inode * inode ) ;
2005-04-17 02:20:36 +04:00
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 ,
2019-04-16 03:29:32 +03:00
. free_inode = qnx4_free_inode ,
2005-04-17 02:20:36 +04:00
. statfs = qnx4_statfs ,
. remount_fs = qnx4_remount ,
} ;
static int qnx4_remount ( struct super_block * sb , int * flags , char * data )
{
struct qnx4_sb_info * qs ;
2014-03-13 18:14:33 +04:00
sync_filesystem ( sb ) ;
2005-04-17 02:20:36 +04:00
qs = qnx4_sb ( sb ) ;
qs - > Version = QNX4_VERSION ;
2017-11-28 00:05:09 +03:00
* flags | = SB_RDONLY ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
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 ;
}
2012-02-15 02:48:20 +04:00
static inline u32 try_extent ( qnx4_xtnt_t * extent , u32 * offset )
{
u32 size = le32_to_cpu ( extent - > xtnt_size ) ;
if ( * offset < size )
return le32_to_cpu ( extent - > xtnt_blk ) + * offset - 1 ;
* offset - = size ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
unsigned long qnx4_block_map ( struct inode * inode , long iblock )
{
int ix ;
2012-02-15 02:48:20 +04:00
long i_xblk ;
2005-04-17 02:20:36 +04:00
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 ) ;
2012-02-15 02:48:20 +04:00
u32 offset = iblock ;
u32 block = try_extent ( & qnx4_inode - > di_first_xtnt , & offset ) ;
2005-04-17 02:20:36 +04:00
2012-02-15 02:48:20 +04:00
if ( block ) {
2005-04-17 02:20:36 +04:00
// iblock is in the first extent. This is easy.
} else {
// iblock is beyond first extent. We have to follow the extent chain.
i_xblk = le32_to_cpu ( qnx4_inode - > di_xblk ) ;
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 ;
}
}
2012-02-15 02:48:20 +04:00
block = try_extent ( & xblk - > xblk_xtnts [ ix ] , & offset ) ;
if ( block ) {
2005-04-17 02:20:36 +04:00
// got it!
break ;
}
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 .
*/
2013-12-11 04:48:58 +04:00
static const char * qnx4_checkroot ( struct super_block * sb ,
struct qnx4_super_block * s )
2005-04-17 02:20:36 +04:00
{
struct buffer_head * bh ;
struct qnx4_inode_entry * rootdir ;
int rd , rl ;
int i , j ;
2013-12-11 04:48:58 +04:00
if ( s - > RootDir . di_fname [ 0 ] ! = ' / ' | | s - > RootDir . di_fname [ 1 ] ! = ' \0 ' )
2005-04-17 02:20:36 +04:00
return " no qnx4 filesystem (no root dir). " ;
2012-01-19 22:40:57 +04:00
QNX4DEBUG ( ( KERN_NOTICE " QNX4 filesystem found on dev %s. \n " , sb - > s_id ) ) ;
2013-12-11 04:48:58 +04:00
rd = le32_to_cpu ( s - > RootDir . di_first_xtnt . xtnt_blk ) - 1 ;
rl = le32_to_cpu ( s - > RootDir . di_first_xtnt . xtnt_size ) ;
2012-01-19 22:40:57 +04:00
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. " ;
rootdir = ( struct qnx4_inode_entry * ) bh - > b_data ;
for ( i = 0 ; i < QNX4_INODES_PER_BLOCK ; i + + , rootdir + + ) {
QNX4DEBUG ( ( KERN_INFO " rootdir entry found : [%s] \n " , rootdir - > di_fname ) ) ;
if ( strcmp ( rootdir - > di_fname , QNX4_BMNAME ) ! = 0 )
continue ;
qnx4_sb ( sb ) - > BitMap = kmemdup ( rootdir ,
sizeof ( struct qnx4_inode_entry ) ,
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
brelse ( bh ) ;
2012-01-19 22:40:57 +04:00
if ( ! qnx4_sb ( sb ) - > BitMap )
return " not enough memory for bitmap inode " ;
/* keep bitmap inode known */
return NULL ;
2005-04-17 02:20:36 +04:00
}
2012-01-19 22:40:57 +04:00
brelse ( bh ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-19 22:40:57 +04:00
return " bitmap file not found. " ;
2005-04-17 02:20:36 +04:00
}
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 ;
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 ) ;
2013-12-11 04:48:58 +04:00
s - > s_op = & qnx4_sops ;
s - > s_magic = QNX4_SUPER_MAGIC ;
2017-11-28 00:05:09 +03:00
s - > s_flags | = SB_RDONLY ; /* Yup, read-only yet */
2019-07-30 18:22:29 +03:00
s - > s_time_min = 0 ;
s - > s_time_max = U32_MAX ;
2013-12-11 04:48:58 +04:00
2005-04-17 02:20:36 +04:00
/* 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 " ) ;
2013-12-11 04:48:58 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
/* check before allocating dentries, inodes, .. */
2013-12-11 04:48:58 +04:00
errmsg = qnx4_checkroot ( s , ( struct qnx4_super_block * ) bh - > b_data ) ;
brelse ( bh ) ;
2005-04-17 02:20:36 +04:00
if ( errmsg ! = NULL ) {
if ( ! silent )
2009-09-26 22:15:09 +04:00
printk ( KERN_ERR " qnx4: %s \n " , errmsg ) ;
2013-12-11 04:48:58 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
/* 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 " ) ;
2013-12-11 04:48:58 +04:00
return PTR_ERR ( root ) ;
2005-04-17 02:20:36 +04:00
}
2012-01-09 07:15:13 +04:00
s - > s_root = d_make_root ( root ) ;
2005-04-17 02:20:36 +04:00
if ( s - > s_root = = NULL )
2013-12-11 04:48:58 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2013-12-11 04:48:58 +04:00
static void qnx4_kill_sb ( struct super_block * sb )
2005-04-17 02:20:36 +04:00
{
struct qnx4_sb_info * qs = qnx4_sb ( sb ) ;
2013-12-11 04:48:58 +04:00
kill_block_super ( sb ) ;
if ( qs ) {
kfree ( qs - > BitMap ) ;
kfree ( qs ) ;
}
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
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 ,
. 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 ) ;
2012-02-11 00:11:12 +04:00
i_uid_write ( inode , ( uid_t ) le16_to_cpu ( raw_inode - > di_uid ) ) ;
i_gid_write ( inode , ( gid_t ) le16_to_cpu ( raw_inode - > di_gid ) ) ;
2011-10-28 16:13:29 +04:00
set_nlink ( inode , le16_to_cpu ( raw_inode - > di_nlink ) ) ;
2005-04-17 02:20:36 +04:00
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 ;
2015-11-17 09:07:57 +03:00
inode_nohighmem ( inode ) ;
2005-04-17 02:20:36 +04:00
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 ;
}
2019-04-16 03:29:32 +03:00
static void qnx4_free_inode ( struct inode * inode )
2005-04-17 02:20:36 +04:00
{
kmem_cache_free ( qnx4_inode_cachep , qnx4_i ( inode ) ) ;
}
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 |
2016-01-15 02:18:21 +03:00
SLAB_MEM_SPREAD | SLAB_ACCOUNT ) ,
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 )
{
2012-09-26 05:33:07 +04:00
/*
* Make sure all delayed rcu free inodes are flushed before we
* destroy cache .
*/
rcu_barrier ( ) ;
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 ,
2013-12-11 04:48:58 +04:00
. kill_sb = qnx4_kill_sb ,
2005-04-17 02:20:36 +04:00
. fs_flags = FS_REQUIRES_DEV ,
} ;
2013-03-03 07:39:14 +04:00
MODULE_ALIAS_FS ( " qnx4 " ) ;
2005-04-17 02:20:36 +04:00
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 " ) ;