162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2000-2001 Christoph Hellwig. 462306a36Sopenharmony_ci * Copyright (c) 2016 Krzysztof Blaszkowski 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci * Veritas filesystem driver - superblock related routines. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/blkdev.h> 1462306a36Sopenharmony_ci#include <linux/fs.h> 1562306a36Sopenharmony_ci#include <linux/buffer_head.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/stat.h> 1962306a36Sopenharmony_ci#include <linux/vfs.h> 2062306a36Sopenharmony_ci#include <linux/mount.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "vxfs.h" 2362306a36Sopenharmony_ci#include "vxfs_extern.h" 2462306a36Sopenharmony_ci#include "vxfs_dir.h" 2562306a36Sopenharmony_ci#include "vxfs_inode.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciMODULE_AUTHOR("Christoph Hellwig, Krzysztof Blaszkowski"); 2962306a36Sopenharmony_ciMODULE_DESCRIPTION("Veritas Filesystem (VxFS) driver"); 3062306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic struct kmem_cache *vxfs_inode_cachep; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/** 3562306a36Sopenharmony_ci * vxfs_put_super - free superblock resources 3662306a36Sopenharmony_ci * @sbp: VFS superblock. 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Description: 3962306a36Sopenharmony_ci * vxfs_put_super frees all resources allocated for @sbp 4062306a36Sopenharmony_ci * after the last instance of the filesystem is unmounted. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void 4462306a36Sopenharmony_civxfs_put_super(struct super_block *sbp) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct vxfs_sb_info *infp = VXFS_SBI(sbp); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci iput(infp->vsi_fship); 4962306a36Sopenharmony_ci iput(infp->vsi_ilist); 5062306a36Sopenharmony_ci iput(infp->vsi_stilist); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci brelse(infp->vsi_bp); 5362306a36Sopenharmony_ci kfree(infp); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * vxfs_statfs - get filesystem information 5862306a36Sopenharmony_ci * @dentry: VFS dentry to locate superblock 5962306a36Sopenharmony_ci * @bufp: output buffer 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Description: 6262306a36Sopenharmony_ci * vxfs_statfs fills the statfs buffer @bufp with information 6362306a36Sopenharmony_ci * about the filesystem described by @dentry. 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * Returns: 6662306a36Sopenharmony_ci * Zero. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Locking: 6962306a36Sopenharmony_ci * No locks held. 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * Notes: 7262306a36Sopenharmony_ci * This is everything but complete... 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistatic int 7562306a36Sopenharmony_civxfs_statfs(struct dentry *dentry, struct kstatfs *bufp) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct vxfs_sb_info *infp = VXFS_SBI(dentry->d_sb); 7862306a36Sopenharmony_ci struct vxfs_sb *raw_sb = infp->vsi_raw; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci bufp->f_type = VXFS_SUPER_MAGIC; 8162306a36Sopenharmony_ci bufp->f_bsize = dentry->d_sb->s_blocksize; 8262306a36Sopenharmony_ci bufp->f_blocks = fs32_to_cpu(infp, raw_sb->vs_dsize); 8362306a36Sopenharmony_ci bufp->f_bfree = fs32_to_cpu(infp, raw_sb->vs_free); 8462306a36Sopenharmony_ci bufp->f_bavail = 0; 8562306a36Sopenharmony_ci bufp->f_files = 0; 8662306a36Sopenharmony_ci bufp->f_ffree = fs32_to_cpu(infp, raw_sb->vs_ifree); 8762306a36Sopenharmony_ci bufp->f_namelen = VXFS_NAMELEN; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int vxfs_remount(struct super_block *sb, int *flags, char *data) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci sync_filesystem(sb); 9562306a36Sopenharmony_ci *flags |= SB_RDONLY; 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic struct inode *vxfs_alloc_inode(struct super_block *sb) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct vxfs_inode_info *vi; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci vi = alloc_inode_sb(sb, vxfs_inode_cachep, GFP_KERNEL); 10462306a36Sopenharmony_ci if (!vi) 10562306a36Sopenharmony_ci return NULL; 10662306a36Sopenharmony_ci inode_init_once(&vi->vfs_inode); 10762306a36Sopenharmony_ci return &vi->vfs_inode; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void vxfs_free_inode(struct inode *inode) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci kmem_cache_free(vxfs_inode_cachep, VXFS_INO(inode)); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const struct super_operations vxfs_super_ops = { 11662306a36Sopenharmony_ci .alloc_inode = vxfs_alloc_inode, 11762306a36Sopenharmony_ci .free_inode = vxfs_free_inode, 11862306a36Sopenharmony_ci .evict_inode = vxfs_evict_inode, 11962306a36Sopenharmony_ci .put_super = vxfs_put_super, 12062306a36Sopenharmony_ci .statfs = vxfs_statfs, 12162306a36Sopenharmony_ci .remount_fs = vxfs_remount, 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic int vxfs_try_sb_magic(struct super_block *sbp, int silent, 12562306a36Sopenharmony_ci unsigned blk, __fs32 magic) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct buffer_head *bp; 12862306a36Sopenharmony_ci struct vxfs_sb *rsbp; 12962306a36Sopenharmony_ci struct vxfs_sb_info *infp = VXFS_SBI(sbp); 13062306a36Sopenharmony_ci int rc = -ENOMEM; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci bp = sb_bread(sbp, blk); 13362306a36Sopenharmony_ci do { 13462306a36Sopenharmony_ci if (!bp || !buffer_mapped(bp)) { 13562306a36Sopenharmony_ci if (!silent) { 13662306a36Sopenharmony_ci printk(KERN_WARNING 13762306a36Sopenharmony_ci "vxfs: unable to read disk superblock at %u\n", 13862306a36Sopenharmony_ci blk); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci rc = -EINVAL; 14462306a36Sopenharmony_ci rsbp = (struct vxfs_sb *)bp->b_data; 14562306a36Sopenharmony_ci if (rsbp->vs_magic != magic) { 14662306a36Sopenharmony_ci if (!silent) 14762306a36Sopenharmony_ci printk(KERN_NOTICE 14862306a36Sopenharmony_ci "vxfs: WRONG superblock magic %08x at %u\n", 14962306a36Sopenharmony_ci rsbp->vs_magic, blk); 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci rc = 0; 15462306a36Sopenharmony_ci infp->vsi_raw = rsbp; 15562306a36Sopenharmony_ci infp->vsi_bp = bp; 15662306a36Sopenharmony_ci } while (0); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (rc) { 15962306a36Sopenharmony_ci infp->vsi_raw = NULL; 16062306a36Sopenharmony_ci infp->vsi_bp = NULL; 16162306a36Sopenharmony_ci brelse(bp); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return rc; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/** 16862306a36Sopenharmony_ci * vxfs_fill_super - read superblock into memory and initialize filesystem 16962306a36Sopenharmony_ci * @sbp: VFS superblock (to fill) 17062306a36Sopenharmony_ci * @dp: fs private mount data 17162306a36Sopenharmony_ci * @silent: do not complain loudly when sth is wrong 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * Description: 17462306a36Sopenharmony_ci * We are called on the first mount of a filesystem to read the 17562306a36Sopenharmony_ci * superblock into memory and do some basic setup. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * Returns: 17862306a36Sopenharmony_ci * The superblock on success, else %NULL. 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci * Locking: 18162306a36Sopenharmony_ci * We are under @sbp->s_lock. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct vxfs_sb_info *infp; 18662306a36Sopenharmony_ci struct vxfs_sb *rsbp; 18762306a36Sopenharmony_ci u_long bsize; 18862306a36Sopenharmony_ci struct inode *root; 18962306a36Sopenharmony_ci int ret = -EINVAL; 19062306a36Sopenharmony_ci u32 j; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci sbp->s_flags |= SB_RDONLY; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci infp = kzalloc(sizeof(*infp), GFP_KERNEL); 19562306a36Sopenharmony_ci if (!infp) { 19662306a36Sopenharmony_ci printk(KERN_WARNING "vxfs: unable to allocate incore superblock\n"); 19762306a36Sopenharmony_ci return -ENOMEM; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci bsize = sb_min_blocksize(sbp, BLOCK_SIZE); 20162306a36Sopenharmony_ci if (!bsize) { 20262306a36Sopenharmony_ci printk(KERN_WARNING "vxfs: unable to set blocksize\n"); 20362306a36Sopenharmony_ci goto out; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci sbp->s_op = &vxfs_super_ops; 20762306a36Sopenharmony_ci sbp->s_fs_info = infp; 20862306a36Sopenharmony_ci sbp->s_time_min = 0; 20962306a36Sopenharmony_ci sbp->s_time_max = U32_MAX; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!vxfs_try_sb_magic(sbp, silent, 1, 21262306a36Sopenharmony_ci (__force __fs32)cpu_to_le32(VXFS_SUPER_MAGIC))) { 21362306a36Sopenharmony_ci /* Unixware, x86 */ 21462306a36Sopenharmony_ci infp->byte_order = VXFS_BO_LE; 21562306a36Sopenharmony_ci } else if (!vxfs_try_sb_magic(sbp, silent, 8, 21662306a36Sopenharmony_ci (__force __fs32)cpu_to_be32(VXFS_SUPER_MAGIC))) { 21762306a36Sopenharmony_ci /* HP-UX, parisc */ 21862306a36Sopenharmony_ci infp->byte_order = VXFS_BO_BE; 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci if (!silent) 22162306a36Sopenharmony_ci printk(KERN_NOTICE "vxfs: can't find superblock.\n"); 22262306a36Sopenharmony_ci goto out; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci rsbp = infp->vsi_raw; 22662306a36Sopenharmony_ci j = fs32_to_cpu(infp, rsbp->vs_version); 22762306a36Sopenharmony_ci if ((j < 2 || j > 4) && !silent) { 22862306a36Sopenharmony_ci printk(KERN_NOTICE "vxfs: unsupported VxFS version (%d)\n", j); 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#ifdef DIAGNOSTIC 23362306a36Sopenharmony_ci printk(KERN_DEBUG "vxfs: supported VxFS version (%d)\n", j); 23462306a36Sopenharmony_ci printk(KERN_DEBUG "vxfs: blocksize: %d\n", 23562306a36Sopenharmony_ci fs32_to_cpu(infp, rsbp->vs_bsize)); 23662306a36Sopenharmony_ci#endif 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci sbp->s_magic = fs32_to_cpu(infp, rsbp->vs_magic); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci infp->vsi_oltext = fs32_to_cpu(infp, rsbp->vs_oltext[0]); 24162306a36Sopenharmony_ci infp->vsi_oltsize = fs32_to_cpu(infp, rsbp->vs_oltsize); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci j = fs32_to_cpu(infp, rsbp->vs_bsize); 24462306a36Sopenharmony_ci if (!sb_set_blocksize(sbp, j)) { 24562306a36Sopenharmony_ci printk(KERN_WARNING "vxfs: unable to set final block size\n"); 24662306a36Sopenharmony_ci goto out; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (vxfs_read_olt(sbp, bsize)) { 25062306a36Sopenharmony_ci printk(KERN_WARNING "vxfs: unable to read olt\n"); 25162306a36Sopenharmony_ci goto out; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (vxfs_read_fshead(sbp)) { 25562306a36Sopenharmony_ci printk(KERN_WARNING "vxfs: unable to read fshead\n"); 25662306a36Sopenharmony_ci goto out; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci root = vxfs_iget(sbp, VXFS_ROOT_INO); 26062306a36Sopenharmony_ci if (IS_ERR(root)) { 26162306a36Sopenharmony_ci ret = PTR_ERR(root); 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci sbp->s_root = d_make_root(root); 26562306a36Sopenharmony_ci if (!sbp->s_root) { 26662306a36Sopenharmony_ci printk(KERN_WARNING "vxfs: unable to get root dentry.\n"); 26762306a36Sopenharmony_ci goto out_free_ilist; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ciout_free_ilist: 27362306a36Sopenharmony_ci iput(infp->vsi_fship); 27462306a36Sopenharmony_ci iput(infp->vsi_ilist); 27562306a36Sopenharmony_ci iput(infp->vsi_stilist); 27662306a36Sopenharmony_ciout: 27762306a36Sopenharmony_ci brelse(infp->vsi_bp); 27862306a36Sopenharmony_ci kfree(infp); 27962306a36Sopenharmony_ci return ret; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* 28362306a36Sopenharmony_ci * The usual module blurb. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistatic struct dentry *vxfs_mount(struct file_system_type *fs_type, 28662306a36Sopenharmony_ci int flags, const char *dev_name, void *data) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci return mount_bdev(fs_type, flags, dev_name, data, vxfs_fill_super); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic struct file_system_type vxfs_fs_type = { 29262306a36Sopenharmony_ci .owner = THIS_MODULE, 29362306a36Sopenharmony_ci .name = "vxfs", 29462306a36Sopenharmony_ci .mount = vxfs_mount, 29562306a36Sopenharmony_ci .kill_sb = kill_block_super, 29662306a36Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ciMODULE_ALIAS_FS("vxfs"); /* makes mount -t vxfs autoload the module */ 29962306a36Sopenharmony_ciMODULE_ALIAS("vxfs"); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int __init 30262306a36Sopenharmony_civxfs_init(void) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int rv; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci vxfs_inode_cachep = kmem_cache_create_usercopy("vxfs_inode", 30762306a36Sopenharmony_ci sizeof(struct vxfs_inode_info), 0, 30862306a36Sopenharmony_ci SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, 30962306a36Sopenharmony_ci offsetof(struct vxfs_inode_info, vii_immed.vi_immed), 31062306a36Sopenharmony_ci sizeof_field(struct vxfs_inode_info, 31162306a36Sopenharmony_ci vii_immed.vi_immed), 31262306a36Sopenharmony_ci NULL); 31362306a36Sopenharmony_ci if (!vxfs_inode_cachep) 31462306a36Sopenharmony_ci return -ENOMEM; 31562306a36Sopenharmony_ci rv = register_filesystem(&vxfs_fs_type); 31662306a36Sopenharmony_ci if (rv < 0) 31762306a36Sopenharmony_ci kmem_cache_destroy(vxfs_inode_cachep); 31862306a36Sopenharmony_ci return rv; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void __exit 32262306a36Sopenharmony_civxfs_cleanup(void) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci unregister_filesystem(&vxfs_fs_type); 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * Make sure all delayed rcu free inodes are flushed before we 32762306a36Sopenharmony_ci * destroy cache. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ci rcu_barrier(); 33062306a36Sopenharmony_ci kmem_cache_destroy(vxfs_inode_cachep); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cimodule_init(vxfs_init); 33462306a36Sopenharmony_cimodule_exit(vxfs_cleanup); 335