18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * QNX4 file system, Linux implementation.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Version : 0.2.1
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Using parts of the xiafs filesystem.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * History :
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * 01-06-1998 by Richard Frowijn : first release.
128c2ecf20Sopenharmony_ci * 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc.
138c2ecf20Sopenharmony_ci * 30-06-1998 by Frank Denis : first step to write inodes.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/highuid.h>
208c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
218c2ecf20Sopenharmony_ci#include <linux/buffer_head.h>
228c2ecf20Sopenharmony_ci#include <linux/writeback.h>
238c2ecf20Sopenharmony_ci#include <linux/statfs.h>
248c2ecf20Sopenharmony_ci#include "qnx4.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define QNX4_VERSION  4
278c2ecf20Sopenharmony_ci#define QNX4_BMNAME   ".bitmap"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const struct super_operations qnx4_sops;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic struct inode *qnx4_alloc_inode(struct super_block *sb);
328c2ecf20Sopenharmony_cistatic void qnx4_free_inode(struct inode *inode);
338c2ecf20Sopenharmony_cistatic int qnx4_remount(struct super_block *sb, int *flags, char *data);
348c2ecf20Sopenharmony_cistatic int qnx4_statfs(struct dentry *, struct kstatfs *);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const struct super_operations qnx4_sops =
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	.alloc_inode	= qnx4_alloc_inode,
398c2ecf20Sopenharmony_ci	.free_inode	= qnx4_free_inode,
408c2ecf20Sopenharmony_ci	.statfs		= qnx4_statfs,
418c2ecf20Sopenharmony_ci	.remount_fs	= qnx4_remount,
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int qnx4_remount(struct super_block *sb, int *flags, char *data)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct qnx4_sb_info *qs;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	sync_filesystem(sb);
498c2ecf20Sopenharmony_ci	qs = qnx4_sb(sb);
508c2ecf20Sopenharmony_ci	qs->Version = QNX4_VERSION;
518c2ecf20Sopenharmony_ci	*flags |= SB_RDONLY;
528c2ecf20Sopenharmony_ci	return 0;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int qnx4_get_block( struct inode *inode, sector_t iblock, struct buffer_head *bh, int create )
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	unsigned long phys;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	QNX4DEBUG((KERN_INFO "qnx4: qnx4_get_block inode=[%ld] iblock=[%ld]\n",inode->i_ino,iblock));
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	phys = qnx4_block_map( inode, iblock );
628c2ecf20Sopenharmony_ci	if ( phys ) {
638c2ecf20Sopenharmony_ci		// logical block is before EOF
648c2ecf20Sopenharmony_ci		map_bh(bh, inode->i_sb, phys);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci	return 0;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic inline u32 try_extent(qnx4_xtnt_t *extent, u32 *offset)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	u32 size = le32_to_cpu(extent->xtnt_size);
728c2ecf20Sopenharmony_ci	if (*offset < size)
738c2ecf20Sopenharmony_ci		return le32_to_cpu(extent->xtnt_blk) + *offset - 1;
748c2ecf20Sopenharmony_ci	*offset -= size;
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ciunsigned long qnx4_block_map( struct inode *inode, long iblock )
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	int ix;
818c2ecf20Sopenharmony_ci	long i_xblk;
828c2ecf20Sopenharmony_ci	struct buffer_head *bh = NULL;
838c2ecf20Sopenharmony_ci	struct qnx4_xblk *xblk = NULL;
848c2ecf20Sopenharmony_ci	struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode);
858c2ecf20Sopenharmony_ci	u16 nxtnt = le16_to_cpu(qnx4_inode->di_num_xtnts);
868c2ecf20Sopenharmony_ci	u32 offset = iblock;
878c2ecf20Sopenharmony_ci	u32 block = try_extent(&qnx4_inode->di_first_xtnt, &offset);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (block) {
908c2ecf20Sopenharmony_ci		// iblock is in the first extent. This is easy.
918c2ecf20Sopenharmony_ci	} else {
928c2ecf20Sopenharmony_ci		// iblock is beyond first extent. We have to follow the extent chain.
938c2ecf20Sopenharmony_ci		i_xblk = le32_to_cpu(qnx4_inode->di_xblk);
948c2ecf20Sopenharmony_ci		ix = 0;
958c2ecf20Sopenharmony_ci		while ( --nxtnt > 0 ) {
968c2ecf20Sopenharmony_ci			if ( ix == 0 ) {
978c2ecf20Sopenharmony_ci				// read next xtnt block.
988c2ecf20Sopenharmony_ci				bh = sb_bread(inode->i_sb, i_xblk - 1);
998c2ecf20Sopenharmony_ci				if ( !bh ) {
1008c2ecf20Sopenharmony_ci					QNX4DEBUG((KERN_ERR "qnx4: I/O error reading xtnt block [%ld])\n", i_xblk - 1));
1018c2ecf20Sopenharmony_ci					return -EIO;
1028c2ecf20Sopenharmony_ci				}
1038c2ecf20Sopenharmony_ci				xblk = (struct qnx4_xblk*)bh->b_data;
1048c2ecf20Sopenharmony_ci				if ( memcmp( xblk->xblk_signature, "IamXblk", 7 ) ) {
1058c2ecf20Sopenharmony_ci					QNX4DEBUG((KERN_ERR "qnx4: block at %ld is not a valid xtnt\n", qnx4_inode->i_xblk));
1068c2ecf20Sopenharmony_ci					return -EIO;
1078c2ecf20Sopenharmony_ci				}
1088c2ecf20Sopenharmony_ci			}
1098c2ecf20Sopenharmony_ci			block = try_extent(&xblk->xblk_xtnts[ix], &offset);
1108c2ecf20Sopenharmony_ci			if (block) {
1118c2ecf20Sopenharmony_ci				// got it!
1128c2ecf20Sopenharmony_ci				break;
1138c2ecf20Sopenharmony_ci			}
1148c2ecf20Sopenharmony_ci			if ( ++ix >= xblk->xblk_num_xtnts ) {
1158c2ecf20Sopenharmony_ci				i_xblk = le32_to_cpu(xblk->xblk_next_xblk);
1168c2ecf20Sopenharmony_ci				ix = 0;
1178c2ecf20Sopenharmony_ci				brelse( bh );
1188c2ecf20Sopenharmony_ci				bh = NULL;
1198c2ecf20Sopenharmony_ci			}
1208c2ecf20Sopenharmony_ci		}
1218c2ecf20Sopenharmony_ci		if ( bh )
1228c2ecf20Sopenharmony_ci			brelse( bh );
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	QNX4DEBUG((KERN_INFO "qnx4: mapping block %ld of inode %ld = %ld\n",iblock,inode->i_ino,block));
1268c2ecf20Sopenharmony_ci	return block;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int qnx4_statfs(struct dentry *dentry, struct kstatfs *buf)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct super_block *sb = dentry->d_sb;
1328c2ecf20Sopenharmony_ci	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	buf->f_type    = sb->s_magic;
1358c2ecf20Sopenharmony_ci	buf->f_bsize   = sb->s_blocksize;
1368c2ecf20Sopenharmony_ci	buf->f_blocks  = le32_to_cpu(qnx4_sb(sb)->BitMap->di_size) * 8;
1378c2ecf20Sopenharmony_ci	buf->f_bfree   = qnx4_count_free_blocks(sb);
1388c2ecf20Sopenharmony_ci	buf->f_bavail  = buf->f_bfree;
1398c2ecf20Sopenharmony_ci	buf->f_namelen = QNX4_NAME_MAX;
1408c2ecf20Sopenharmony_ci	buf->f_fsid    = u64_to_fsid(id);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci * Check the root directory of the filesystem to make sure
1478c2ecf20Sopenharmony_ci * it really _is_ a qnx4 filesystem, and to check the size
1488c2ecf20Sopenharmony_ci * of the directory entry.
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_cistatic const char *qnx4_checkroot(struct super_block *sb,
1518c2ecf20Sopenharmony_ci				  struct qnx4_super_block *s)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct buffer_head *bh;
1548c2ecf20Sopenharmony_ci	struct qnx4_inode_entry *rootdir;
1558c2ecf20Sopenharmony_ci	int rd, rl;
1568c2ecf20Sopenharmony_ci	int i, j;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (s->RootDir.di_fname[0] != '/' || s->RootDir.di_fname[1] != '\0')
1598c2ecf20Sopenharmony_ci		return "no qnx4 filesystem (no root dir).";
1608c2ecf20Sopenharmony_ci	QNX4DEBUG((KERN_NOTICE "QNX4 filesystem found on dev %s.\n", sb->s_id));
1618c2ecf20Sopenharmony_ci	rd = le32_to_cpu(s->RootDir.di_first_xtnt.xtnt_blk) - 1;
1628c2ecf20Sopenharmony_ci	rl = le32_to_cpu(s->RootDir.di_first_xtnt.xtnt_size);
1638c2ecf20Sopenharmony_ci	for (j = 0; j < rl; j++) {
1648c2ecf20Sopenharmony_ci		bh = sb_bread(sb, rd + j);	/* root dir, first block */
1658c2ecf20Sopenharmony_ci		if (bh == NULL)
1668c2ecf20Sopenharmony_ci			return "unable to read root entry.";
1678c2ecf20Sopenharmony_ci		rootdir = (struct qnx4_inode_entry *) bh->b_data;
1688c2ecf20Sopenharmony_ci		for (i = 0; i < QNX4_INODES_PER_BLOCK; i++, rootdir++) {
1698c2ecf20Sopenharmony_ci			QNX4DEBUG((KERN_INFO "rootdir entry found : [%s]\n", rootdir->di_fname));
1708c2ecf20Sopenharmony_ci			if (strcmp(rootdir->di_fname, QNX4_BMNAME) != 0)
1718c2ecf20Sopenharmony_ci				continue;
1728c2ecf20Sopenharmony_ci			qnx4_sb(sb)->BitMap = kmemdup(rootdir,
1738c2ecf20Sopenharmony_ci						      sizeof(struct qnx4_inode_entry),
1748c2ecf20Sopenharmony_ci						      GFP_KERNEL);
1758c2ecf20Sopenharmony_ci			brelse(bh);
1768c2ecf20Sopenharmony_ci			if (!qnx4_sb(sb)->BitMap)
1778c2ecf20Sopenharmony_ci				return "not enough memory for bitmap inode";
1788c2ecf20Sopenharmony_ci			/* keep bitmap inode known */
1798c2ecf20Sopenharmony_ci			return NULL;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci		brelse(bh);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	return "bitmap file not found.";
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int qnx4_fill_super(struct super_block *s, void *data, int silent)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct buffer_head *bh;
1898c2ecf20Sopenharmony_ci	struct inode *root;
1908c2ecf20Sopenharmony_ci	const char *errmsg;
1918c2ecf20Sopenharmony_ci	struct qnx4_sb_info *qs;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	qs = kzalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL);
1948c2ecf20Sopenharmony_ci	if (!qs)
1958c2ecf20Sopenharmony_ci		return -ENOMEM;
1968c2ecf20Sopenharmony_ci	s->s_fs_info = qs;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	sb_set_blocksize(s, QNX4_BLOCK_SIZE);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	s->s_op = &qnx4_sops;
2018c2ecf20Sopenharmony_ci	s->s_magic = QNX4_SUPER_MAGIC;
2028c2ecf20Sopenharmony_ci	s->s_flags |= SB_RDONLY;	/* Yup, read-only yet */
2038c2ecf20Sopenharmony_ci	s->s_time_min = 0;
2048c2ecf20Sopenharmony_ci	s->s_time_max = U32_MAX;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Check the superblock signature. Since the qnx4 code is
2078c2ecf20Sopenharmony_ci	   dangerous, we should leave as quickly as possible
2088c2ecf20Sopenharmony_ci	   if we don't belong here... */
2098c2ecf20Sopenharmony_ci	bh = sb_bread(s, 1);
2108c2ecf20Sopenharmony_ci	if (!bh) {
2118c2ecf20Sopenharmony_ci		printk(KERN_ERR "qnx4: unable to read the superblock\n");
2128c2ecf20Sopenharmony_ci		return -EINVAL;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci 	/* check before allocating dentries, inodes, .. */
2168c2ecf20Sopenharmony_ci	errmsg = qnx4_checkroot(s, (struct qnx4_super_block *) bh->b_data);
2178c2ecf20Sopenharmony_ci	brelse(bh);
2188c2ecf20Sopenharmony_ci	if (errmsg != NULL) {
2198c2ecf20Sopenharmony_ci 		if (!silent)
2208c2ecf20Sopenharmony_ci			printk(KERN_ERR "qnx4: %s\n", errmsg);
2218c2ecf20Sopenharmony_ci		return -EINVAL;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci 	/* does root not have inode number QNX4_ROOT_INO ?? */
2258c2ecf20Sopenharmony_ci	root = qnx4_iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK);
2268c2ecf20Sopenharmony_ci	if (IS_ERR(root)) {
2278c2ecf20Sopenharmony_ci		printk(KERN_ERR "qnx4: get inode failed\n");
2288c2ecf20Sopenharmony_ci		return PTR_ERR(root);
2298c2ecf20Sopenharmony_ci 	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci 	s->s_root = d_make_root(root);
2328c2ecf20Sopenharmony_ci 	if (s->s_root == NULL)
2338c2ecf20Sopenharmony_ci 		return -ENOMEM;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic void qnx4_kill_sb(struct super_block *sb)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct qnx4_sb_info *qs = qnx4_sb(sb);
2418c2ecf20Sopenharmony_ci	kill_block_super(sb);
2428c2ecf20Sopenharmony_ci	if (qs) {
2438c2ecf20Sopenharmony_ci		kfree(qs->BitMap);
2448c2ecf20Sopenharmony_ci		kfree(qs);
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int qnx4_readpage(struct file *file, struct page *page)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	return block_read_full_page(page,qnx4_get_block);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic sector_t qnx4_bmap(struct address_space *mapping, sector_t block)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	return generic_block_bmap(mapping,block,qnx4_get_block);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_cistatic const struct address_space_operations qnx4_aops = {
2588c2ecf20Sopenharmony_ci	.readpage	= qnx4_readpage,
2598c2ecf20Sopenharmony_ci	.bmap		= qnx4_bmap
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistruct inode *qnx4_iget(struct super_block *sb, unsigned long ino)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct buffer_head *bh;
2658c2ecf20Sopenharmony_ci	struct qnx4_inode_entry *raw_inode;
2668c2ecf20Sopenharmony_ci	int block;
2678c2ecf20Sopenharmony_ci	struct qnx4_inode_entry *qnx4_inode;
2688c2ecf20Sopenharmony_ci	struct inode *inode;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	inode = iget_locked(sb, ino);
2718c2ecf20Sopenharmony_ci	if (!inode)
2728c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2738c2ecf20Sopenharmony_ci	if (!(inode->i_state & I_NEW))
2748c2ecf20Sopenharmony_ci		return inode;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	qnx4_inode = qnx4_raw_inode(inode);
2778c2ecf20Sopenharmony_ci	inode->i_mode = 0;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	QNX4DEBUG((KERN_INFO "reading inode : [%d]\n", ino));
2808c2ecf20Sopenharmony_ci	if (!ino) {
2818c2ecf20Sopenharmony_ci		printk(KERN_ERR "qnx4: bad inode number on dev %s: %lu is "
2828c2ecf20Sopenharmony_ci				"out of range\n",
2838c2ecf20Sopenharmony_ci		       sb->s_id, ino);
2848c2ecf20Sopenharmony_ci		iget_failed(inode);
2858c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci	block = ino / QNX4_INODES_PER_BLOCK;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!(bh = sb_bread(sb, block))) {
2908c2ecf20Sopenharmony_ci		printk(KERN_ERR "qnx4: major problem: unable to read inode from dev "
2918c2ecf20Sopenharmony_ci		       "%s\n", sb->s_id);
2928c2ecf20Sopenharmony_ci		iget_failed(inode);
2938c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci	raw_inode = ((struct qnx4_inode_entry *) bh->b_data) +
2968c2ecf20Sopenharmony_ci	    (ino % QNX4_INODES_PER_BLOCK);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	inode->i_mode    = le16_to_cpu(raw_inode->di_mode);
2998c2ecf20Sopenharmony_ci	i_uid_write(inode, (uid_t)le16_to_cpu(raw_inode->di_uid));
3008c2ecf20Sopenharmony_ci	i_gid_write(inode, (gid_t)le16_to_cpu(raw_inode->di_gid));
3018c2ecf20Sopenharmony_ci	set_nlink(inode, le16_to_cpu(raw_inode->di_nlink));
3028c2ecf20Sopenharmony_ci	inode->i_size    = le32_to_cpu(raw_inode->di_size);
3038c2ecf20Sopenharmony_ci	inode->i_mtime.tv_sec   = le32_to_cpu(raw_inode->di_mtime);
3048c2ecf20Sopenharmony_ci	inode->i_mtime.tv_nsec = 0;
3058c2ecf20Sopenharmony_ci	inode->i_atime.tv_sec   = le32_to_cpu(raw_inode->di_atime);
3068c2ecf20Sopenharmony_ci	inode->i_atime.tv_nsec = 0;
3078c2ecf20Sopenharmony_ci	inode->i_ctime.tv_sec   = le32_to_cpu(raw_inode->di_ctime);
3088c2ecf20Sopenharmony_ci	inode->i_ctime.tv_nsec = 0;
3098c2ecf20Sopenharmony_ci	inode->i_blocks  = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	memcpy(qnx4_inode, raw_inode, QNX4_DIR_ENTRY_SIZE);
3128c2ecf20Sopenharmony_ci	if (S_ISREG(inode->i_mode)) {
3138c2ecf20Sopenharmony_ci		inode->i_fop = &generic_ro_fops;
3148c2ecf20Sopenharmony_ci		inode->i_mapping->a_ops = &qnx4_aops;
3158c2ecf20Sopenharmony_ci		qnx4_i(inode)->mmu_private = inode->i_size;
3168c2ecf20Sopenharmony_ci	} else if (S_ISDIR(inode->i_mode)) {
3178c2ecf20Sopenharmony_ci		inode->i_op = &qnx4_dir_inode_operations;
3188c2ecf20Sopenharmony_ci		inode->i_fop = &qnx4_dir_operations;
3198c2ecf20Sopenharmony_ci	} else if (S_ISLNK(inode->i_mode)) {
3208c2ecf20Sopenharmony_ci		inode->i_op = &page_symlink_inode_operations;
3218c2ecf20Sopenharmony_ci		inode_nohighmem(inode);
3228c2ecf20Sopenharmony_ci		inode->i_mapping->a_ops = &qnx4_aops;
3238c2ecf20Sopenharmony_ci		qnx4_i(inode)->mmu_private = inode->i_size;
3248c2ecf20Sopenharmony_ci	} else {
3258c2ecf20Sopenharmony_ci		printk(KERN_ERR "qnx4: bad inode %lu on dev %s\n",
3268c2ecf20Sopenharmony_ci			ino, sb->s_id);
3278c2ecf20Sopenharmony_ci		iget_failed(inode);
3288c2ecf20Sopenharmony_ci		brelse(bh);
3298c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci	brelse(bh);
3328c2ecf20Sopenharmony_ci	unlock_new_inode(inode);
3338c2ecf20Sopenharmony_ci	return inode;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic struct kmem_cache *qnx4_inode_cachep;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic struct inode *qnx4_alloc_inode(struct super_block *sb)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct qnx4_inode_info *ei;
3418c2ecf20Sopenharmony_ci	ei = kmem_cache_alloc(qnx4_inode_cachep, GFP_KERNEL);
3428c2ecf20Sopenharmony_ci	if (!ei)
3438c2ecf20Sopenharmony_ci		return NULL;
3448c2ecf20Sopenharmony_ci	return &ei->vfs_inode;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void qnx4_free_inode(struct inode *inode)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	kmem_cache_free(qnx4_inode_cachep, qnx4_i(inode));
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic void init_once(void *foo)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct qnx4_inode_info *ei = (struct qnx4_inode_info *) foo;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	inode_init_once(&ei->vfs_inode);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic int init_inodecache(void)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache",
3628c2ecf20Sopenharmony_ci					     sizeof(struct qnx4_inode_info),
3638c2ecf20Sopenharmony_ci					     0, (SLAB_RECLAIM_ACCOUNT|
3648c2ecf20Sopenharmony_ci						SLAB_MEM_SPREAD|SLAB_ACCOUNT),
3658c2ecf20Sopenharmony_ci					     init_once);
3668c2ecf20Sopenharmony_ci	if (qnx4_inode_cachep == NULL)
3678c2ecf20Sopenharmony_ci		return -ENOMEM;
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic void destroy_inodecache(void)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	/*
3748c2ecf20Sopenharmony_ci	 * Make sure all delayed rcu free inodes are flushed before we
3758c2ecf20Sopenharmony_ci	 * destroy cache.
3768c2ecf20Sopenharmony_ci	 */
3778c2ecf20Sopenharmony_ci	rcu_barrier();
3788c2ecf20Sopenharmony_ci	kmem_cache_destroy(qnx4_inode_cachep);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic struct dentry *qnx4_mount(struct file_system_type *fs_type,
3828c2ecf20Sopenharmony_ci	int flags, const char *dev_name, void *data)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	return mount_bdev(fs_type, flags, dev_name, data, qnx4_fill_super);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic struct file_system_type qnx4_fs_type = {
3888c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
3898c2ecf20Sopenharmony_ci	.name		= "qnx4",
3908c2ecf20Sopenharmony_ci	.mount		= qnx4_mount,
3918c2ecf20Sopenharmony_ci	.kill_sb	= qnx4_kill_sb,
3928c2ecf20Sopenharmony_ci	.fs_flags	= FS_REQUIRES_DEV,
3938c2ecf20Sopenharmony_ci};
3948c2ecf20Sopenharmony_ciMODULE_ALIAS_FS("qnx4");
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int __init init_qnx4_fs(void)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	int err;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	err = init_inodecache();
4018c2ecf20Sopenharmony_ci	if (err)
4028c2ecf20Sopenharmony_ci		return err;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	err = register_filesystem(&qnx4_fs_type);
4058c2ecf20Sopenharmony_ci	if (err) {
4068c2ecf20Sopenharmony_ci		destroy_inodecache();
4078c2ecf20Sopenharmony_ci		return err;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	printk(KERN_INFO "QNX4 filesystem 0.2.3 registered.\n");
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic void __exit exit_qnx4_fs(void)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	unregister_filesystem(&qnx4_fs_type);
4178c2ecf20Sopenharmony_ci	destroy_inodecache();
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cimodule_init(init_qnx4_fs)
4218c2ecf20Sopenharmony_cimodule_exit(exit_qnx4_fs)
4228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4238c2ecf20Sopenharmony_ci
424