15d026c72SKai Bankett /* 25d026c72SKai Bankett * QNX6 file system, Linux implementation. 35d026c72SKai Bankett * 45d026c72SKai Bankett * Version : 1.0.0 55d026c72SKai Bankett * 65d026c72SKai Bankett * History : 75d026c72SKai Bankett * 85d026c72SKai Bankett * 01-02-2012 by Kai Bankett (chaosman@ontika.net) : first release. 95d026c72SKai Bankett * 16-02-2012 pagemap extension by Al Viro 105d026c72SKai Bankett * 115d026c72SKai Bankett */ 125d026c72SKai Bankett 135d026c72SKai Bankett #include <linux/module.h> 145d026c72SKai Bankett #include <linux/init.h> 155d026c72SKai Bankett #include <linux/slab.h> 165d026c72SKai Bankett #include <linux/highuid.h> 175d026c72SKai Bankett #include <linux/pagemap.h> 185d026c72SKai Bankett #include <linux/buffer_head.h> 195d026c72SKai Bankett #include <linux/writeback.h> 205d026c72SKai Bankett #include <linux/statfs.h> 215d026c72SKai Bankett #include <linux/parser.h> 225d026c72SKai Bankett #include <linux/seq_file.h> 235d026c72SKai Bankett #include <linux/mount.h> 245d026c72SKai Bankett #include <linux/crc32.h> 255d026c72SKai Bankett #include <linux/mpage.h> 265d026c72SKai Bankett #include "qnx6.h" 275d026c72SKai Bankett 285d026c72SKai Bankett static const struct super_operations qnx6_sops; 295d026c72SKai Bankett 305d026c72SKai Bankett static void qnx6_put_super(struct super_block *sb); 315d026c72SKai Bankett static struct inode *qnx6_alloc_inode(struct super_block *sb); 325d026c72SKai Bankett static void qnx6_destroy_inode(struct inode *inode); 335d026c72SKai Bankett static int qnx6_remount(struct super_block *sb, int *flags, char *data); 345d026c72SKai Bankett static int qnx6_statfs(struct dentry *dentry, struct kstatfs *buf); 355d026c72SKai Bankett static int qnx6_show_options(struct seq_file *seq, struct dentry *root); 365d026c72SKai Bankett 375d026c72SKai Bankett static const struct super_operations qnx6_sops = { 385d026c72SKai Bankett .alloc_inode = qnx6_alloc_inode, 395d026c72SKai Bankett .destroy_inode = qnx6_destroy_inode, 405d026c72SKai Bankett .put_super = qnx6_put_super, 415d026c72SKai Bankett .statfs = qnx6_statfs, 425d026c72SKai Bankett .remount_fs = qnx6_remount, 435d026c72SKai Bankett .show_options = qnx6_show_options, 445d026c72SKai Bankett }; 455d026c72SKai Bankett 465d026c72SKai Bankett static int qnx6_show_options(struct seq_file *seq, struct dentry *root) 475d026c72SKai Bankett { 485d026c72SKai Bankett struct super_block *sb = root->d_sb; 495d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(sb); 505d026c72SKai Bankett 515d026c72SKai Bankett if (sbi->s_mount_opt & QNX6_MOUNT_MMI_FS) 525d026c72SKai Bankett seq_puts(seq, ",mmi_fs"); 535d026c72SKai Bankett return 0; 545d026c72SKai Bankett } 555d026c72SKai Bankett 565d026c72SKai Bankett static int qnx6_remount(struct super_block *sb, int *flags, char *data) 575d026c72SKai Bankett { 5802b9984dSTheodore Ts'o sync_filesystem(sb); 595d026c72SKai Bankett *flags |= MS_RDONLY; 605d026c72SKai Bankett return 0; 615d026c72SKai Bankett } 625d026c72SKai Bankett 635d026c72SKai Bankett static unsigned qnx6_get_devblock(struct super_block *sb, __fs32 block) 645d026c72SKai Bankett { 655d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(sb); 665d026c72SKai Bankett return fs32_to_cpu(sbi, block) + sbi->s_blks_off; 675d026c72SKai Bankett } 685d026c72SKai Bankett 695d026c72SKai Bankett static unsigned qnx6_block_map(struct inode *inode, unsigned iblock); 705d026c72SKai Bankett 715d026c72SKai Bankett static int qnx6_get_block(struct inode *inode, sector_t iblock, 725d026c72SKai Bankett struct buffer_head *bh, int create) 735d026c72SKai Bankett { 745d026c72SKai Bankett unsigned phys; 755d026c72SKai Bankett 765d026c72SKai Bankett QNX6DEBUG((KERN_INFO "qnx6: qnx6_get_block inode=[%ld] iblock=[%ld]\n", 775d026c72SKai Bankett inode->i_ino, (unsigned long)iblock)); 785d026c72SKai Bankett 795d026c72SKai Bankett phys = qnx6_block_map(inode, iblock); 805d026c72SKai Bankett if (phys) { 815d026c72SKai Bankett /* logical block is before EOF */ 825d026c72SKai Bankett map_bh(bh, inode->i_sb, phys); 835d026c72SKai Bankett } 845d026c72SKai Bankett return 0; 855d026c72SKai Bankett } 865d026c72SKai Bankett 875d026c72SKai Bankett static int qnx6_check_blockptr(__fs32 ptr) 885d026c72SKai Bankett { 895d026c72SKai Bankett if (ptr == ~(__fs32)0) { 905d026c72SKai Bankett printk(KERN_ERR "qnx6: hit unused blockpointer.\n"); 915d026c72SKai Bankett return 0; 925d026c72SKai Bankett } 935d026c72SKai Bankett return 1; 945d026c72SKai Bankett } 955d026c72SKai Bankett 965d026c72SKai Bankett static int qnx6_readpage(struct file *file, struct page *page) 975d026c72SKai Bankett { 985d026c72SKai Bankett return mpage_readpage(page, qnx6_get_block); 995d026c72SKai Bankett } 1005d026c72SKai Bankett 1015d026c72SKai Bankett static int qnx6_readpages(struct file *file, struct address_space *mapping, 1025d026c72SKai Bankett struct list_head *pages, unsigned nr_pages) 1035d026c72SKai Bankett { 1045d026c72SKai Bankett return mpage_readpages(mapping, pages, nr_pages, qnx6_get_block); 1055d026c72SKai Bankett } 1065d026c72SKai Bankett 1075d026c72SKai Bankett /* 1085d026c72SKai Bankett * returns the block number for the no-th element in the tree 1095d026c72SKai Bankett * inodebits requred as there are multiple inodes in one inode block 1105d026c72SKai Bankett */ 1115d026c72SKai Bankett static unsigned qnx6_block_map(struct inode *inode, unsigned no) 1125d026c72SKai Bankett { 1135d026c72SKai Bankett struct super_block *s = inode->i_sb; 1145d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(s); 1155d026c72SKai Bankett struct qnx6_inode_info *ei = QNX6_I(inode); 1165d026c72SKai Bankett unsigned block = 0; 1175d026c72SKai Bankett struct buffer_head *bh; 1185d026c72SKai Bankett __fs32 ptr; 1195d026c72SKai Bankett int levelptr; 1205d026c72SKai Bankett int ptrbits = sbi->s_ptrbits; 1215d026c72SKai Bankett int bitdelta; 1225d026c72SKai Bankett u32 mask = (1 << ptrbits) - 1; 1235d026c72SKai Bankett int depth = ei->di_filelevels; 1245d026c72SKai Bankett int i; 1255d026c72SKai Bankett 1265d026c72SKai Bankett bitdelta = ptrbits * depth; 1275d026c72SKai Bankett levelptr = no >> bitdelta; 1285d026c72SKai Bankett 1295d026c72SKai Bankett if (levelptr > QNX6_NO_DIRECT_POINTERS - 1) { 1305d026c72SKai Bankett printk(KERN_ERR "qnx6:Requested file block number (%u) too big.", 1315d026c72SKai Bankett no); 1325d026c72SKai Bankett return 0; 1335d026c72SKai Bankett } 1345d026c72SKai Bankett 1355d026c72SKai Bankett block = qnx6_get_devblock(s, ei->di_block_ptr[levelptr]); 1365d026c72SKai Bankett 1375d026c72SKai Bankett for (i = 0; i < depth; i++) { 1385d026c72SKai Bankett bh = sb_bread(s, block); 1395d026c72SKai Bankett if (!bh) { 1405d026c72SKai Bankett printk(KERN_ERR "qnx6:Error reading block (%u)\n", 1415d026c72SKai Bankett block); 1425d026c72SKai Bankett return 0; 1435d026c72SKai Bankett } 1445d026c72SKai Bankett bitdelta -= ptrbits; 1455d026c72SKai Bankett levelptr = (no >> bitdelta) & mask; 1465d026c72SKai Bankett ptr = ((__fs32 *)bh->b_data)[levelptr]; 1475d026c72SKai Bankett 1485d026c72SKai Bankett if (!qnx6_check_blockptr(ptr)) 1495d026c72SKai Bankett return 0; 1505d026c72SKai Bankett 1515d026c72SKai Bankett block = qnx6_get_devblock(s, ptr); 1525d026c72SKai Bankett brelse(bh); 1535d026c72SKai Bankett } 1545d026c72SKai Bankett return block; 1555d026c72SKai Bankett } 1565d026c72SKai Bankett 1575d026c72SKai Bankett static int qnx6_statfs(struct dentry *dentry, struct kstatfs *buf) 1585d026c72SKai Bankett { 1595d026c72SKai Bankett struct super_block *sb = dentry->d_sb; 1605d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(sb); 1615d026c72SKai Bankett u64 id = huge_encode_dev(sb->s_bdev->bd_dev); 1625d026c72SKai Bankett 1635d026c72SKai Bankett buf->f_type = sb->s_magic; 1645d026c72SKai Bankett buf->f_bsize = sb->s_blocksize; 1655d026c72SKai Bankett buf->f_blocks = fs32_to_cpu(sbi, sbi->sb->sb_num_blocks); 1665d026c72SKai Bankett buf->f_bfree = fs32_to_cpu(sbi, sbi->sb->sb_free_blocks); 1675d026c72SKai Bankett buf->f_files = fs32_to_cpu(sbi, sbi->sb->sb_num_inodes); 1685d026c72SKai Bankett buf->f_ffree = fs32_to_cpu(sbi, sbi->sb->sb_free_inodes); 1695d026c72SKai Bankett buf->f_bavail = buf->f_bfree; 1705d026c72SKai Bankett buf->f_namelen = QNX6_LONG_NAME_MAX; 1715d026c72SKai Bankett buf->f_fsid.val[0] = (u32)id; 1725d026c72SKai Bankett buf->f_fsid.val[1] = (u32)(id >> 32); 1735d026c72SKai Bankett 1745d026c72SKai Bankett return 0; 1755d026c72SKai Bankett } 1765d026c72SKai Bankett 1775d026c72SKai Bankett /* 1785d026c72SKai Bankett * Check the root directory of the filesystem to make sure 1795d026c72SKai Bankett * it really _is_ a qnx6 filesystem, and to check the size 1805d026c72SKai Bankett * of the directory entry. 1815d026c72SKai Bankett */ 1825d026c72SKai Bankett static const char *qnx6_checkroot(struct super_block *s) 1835d026c72SKai Bankett { 1845d026c72SKai Bankett static char match_root[2][3] = {".\0\0", "..\0"}; 1855d026c72SKai Bankett int i, error = 0; 1865d026c72SKai Bankett struct qnx6_dir_entry *dir_entry; 1875d026c72SKai Bankett struct inode *root = s->s_root->d_inode; 1885d026c72SKai Bankett struct address_space *mapping = root->i_mapping; 1895d026c72SKai Bankett struct page *page = read_mapping_page(mapping, 0, NULL); 1905d026c72SKai Bankett if (IS_ERR(page)) 1915d026c72SKai Bankett return "error reading root directory"; 1925d026c72SKai Bankett kmap(page); 1935d026c72SKai Bankett dir_entry = page_address(page); 1945d026c72SKai Bankett for (i = 0; i < 2; i++) { 1955d026c72SKai Bankett /* maximum 3 bytes - due to match_root limitation */ 1965d026c72SKai Bankett if (strncmp(dir_entry[i].de_fname, match_root[i], 3)) 1975d026c72SKai Bankett error = 1; 1985d026c72SKai Bankett } 1995d026c72SKai Bankett qnx6_put_page(page); 2005d026c72SKai Bankett if (error) 2015d026c72SKai Bankett return "error reading root directory."; 2025d026c72SKai Bankett return NULL; 2035d026c72SKai Bankett } 2045d026c72SKai Bankett 2055d026c72SKai Bankett #ifdef CONFIG_QNX6FS_DEBUG 2065d026c72SKai Bankett void qnx6_superblock_debug(struct qnx6_super_block *sb, struct super_block *s) 2075d026c72SKai Bankett { 2085d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(s); 2095d026c72SKai Bankett 2105d026c72SKai Bankett QNX6DEBUG((KERN_INFO "magic: %08x\n", 2115d026c72SKai Bankett fs32_to_cpu(sbi, sb->sb_magic))); 2125d026c72SKai Bankett QNX6DEBUG((KERN_INFO "checksum: %08x\n", 2135d026c72SKai Bankett fs32_to_cpu(sbi, sb->sb_checksum))); 2145d026c72SKai Bankett QNX6DEBUG((KERN_INFO "serial: %llx\n", 2155d026c72SKai Bankett fs64_to_cpu(sbi, sb->sb_serial))); 2165d026c72SKai Bankett QNX6DEBUG((KERN_INFO "flags: %08x\n", 2175d026c72SKai Bankett fs32_to_cpu(sbi, sb->sb_flags))); 2185d026c72SKai Bankett QNX6DEBUG((KERN_INFO "blocksize: %08x\n", 2195d026c72SKai Bankett fs32_to_cpu(sbi, sb->sb_blocksize))); 2205d026c72SKai Bankett QNX6DEBUG((KERN_INFO "num_inodes: %08x\n", 2215d026c72SKai Bankett fs32_to_cpu(sbi, sb->sb_num_inodes))); 2225d026c72SKai Bankett QNX6DEBUG((KERN_INFO "free_inodes: %08x\n", 2235d026c72SKai Bankett fs32_to_cpu(sbi, sb->sb_free_inodes))); 2245d026c72SKai Bankett QNX6DEBUG((KERN_INFO "num_blocks: %08x\n", 2255d026c72SKai Bankett fs32_to_cpu(sbi, sb->sb_num_blocks))); 2265d026c72SKai Bankett QNX6DEBUG((KERN_INFO "free_blocks: %08x\n", 2275d026c72SKai Bankett fs32_to_cpu(sbi, sb->sb_free_blocks))); 2285d026c72SKai Bankett QNX6DEBUG((KERN_INFO "inode_levels: %02x\n", 2295d026c72SKai Bankett sb->Inode.levels)); 2305d026c72SKai Bankett } 2315d026c72SKai Bankett #endif 2325d026c72SKai Bankett 2335d026c72SKai Bankett enum { 2345d026c72SKai Bankett Opt_mmifs, 2355d026c72SKai Bankett Opt_err 2365d026c72SKai Bankett }; 2375d026c72SKai Bankett 2385d026c72SKai Bankett static const match_table_t tokens = { 2395d026c72SKai Bankett {Opt_mmifs, "mmi_fs"}, 2405d026c72SKai Bankett {Opt_err, NULL} 2415d026c72SKai Bankett }; 2425d026c72SKai Bankett 2435d026c72SKai Bankett static int qnx6_parse_options(char *options, struct super_block *sb) 2445d026c72SKai Bankett { 2455d026c72SKai Bankett char *p; 2465d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(sb); 2475d026c72SKai Bankett substring_t args[MAX_OPT_ARGS]; 2485d026c72SKai Bankett 2495d026c72SKai Bankett if (!options) 2505d026c72SKai Bankett return 1; 2515d026c72SKai Bankett 2525d026c72SKai Bankett while ((p = strsep(&options, ",")) != NULL) { 2535d026c72SKai Bankett int token; 2545d026c72SKai Bankett if (!*p) 2555d026c72SKai Bankett continue; 2565d026c72SKai Bankett 2575d026c72SKai Bankett token = match_token(p, tokens, args); 2585d026c72SKai Bankett switch (token) { 2595d026c72SKai Bankett case Opt_mmifs: 2605d026c72SKai Bankett set_opt(sbi->s_mount_opt, MMI_FS); 2615d026c72SKai Bankett break; 2625d026c72SKai Bankett default: 2635d026c72SKai Bankett return 0; 2645d026c72SKai Bankett } 2655d026c72SKai Bankett } 2665d026c72SKai Bankett return 1; 2675d026c72SKai Bankett } 2685d026c72SKai Bankett 2695d026c72SKai Bankett static struct buffer_head *qnx6_check_first_superblock(struct super_block *s, 2705d026c72SKai Bankett int offset, int silent) 2715d026c72SKai Bankett { 2725d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(s); 2735d026c72SKai Bankett struct buffer_head *bh; 2745d026c72SKai Bankett struct qnx6_super_block *sb; 2755d026c72SKai Bankett 2765d026c72SKai Bankett /* Check the superblock signatures 2775d026c72SKai Bankett start with the first superblock */ 2785d026c72SKai Bankett bh = sb_bread(s, offset); 2795d026c72SKai Bankett if (!bh) { 2805d026c72SKai Bankett printk(KERN_ERR "qnx6: unable to read the first superblock\n"); 2815d026c72SKai Bankett return NULL; 2825d026c72SKai Bankett } 2835d026c72SKai Bankett sb = (struct qnx6_super_block *)bh->b_data; 2845d026c72SKai Bankett if (fs32_to_cpu(sbi, sb->sb_magic) != QNX6_SUPER_MAGIC) { 2855d026c72SKai Bankett sbi->s_bytesex = BYTESEX_BE; 2865d026c72SKai Bankett if (fs32_to_cpu(sbi, sb->sb_magic) == QNX6_SUPER_MAGIC) { 2875d026c72SKai Bankett /* we got a big endian fs */ 2885d026c72SKai Bankett QNX6DEBUG((KERN_INFO "qnx6: fs got different" 2898a168ca7SMasanari Iida " endianness.\n")); 2905d026c72SKai Bankett return bh; 2915d026c72SKai Bankett } else 2925d026c72SKai Bankett sbi->s_bytesex = BYTESEX_LE; 2935d026c72SKai Bankett if (!silent) { 2945d026c72SKai Bankett if (offset == 0) { 2955d026c72SKai Bankett printk(KERN_ERR "qnx6: wrong signature (magic)" 2965d026c72SKai Bankett " in superblock #1.\n"); 2975d026c72SKai Bankett } else { 2985d026c72SKai Bankett printk(KERN_INFO "qnx6: wrong signature (magic)" 2995d026c72SKai Bankett " at position (0x%lx) - will try" 3005d026c72SKai Bankett " alternative position (0x0000).\n", 3015d026c72SKai Bankett offset * s->s_blocksize); 3025d026c72SKai Bankett } 3035d026c72SKai Bankett } 3045d026c72SKai Bankett brelse(bh); 3055d026c72SKai Bankett return NULL; 3065d026c72SKai Bankett } 3075d026c72SKai Bankett return bh; 3085d026c72SKai Bankett } 3095d026c72SKai Bankett 3105d026c72SKai Bankett static struct inode *qnx6_private_inode(struct super_block *s, 3115d026c72SKai Bankett struct qnx6_root_node *p); 3125d026c72SKai Bankett 3135d026c72SKai Bankett static int qnx6_fill_super(struct super_block *s, void *data, int silent) 3145d026c72SKai Bankett { 3155d026c72SKai Bankett struct buffer_head *bh1 = NULL, *bh2 = NULL; 3165d026c72SKai Bankett struct qnx6_super_block *sb1 = NULL, *sb2 = NULL; 3175d026c72SKai Bankett struct qnx6_sb_info *sbi; 3185d026c72SKai Bankett struct inode *root; 3195d026c72SKai Bankett const char *errmsg; 3205d026c72SKai Bankett struct qnx6_sb_info *qs; 3215d026c72SKai Bankett int ret = -EINVAL; 3225d026c72SKai Bankett u64 offset; 3235d026c72SKai Bankett int bootblock_offset = QNX6_BOOTBLOCK_SIZE; 3245d026c72SKai Bankett 3255d026c72SKai Bankett qs = kzalloc(sizeof(struct qnx6_sb_info), GFP_KERNEL); 3265d026c72SKai Bankett if (!qs) 3275d026c72SKai Bankett return -ENOMEM; 3285d026c72SKai Bankett s->s_fs_info = qs; 3295d026c72SKai Bankett 3305d026c72SKai Bankett /* Superblock always is 512 Byte long */ 3315d026c72SKai Bankett if (!sb_set_blocksize(s, QNX6_SUPERBLOCK_SIZE)) { 3325d026c72SKai Bankett printk(KERN_ERR "qnx6: unable to set blocksize\n"); 3335d026c72SKai Bankett goto outnobh; 3345d026c72SKai Bankett } 3355d026c72SKai Bankett 3365d026c72SKai Bankett /* parse the mount-options */ 3375d026c72SKai Bankett if (!qnx6_parse_options((char *) data, s)) { 3385d026c72SKai Bankett printk(KERN_ERR "qnx6: invalid mount options.\n"); 3395d026c72SKai Bankett goto outnobh; 3405d026c72SKai Bankett } 3415d026c72SKai Bankett if (test_opt(s, MMI_FS)) { 3425d026c72SKai Bankett sb1 = qnx6_mmi_fill_super(s, silent); 3435d026c72SKai Bankett if (sb1) 3445d026c72SKai Bankett goto mmi_success; 3455d026c72SKai Bankett else 3465d026c72SKai Bankett goto outnobh; 3475d026c72SKai Bankett } 3485d026c72SKai Bankett sbi = QNX6_SB(s); 3495d026c72SKai Bankett sbi->s_bytesex = BYTESEX_LE; 3505d026c72SKai Bankett /* Check the superblock signatures 3515d026c72SKai Bankett start with the first superblock */ 3525d026c72SKai Bankett bh1 = qnx6_check_first_superblock(s, 3535d026c72SKai Bankett bootblock_offset / QNX6_SUPERBLOCK_SIZE, silent); 3545d026c72SKai Bankett if (!bh1) { 3555d026c72SKai Bankett /* try again without bootblock offset */ 3565d026c72SKai Bankett bh1 = qnx6_check_first_superblock(s, 0, silent); 3575d026c72SKai Bankett if (!bh1) { 3585d026c72SKai Bankett printk(KERN_ERR "qnx6: unable to read the first superblock\n"); 3595d026c72SKai Bankett goto outnobh; 3605d026c72SKai Bankett } 3615d026c72SKai Bankett /* seems that no bootblock at partition start */ 3625d026c72SKai Bankett bootblock_offset = 0; 3635d026c72SKai Bankett } 3645d026c72SKai Bankett sb1 = (struct qnx6_super_block *)bh1->b_data; 3655d026c72SKai Bankett 3665d026c72SKai Bankett #ifdef CONFIG_QNX6FS_DEBUG 3675d026c72SKai Bankett qnx6_superblock_debug(sb1, s); 3685d026c72SKai Bankett #endif 3695d026c72SKai Bankett 3705d026c72SKai Bankett /* checksum check - start at byte 8 and end at byte 512 */ 3715d026c72SKai Bankett if (fs32_to_cpu(sbi, sb1->sb_checksum) != 3725d026c72SKai Bankett crc32_be(0, (char *)(bh1->b_data + 8), 504)) { 3735d026c72SKai Bankett printk(KERN_ERR "qnx6: superblock #1 checksum error\n"); 3745d026c72SKai Bankett goto out; 3755d026c72SKai Bankett } 3765d026c72SKai Bankett 3775d026c72SKai Bankett /* set new blocksize */ 3785d026c72SKai Bankett if (!sb_set_blocksize(s, fs32_to_cpu(sbi, sb1->sb_blocksize))) { 3795d026c72SKai Bankett printk(KERN_ERR "qnx6: unable to set blocksize\n"); 3805d026c72SKai Bankett goto out; 3815d026c72SKai Bankett } 3825d026c72SKai Bankett /* blocksize invalidates bh - pull it back in */ 3835d026c72SKai Bankett brelse(bh1); 3845d026c72SKai Bankett bh1 = sb_bread(s, bootblock_offset >> s->s_blocksize_bits); 3855d026c72SKai Bankett if (!bh1) 3865d026c72SKai Bankett goto outnobh; 3875d026c72SKai Bankett sb1 = (struct qnx6_super_block *)bh1->b_data; 3885d026c72SKai Bankett 3895d026c72SKai Bankett /* calculate second superblock blocknumber */ 3905d026c72SKai Bankett offset = fs32_to_cpu(sbi, sb1->sb_num_blocks) + 3915d026c72SKai Bankett (bootblock_offset >> s->s_blocksize_bits) + 3925d026c72SKai Bankett (QNX6_SUPERBLOCK_AREA >> s->s_blocksize_bits); 3935d026c72SKai Bankett 3945d026c72SKai Bankett /* set bootblock offset */ 3955d026c72SKai Bankett sbi->s_blks_off = (bootblock_offset >> s->s_blocksize_bits) + 3965d026c72SKai Bankett (QNX6_SUPERBLOCK_AREA >> s->s_blocksize_bits); 3975d026c72SKai Bankett 3985d026c72SKai Bankett /* next the second superblock */ 3995d026c72SKai Bankett bh2 = sb_bread(s, offset); 4005d026c72SKai Bankett if (!bh2) { 4015d026c72SKai Bankett printk(KERN_ERR "qnx6: unable to read the second superblock\n"); 4025d026c72SKai Bankett goto out; 4035d026c72SKai Bankett } 4045d026c72SKai Bankett sb2 = (struct qnx6_super_block *)bh2->b_data; 4055d026c72SKai Bankett if (fs32_to_cpu(sbi, sb2->sb_magic) != QNX6_SUPER_MAGIC) { 4065d026c72SKai Bankett if (!silent) 4075d026c72SKai Bankett printk(KERN_ERR "qnx6: wrong signature (magic)" 4085d026c72SKai Bankett " in superblock #2.\n"); 4095d026c72SKai Bankett goto out; 4105d026c72SKai Bankett } 4115d026c72SKai Bankett 4125d026c72SKai Bankett /* checksum check - start at byte 8 and end at byte 512 */ 4135d026c72SKai Bankett if (fs32_to_cpu(sbi, sb2->sb_checksum) != 4145d026c72SKai Bankett crc32_be(0, (char *)(bh2->b_data + 8), 504)) { 4155d026c72SKai Bankett printk(KERN_ERR "qnx6: superblock #2 checksum error\n"); 4165d026c72SKai Bankett goto out; 4175d026c72SKai Bankett } 4185d026c72SKai Bankett 4195d026c72SKai Bankett if (fs64_to_cpu(sbi, sb1->sb_serial) >= 4205d026c72SKai Bankett fs64_to_cpu(sbi, sb2->sb_serial)) { 4215d026c72SKai Bankett /* superblock #1 active */ 4225d026c72SKai Bankett sbi->sb_buf = bh1; 4235d026c72SKai Bankett sbi->sb = (struct qnx6_super_block *)bh1->b_data; 4245d026c72SKai Bankett brelse(bh2); 4255d026c72SKai Bankett printk(KERN_INFO "qnx6: superblock #1 active\n"); 4265d026c72SKai Bankett } else { 4275d026c72SKai Bankett /* superblock #2 active */ 4285d026c72SKai Bankett sbi->sb_buf = bh2; 4295d026c72SKai Bankett sbi->sb = (struct qnx6_super_block *)bh2->b_data; 4305d026c72SKai Bankett brelse(bh1); 4315d026c72SKai Bankett printk(KERN_INFO "qnx6: superblock #2 active\n"); 4325d026c72SKai Bankett } 4335d026c72SKai Bankett mmi_success: 4345d026c72SKai Bankett /* sanity check - limit maximum indirect pointer levels */ 4355d026c72SKai Bankett if (sb1->Inode.levels > QNX6_PTR_MAX_LEVELS) { 4365d026c72SKai Bankett printk(KERN_ERR "qnx6: too many inode levels (max %i, sb %i)\n", 4375d026c72SKai Bankett QNX6_PTR_MAX_LEVELS, sb1->Inode.levels); 4385d026c72SKai Bankett goto out; 4395d026c72SKai Bankett } 4405d026c72SKai Bankett if (sb1->Longfile.levels > QNX6_PTR_MAX_LEVELS) { 4415d026c72SKai Bankett printk(KERN_ERR "qnx6: too many longfilename levels" 4425d026c72SKai Bankett " (max %i, sb %i)\n", 4435d026c72SKai Bankett QNX6_PTR_MAX_LEVELS, sb1->Longfile.levels); 4445d026c72SKai Bankett goto out; 4455d026c72SKai Bankett } 4465d026c72SKai Bankett s->s_op = &qnx6_sops; 4475d026c72SKai Bankett s->s_magic = QNX6_SUPER_MAGIC; 4485d026c72SKai Bankett s->s_flags |= MS_RDONLY; /* Yup, read-only yet */ 4495d026c72SKai Bankett 4505d026c72SKai Bankett /* ease the later tree level calculations */ 4515d026c72SKai Bankett sbi = QNX6_SB(s); 4525d026c72SKai Bankett sbi->s_ptrbits = ilog2(s->s_blocksize / 4); 4535d026c72SKai Bankett sbi->inodes = qnx6_private_inode(s, &sb1->Inode); 4545d026c72SKai Bankett if (!sbi->inodes) 4555d026c72SKai Bankett goto out; 4565d026c72SKai Bankett sbi->longfile = qnx6_private_inode(s, &sb1->Longfile); 4575d026c72SKai Bankett if (!sbi->longfile) 4585d026c72SKai Bankett goto out1; 4595d026c72SKai Bankett 4605d026c72SKai Bankett /* prefetch root inode */ 4615d026c72SKai Bankett root = qnx6_iget(s, QNX6_ROOT_INO); 4625d026c72SKai Bankett if (IS_ERR(root)) { 4635d026c72SKai Bankett printk(KERN_ERR "qnx6: get inode failed\n"); 4645d026c72SKai Bankett ret = PTR_ERR(root); 4655d026c72SKai Bankett goto out2; 4665d026c72SKai Bankett } 4675d026c72SKai Bankett 4685d026c72SKai Bankett ret = -ENOMEM; 4695d026c72SKai Bankett s->s_root = d_make_root(root); 4705d026c72SKai Bankett if (!s->s_root) 4715d026c72SKai Bankett goto out2; 4725d026c72SKai Bankett 4735d026c72SKai Bankett ret = -EINVAL; 4745d026c72SKai Bankett errmsg = qnx6_checkroot(s); 4755d026c72SKai Bankett if (errmsg != NULL) { 4765d026c72SKai Bankett if (!silent) 4775d026c72SKai Bankett printk(KERN_ERR "qnx6: %s\n", errmsg); 4785d026c72SKai Bankett goto out3; 4795d026c72SKai Bankett } 4805d026c72SKai Bankett return 0; 4815d026c72SKai Bankett 4825d026c72SKai Bankett out3: 4835d026c72SKai Bankett dput(s->s_root); 4845d026c72SKai Bankett s->s_root = NULL; 4855d026c72SKai Bankett out2: 4865d026c72SKai Bankett iput(sbi->longfile); 4875d026c72SKai Bankett out1: 4885d026c72SKai Bankett iput(sbi->inodes); 4895d026c72SKai Bankett out: 4905d026c72SKai Bankett if (bh1) 4915d026c72SKai Bankett brelse(bh1); 4925d026c72SKai Bankett if (bh2) 4935d026c72SKai Bankett brelse(bh2); 4945d026c72SKai Bankett outnobh: 4955d026c72SKai Bankett kfree(qs); 4965d026c72SKai Bankett s->s_fs_info = NULL; 4975d026c72SKai Bankett return ret; 4985d026c72SKai Bankett } 4995d026c72SKai Bankett 5005d026c72SKai Bankett static void qnx6_put_super(struct super_block *sb) 5015d026c72SKai Bankett { 5025d026c72SKai Bankett struct qnx6_sb_info *qs = QNX6_SB(sb); 5035d026c72SKai Bankett brelse(qs->sb_buf); 5045d026c72SKai Bankett iput(qs->longfile); 5055d026c72SKai Bankett iput(qs->inodes); 5065d026c72SKai Bankett kfree(qs); 5075d026c72SKai Bankett sb->s_fs_info = NULL; 5085d026c72SKai Bankett return; 5095d026c72SKai Bankett } 5105d026c72SKai Bankett 5115d026c72SKai Bankett static sector_t qnx6_bmap(struct address_space *mapping, sector_t block) 5125d026c72SKai Bankett { 5135d026c72SKai Bankett return generic_block_bmap(mapping, block, qnx6_get_block); 5145d026c72SKai Bankett } 5155d026c72SKai Bankett static const struct address_space_operations qnx6_aops = { 5165d026c72SKai Bankett .readpage = qnx6_readpage, 5175d026c72SKai Bankett .readpages = qnx6_readpages, 5185d026c72SKai Bankett .bmap = qnx6_bmap 5195d026c72SKai Bankett }; 5205d026c72SKai Bankett 5215d026c72SKai Bankett static struct inode *qnx6_private_inode(struct super_block *s, 5225d026c72SKai Bankett struct qnx6_root_node *p) 5235d026c72SKai Bankett { 5245d026c72SKai Bankett struct inode *inode = new_inode(s); 5255d026c72SKai Bankett if (inode) { 5265d026c72SKai Bankett struct qnx6_inode_info *ei = QNX6_I(inode); 5275d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(s); 5285d026c72SKai Bankett inode->i_size = fs64_to_cpu(sbi, p->size); 5295d026c72SKai Bankett memcpy(ei->di_block_ptr, p->ptr, sizeof(p->ptr)); 5305d026c72SKai Bankett ei->di_filelevels = p->levels; 5315d026c72SKai Bankett inode->i_mode = S_IFREG | S_IRUSR; /* probably wrong */ 5325d026c72SKai Bankett inode->i_mapping->a_ops = &qnx6_aops; 5335d026c72SKai Bankett } 5345d026c72SKai Bankett return inode; 5355d026c72SKai Bankett } 5365d026c72SKai Bankett 5375d026c72SKai Bankett struct inode *qnx6_iget(struct super_block *sb, unsigned ino) 5385d026c72SKai Bankett { 5395d026c72SKai Bankett struct qnx6_sb_info *sbi = QNX6_SB(sb); 5405d026c72SKai Bankett struct qnx6_inode_entry *raw_inode; 5415d026c72SKai Bankett struct inode *inode; 5425d026c72SKai Bankett struct qnx6_inode_info *ei; 5435d026c72SKai Bankett struct address_space *mapping; 5445d026c72SKai Bankett struct page *page; 5455d026c72SKai Bankett u32 n, offs; 5465d026c72SKai Bankett 5475d026c72SKai Bankett inode = iget_locked(sb, ino); 5485d026c72SKai Bankett if (!inode) 5495d026c72SKai Bankett return ERR_PTR(-ENOMEM); 5505d026c72SKai Bankett if (!(inode->i_state & I_NEW)) 5515d026c72SKai Bankett return inode; 5525d026c72SKai Bankett 5535d026c72SKai Bankett ei = QNX6_I(inode); 5545d026c72SKai Bankett 5555d026c72SKai Bankett inode->i_mode = 0; 5565d026c72SKai Bankett 5575d026c72SKai Bankett if (ino == 0) { 5585d026c72SKai Bankett printk(KERN_ERR "qnx6: bad inode number on dev %s: %u is " 5595d026c72SKai Bankett "out of range\n", 5605d026c72SKai Bankett sb->s_id, ino); 5615d026c72SKai Bankett iget_failed(inode); 5625d026c72SKai Bankett return ERR_PTR(-EIO); 5635d026c72SKai Bankett } 5645d026c72SKai Bankett n = (ino - 1) >> (PAGE_CACHE_SHIFT - QNX6_INODE_SIZE_BITS); 5655d026c72SKai Bankett offs = (ino - 1) & (~PAGE_CACHE_MASK >> QNX6_INODE_SIZE_BITS); 5665d026c72SKai Bankett mapping = sbi->inodes->i_mapping; 5675d026c72SKai Bankett page = read_mapping_page(mapping, n, NULL); 5685d026c72SKai Bankett if (IS_ERR(page)) { 5695d026c72SKai Bankett printk(KERN_ERR "qnx6: major problem: unable to read inode from " 5705d026c72SKai Bankett "dev %s\n", sb->s_id); 5715d026c72SKai Bankett iget_failed(inode); 5725d026c72SKai Bankett return ERR_CAST(page); 5735d026c72SKai Bankett } 5745d026c72SKai Bankett kmap(page); 5755d026c72SKai Bankett raw_inode = ((struct qnx6_inode_entry *)page_address(page)) + offs; 5765d026c72SKai Bankett 5775d026c72SKai Bankett inode->i_mode = fs16_to_cpu(sbi, raw_inode->di_mode); 57885a03d1bSEric W. Biederman i_uid_write(inode, (uid_t)fs32_to_cpu(sbi, raw_inode->di_uid)); 57985a03d1bSEric W. Biederman i_gid_write(inode, (gid_t)fs32_to_cpu(sbi, raw_inode->di_gid)); 5805d026c72SKai Bankett inode->i_size = fs64_to_cpu(sbi, raw_inode->di_size); 5815d026c72SKai Bankett inode->i_mtime.tv_sec = fs32_to_cpu(sbi, raw_inode->di_mtime); 5825d026c72SKai Bankett inode->i_mtime.tv_nsec = 0; 5835d026c72SKai Bankett inode->i_atime.tv_sec = fs32_to_cpu(sbi, raw_inode->di_atime); 5845d026c72SKai Bankett inode->i_atime.tv_nsec = 0; 5855d026c72SKai Bankett inode->i_ctime.tv_sec = fs32_to_cpu(sbi, raw_inode->di_ctime); 5865d026c72SKai Bankett inode->i_ctime.tv_nsec = 0; 5875d026c72SKai Bankett 5885d026c72SKai Bankett /* calc blocks based on 512 byte blocksize */ 5895d026c72SKai Bankett inode->i_blocks = (inode->i_size + 511) >> 9; 5905d026c72SKai Bankett 5915d026c72SKai Bankett memcpy(&ei->di_block_ptr, &raw_inode->di_block_ptr, 5925d026c72SKai Bankett sizeof(raw_inode->di_block_ptr)); 5935d026c72SKai Bankett ei->di_filelevels = raw_inode->di_filelevels; 5945d026c72SKai Bankett 5955d026c72SKai Bankett if (S_ISREG(inode->i_mode)) { 5965d026c72SKai Bankett inode->i_fop = &generic_ro_fops; 5975d026c72SKai Bankett inode->i_mapping->a_ops = &qnx6_aops; 5985d026c72SKai Bankett } else if (S_ISDIR(inode->i_mode)) { 5995d026c72SKai Bankett inode->i_op = &qnx6_dir_inode_operations; 6005d026c72SKai Bankett inode->i_fop = &qnx6_dir_operations; 6015d026c72SKai Bankett inode->i_mapping->a_ops = &qnx6_aops; 6025d026c72SKai Bankett } else if (S_ISLNK(inode->i_mode)) { 6035d026c72SKai Bankett inode->i_op = &page_symlink_inode_operations; 6045d026c72SKai Bankett inode->i_mapping->a_ops = &qnx6_aops; 6055d026c72SKai Bankett } else 6065d026c72SKai Bankett init_special_inode(inode, inode->i_mode, 0); 6075d026c72SKai Bankett qnx6_put_page(page); 6085d026c72SKai Bankett unlock_new_inode(inode); 6095d026c72SKai Bankett return inode; 6105d026c72SKai Bankett } 6115d026c72SKai Bankett 6125d026c72SKai Bankett static struct kmem_cache *qnx6_inode_cachep; 6135d026c72SKai Bankett 6145d026c72SKai Bankett static struct inode *qnx6_alloc_inode(struct super_block *sb) 6155d026c72SKai Bankett { 6165d026c72SKai Bankett struct qnx6_inode_info *ei; 6175d026c72SKai Bankett ei = kmem_cache_alloc(qnx6_inode_cachep, GFP_KERNEL); 6185d026c72SKai Bankett if (!ei) 6195d026c72SKai Bankett return NULL; 6205d026c72SKai Bankett return &ei->vfs_inode; 6215d026c72SKai Bankett } 6225d026c72SKai Bankett 6235d026c72SKai Bankett static void qnx6_i_callback(struct rcu_head *head) 6245d026c72SKai Bankett { 6255d026c72SKai Bankett struct inode *inode = container_of(head, struct inode, i_rcu); 6265d026c72SKai Bankett kmem_cache_free(qnx6_inode_cachep, QNX6_I(inode)); 6275d026c72SKai Bankett } 6285d026c72SKai Bankett 6295d026c72SKai Bankett static void qnx6_destroy_inode(struct inode *inode) 6305d026c72SKai Bankett { 6315d026c72SKai Bankett call_rcu(&inode->i_rcu, qnx6_i_callback); 6325d026c72SKai Bankett } 6335d026c72SKai Bankett 6345d026c72SKai Bankett static void init_once(void *foo) 6355d026c72SKai Bankett { 6365d026c72SKai Bankett struct qnx6_inode_info *ei = (struct qnx6_inode_info *) foo; 6375d026c72SKai Bankett 6385d026c72SKai Bankett inode_init_once(&ei->vfs_inode); 6395d026c72SKai Bankett } 6405d026c72SKai Bankett 6415d026c72SKai Bankett static int init_inodecache(void) 6425d026c72SKai Bankett { 6435d026c72SKai Bankett qnx6_inode_cachep = kmem_cache_create("qnx6_inode_cache", 6445d026c72SKai Bankett sizeof(struct qnx6_inode_info), 6455d026c72SKai Bankett 0, (SLAB_RECLAIM_ACCOUNT| 6465d026c72SKai Bankett SLAB_MEM_SPREAD), 6475d026c72SKai Bankett init_once); 6485d026c72SKai Bankett if (!qnx6_inode_cachep) 6495d026c72SKai Bankett return -ENOMEM; 6505d026c72SKai Bankett return 0; 6515d026c72SKai Bankett } 6525d026c72SKai Bankett 6535d026c72SKai Bankett static void destroy_inodecache(void) 6545d026c72SKai Bankett { 6558c0a8537SKirill A. Shutemov /* 6568c0a8537SKirill A. Shutemov * Make sure all delayed rcu free inodes are flushed before we 6578c0a8537SKirill A. Shutemov * destroy cache. 6588c0a8537SKirill A. Shutemov */ 6598c0a8537SKirill A. Shutemov rcu_barrier(); 6605d026c72SKai Bankett kmem_cache_destroy(qnx6_inode_cachep); 6615d026c72SKai Bankett } 6625d026c72SKai Bankett 6635d026c72SKai Bankett static struct dentry *qnx6_mount(struct file_system_type *fs_type, 6645d026c72SKai Bankett int flags, const char *dev_name, void *data) 6655d026c72SKai Bankett { 6665d026c72SKai Bankett return mount_bdev(fs_type, flags, dev_name, data, qnx6_fill_super); 6675d026c72SKai Bankett } 6685d026c72SKai Bankett 6695d026c72SKai Bankett static struct file_system_type qnx6_fs_type = { 6705d026c72SKai Bankett .owner = THIS_MODULE, 6715d026c72SKai Bankett .name = "qnx6", 6725d026c72SKai Bankett .mount = qnx6_mount, 6735d026c72SKai Bankett .kill_sb = kill_block_super, 6745d026c72SKai Bankett .fs_flags = FS_REQUIRES_DEV, 6755d026c72SKai Bankett }; 6767f78e035SEric W. Biederman MODULE_ALIAS_FS("qnx6"); 6775d026c72SKai Bankett 6785d026c72SKai Bankett static int __init init_qnx6_fs(void) 6795d026c72SKai Bankett { 6805d026c72SKai Bankett int err; 6815d026c72SKai Bankett 6825d026c72SKai Bankett err = init_inodecache(); 6835d026c72SKai Bankett if (err) 6845d026c72SKai Bankett return err; 6855d026c72SKai Bankett 6865d026c72SKai Bankett err = register_filesystem(&qnx6_fs_type); 6875d026c72SKai Bankett if (err) { 6885d026c72SKai Bankett destroy_inodecache(); 6895d026c72SKai Bankett return err; 6905d026c72SKai Bankett } 6915d026c72SKai Bankett 6925d026c72SKai Bankett printk(KERN_INFO "QNX6 filesystem 1.0.0 registered.\n"); 6935d026c72SKai Bankett return 0; 6945d026c72SKai Bankett } 6955d026c72SKai Bankett 6965d026c72SKai Bankett static void __exit exit_qnx6_fs(void) 6975d026c72SKai Bankett { 6985d026c72SKai Bankett unregister_filesystem(&qnx6_fs_type); 6995d026c72SKai Bankett destroy_inodecache(); 7005d026c72SKai Bankett } 7015d026c72SKai Bankett 7025d026c72SKai Bankett module_init(init_qnx6_fs) 7035d026c72SKai Bankett module_exit(exit_qnx6_fs) 7045d026c72SKai Bankett MODULE_LICENSE("GPL"); 705