Signed-off-by: Josef 'Jeff' Sipek <jeffpc@xxxxxxxxxxxxxx> --- fs/cmsfs/super.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 379 insertions(+), 0 deletions(-) create mode 100644 fs/cmsfs/super.c diff --git a/fs/cmsfs/super.c b/fs/cmsfs/super.c new file mode 100644 index 0000000..a397b09 --- /dev/null +++ b/fs/cmsfs/super.c @@ -0,0 +1,379 @@ +/* + * CMSFS + * + * (C) 2008 Josef 'Jeff' Sipek <jeffpc@xxxxxxxxxxxxxx> + * + * Based on cmsfs from 2.4.12-ac6: + * + * (C) 2001 Rick Troth <rtroth@xxxxxxx> + * (C) 2001 BMC Software, Inc., Houston, Texas, USA + * + */ + +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/fs.h> + +#include <linux/magic.h> +#include <linux/statfs.h> + +#include "cmsfs.h" + +static struct kmem_cache *cmsfs_inode_cachep = NULL; + +/* + * Blocksize test array - possible block sizes for a CMS "EDF" minidisk + * filesystem + */ +static int blksizes[] = { 512, 1024, 2048, 4096, 0, }; + +/** + * cmsfs_find_label - look for the CMS volume label structure (ADT) + * @sb: superblock to operate on + * @adt: ADT structure to fill in + * + * Tries various block sizes to find the CMSFS_SUPER_MAGIC. When found, + * sb->s_blocksize is updated apropriately, and the adt is filled in + */ +static int cmsfs_find_label(struct super_block *sb, struct CMSFSADT *adt) +{ + void *buf; + int i; + int err; + + buf = (void *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = -EIO; + + /* + * FBA DASDs are special. Physical blocksize is always 512 + * and the label is at physical offset 512 (second record), + * though the logical blocksize may be 512, 1K, 2K, or 4K. + */ + if (sb->s_blocksize == 512) { + /* read FBA block #1 (second record) */ + if (cmsfs_read_block(sb, buf, 1, 512) != 512) + goto out; + + memcpy(adt, buf, sizeof(*adt)); + + /* check for the CMS1 magic at the FBA offset */ + if (be32_to_cpu(adt->ADTIDENT) == CMSFS_SUPER_MAGIC) { + u32 blksz; + + blksz = be32_to_cpu(adt->ADTDBSIZ); + if (blksz != 512) { + printk(KERN_WARNING "cmsfs: FS blocksize" + " does not match device blocksize" + " (%u != %d).\n", blksz, 512); + } else { + CMSFS_SB(sb)->blksz = 512; + + err = 0; + } + + goto out; + } + } + + /* not an FBA volume; try CKD blocksizes + * + * CKD DASDs are C/H/S in nature and can have any blocksize + * that the utility or operating system decided to put there. + * OS/390 uses no particular blocksize, referring to tracks + * directly. For CMS CKD volumes, the physical blocksize should + * match logical, unless obscured by the access method. + */ + for (i = 0; blksizes[i] != 0; i++) { + u32 blksz; + + if (sb->s_blocksize != blksizes[i]) + continue; + + /* read CKD block #2 (third record) */ + if (cmsfs_read_block(sb, buf, 2, blksizes[i]) != blksizes[i]) + continue; + + memcpy(adt, buf, sizeof(*adt)); + + /* check for the CMS1 magic */ + if (be32_to_cpu(adt->ADTIDENT) != CMSFS_SUPER_MAGIC) + continue; + + blksz = be32_to_cpu(adt->ADTDBSIZ); + if (blksz == blksizes[i]) { + CMSFS_SB(sb)->blksz = blksz; + + sb_min_blocksize(sb, blksz); + + err = 0; + goto out; + } + + printk(KERN_WARNING "cmsfs: FS blocksize does not match" + " device blocksize (%u != %d).\n", blksz, blksizes[i]); + goto out; + } + + err = -ENOMEDIUM; + +out: + free_page((unsigned long)buf); + return err; +} + +/** + * __fill_super - fill in superblock private data + * @sb: superblock to operate on + * + * Find the ADT struct on the disk, read in the data, byteswap if necessary, + * and then will in CMSFS_SB(sb). + */ +static int __fill_super(struct super_block *sb) +{ + struct CMSFSADT cmsvol; /* partial "ADT" structure */ + struct cmsfs_sb_info *sbi = CMSFS_SB(sb); + struct inode *root; + int err; + + BUG_ON(!sbi); + + /* + * look for the CMS volume label (aka VOLSER) + * + * NOTE: This also effects a load of the ADT struct to &cmsvol and + * sets the blksz member to match the volume found + */ + err = cmsfs_find_label(sb, &cmsvol); + if (err) + return err; + + /* extract volume label */ + memcpy(sbi->volid, cmsvol.ADTID, 6); + sbi->volid[6] = 0x00; + + /* extract "directory origin pointer" */ + sbi->origin = be32_to_cpu(cmsvol.ADTDOP); + + /* extract number of cylinders used */ + sbi->ncyls = be32_to_cpu(cmsvol.ADTCYL); + + /* extract max number of cylinders */ + sbi->mcyls = be32_to_cpu(cmsvol.ADTMCYL); + + /* extract "total blocks on disk" count */ + sbi->blocks = be32_to_cpu(cmsvol.ADTNUM); + + /* compute "blocks used" count */ + sbi->bkused = be32_to_cpu(cmsvol.ADTUSED); + sbi->bkused += 1; /* why??? */ + + /* extract size and number of FSTs */ + sbi->fstsz = be32_to_cpu(cmsvol.ADTFSTSZ); + sbi->fstct = be32_to_cpu(cmsvol.ADTNFST); + + /* extract offset to reserved file, if any */ + sbi->resoff = be32_to_cpu(cmsvol.ADTOFFST); + + sbi->ctime = mktime(hex2int(cmsvol.ADTDCRED[0]) + + (cmsvol.ADTFLGL & ADTCNTRY ? 2000 : 1900), + hex2int(cmsvol.ADTDCRED[1] - 1), + hex2int(cmsvol.ADTDCRED[2]), + hex2int(cmsvol.ADTDCRED[3]), + hex2int(cmsvol.ADTDCRED[4]), + hex2int(cmsvol.ADTDCRED[5])); + + err = -ENOMEM; + root = cmsfs_iget(sb, CMSFS_DIRECTOR_INO); + if (IS_ERR(root)) { + err = PTR_ERR(root); + goto out; + } + + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + printk(KERN_ERR "cmsfs: get root inode failed\n"); + goto out_inode; + } + + err = -EUCLEAN; + + /* sanity check: FOP in dir entry must match DOP in vol */ + if (CMSFS_I(root)->origin != CMSFS_SB(sb)->origin) { + printk("cmsfs: directory FOP != volume DOP\n"); + goto out_inode; + } + + /* sanity check: RECFM of a directory must be fixed */ + if (CMSFS_I(root)->recfm != RECFM_FIXED) { + printk("cmsfs: directory must have recfm fixed.\n"); + goto out_inode; + } + + /* sanity check: LRECL of a directory must be 64 */ + if (CMSFS_I(root)->lrecl != 64) { + printk(KERN_WARNING "cmsfs: directory lrecl %d not 64.\n", + CMSFS_I(root)->lrecl); + goto out_inode; + } + + sbi->files = CMSFS_I(root)->items; + + sb->s_flags |= MS_RDONLY; + sb->s_maxbytes = MAX_NON_LFS; + + err = 0; +out: + return err; + +out_inode: + iput(root); + goto out; +} + +/** + * cmsfs_alloc_inode - allocate a VFS+cmsfs inode structs + * @sb: superblock to work with + */ +static struct inode *cmsfs_alloc_inode(struct super_block *sb) +{ + struct cmsfs_inode_info *cinode; + + cinode = kmem_cache_alloc(cmsfs_inode_cachep, GFP_NOFS); + if (!cinode) + return NULL; + + return &cinode->vfs_inode; +} + +/** + * cmsfs_destroy_inode - free VFS+cmsfs inode structs + * @inode: inode to free + */ +static void cmsfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(cmsfs_inode_cachep, CMSFS_I(inode)); +} + +/** + * cmsfs_statfs - return stat information + * @dentry: destry being stat'd + * @buf: stat buffer to fill + */ +static int cmsfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct cmsfs_sb_info *sbi = CMSFS_SB(dentry->d_sb); + + buf->f_type = CMSFS_SUPER_MAGIC; + buf->f_bsize = sbi->blksz; + buf->f_blocks = sbi->blocks; + buf->f_bfree = sbi->blocks - sbi->bkused; + buf->f_bavail = sbi->blocks - sbi->bkused; + + buf->f_files = sbi->files; + buf->f_ffree = 0; /* no files available on R/O media */ + + buf->f_namelen = CMSFS_NAME_LEN; + + memset(buf->f_fsid.val, 0, 8); + memcpy(buf->f_fsid.val, sbi->volid, 6); + + return 0; +} + +static struct super_operations cmsfs_sops = { + .alloc_inode = cmsfs_alloc_inode, + .destroy_inode = cmsfs_destroy_inode, + .statfs = cmsfs_statfs, +}; + +/** + * cmsfs_fill_super - fill in a superblock + * @sb: superblock to fill + * @data: (unused) + * @silent: don't printk anything + */ +static int cmsfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct cmsfs_sb_info *sbi; + int err; + + sbi = kzalloc(sizeof(struct cmsfs_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + sb->s_fs_info = sbi; + + sb->s_magic = CMSFS_SUPER_MAGIC; + sb->s_op = &cmsfs_sops; + + sb_min_blocksize(sb, 512); + + if ((err = __fill_super(sb))) { + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; + return err; + } + + return 0; +} + +static int cmsfs_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, cmsfs_fill_super, + mnt); +} + +static struct file_system_type cmsfs_type = { + .owner = THIS_MODULE, + .name = "cmsfs", + .get_sb = cmsfs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static void cmsfs_inode_init_once(void *data) +{ + struct cmsfs_inode_info *cinode = data; + + memset(cinode, 0, sizeof(struct cmsfs_inode_info)); + + inode_init_once(&cinode->vfs_inode); +} + +static int cmsfs_init(void) +{ + int err; + printk(KERN_INFO "CMSFS\n"); + + cmsfs_inode_cachep = kmem_cache_create("cmsfs_inode_cache", + sizeof(struct cmsfs_inode_info), + 0, 0, + cmsfs_inode_init_once); + if (!cmsfs_inode_cachep) + return -ENOMEM; + + err = register_filesystem(&cmsfs_type); + if (err) + kmem_cache_destroy(cmsfs_inode_cachep); + + return err; +} + +static void cmsfs_cleanup(void) +{ + unregister_filesystem(&cmsfs_type); + kmem_cache_destroy(cmsfs_inode_cachep); + return; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CMS Minidisk filesystem Support"); +MODULE_AUTHOR("Josef 'Jeff' Sipek <jeffpc@xxxxxxxxxxxxxx>"); + +module_init(cmsfs_init); +module_exit(cmsfs_cleanup); -- 1.5.6.3 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html