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