162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * super.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 1999 Al Smith
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/exportfs.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/buffer_head.h>
1562306a36Sopenharmony_ci#include <linux/vfs.h>
1662306a36Sopenharmony_ci#include <linux/blkdev.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "efs.h"
1962306a36Sopenharmony_ci#include <linux/efs_vh.h>
2062306a36Sopenharmony_ci#include <linux/efs_fs_sb.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int efs_statfs(struct dentry *dentry, struct kstatfs *buf);
2362306a36Sopenharmony_cistatic int efs_fill_super(struct super_block *s, void *d, int silent);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic struct dentry *efs_mount(struct file_system_type *fs_type,
2662306a36Sopenharmony_ci	int flags, const char *dev_name, void *data)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	return mount_bdev(fs_type, flags, dev_name, data, efs_fill_super);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void efs_kill_sb(struct super_block *s)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct efs_sb_info *sbi = SUPER_INFO(s);
3462306a36Sopenharmony_ci	kill_block_super(s);
3562306a36Sopenharmony_ci	kfree(sbi);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic struct file_system_type efs_fs_type = {
3962306a36Sopenharmony_ci	.owner		= THIS_MODULE,
4062306a36Sopenharmony_ci	.name		= "efs",
4162306a36Sopenharmony_ci	.mount		= efs_mount,
4262306a36Sopenharmony_ci	.kill_sb	= efs_kill_sb,
4362306a36Sopenharmony_ci	.fs_flags	= FS_REQUIRES_DEV,
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ciMODULE_ALIAS_FS("efs");
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic struct pt_types sgi_pt_types[] = {
4862306a36Sopenharmony_ci	{0x00,		"SGI vh"},
4962306a36Sopenharmony_ci	{0x01,		"SGI trkrepl"},
5062306a36Sopenharmony_ci	{0x02,		"SGI secrepl"},
5162306a36Sopenharmony_ci	{0x03,		"SGI raw"},
5262306a36Sopenharmony_ci	{0x04,		"SGI bsd"},
5362306a36Sopenharmony_ci	{SGI_SYSV,	"SGI sysv"},
5462306a36Sopenharmony_ci	{0x06,		"SGI vol"},
5562306a36Sopenharmony_ci	{SGI_EFS,	"SGI efs"},
5662306a36Sopenharmony_ci	{0x08,		"SGI lv"},
5762306a36Sopenharmony_ci	{0x09,		"SGI rlv"},
5862306a36Sopenharmony_ci	{0x0A,		"SGI xfs"},
5962306a36Sopenharmony_ci	{0x0B,		"SGI xfslog"},
6062306a36Sopenharmony_ci	{0x0C,		"SGI xlv"},
6162306a36Sopenharmony_ci	{0x82,		"Linux swap"},
6262306a36Sopenharmony_ci	{0x83,		"Linux native"},
6362306a36Sopenharmony_ci	{0,		NULL}
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic struct kmem_cache * efs_inode_cachep;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic struct inode *efs_alloc_inode(struct super_block *sb)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct efs_inode_info *ei;
7262306a36Sopenharmony_ci	ei = alloc_inode_sb(sb, efs_inode_cachep, GFP_KERNEL);
7362306a36Sopenharmony_ci	if (!ei)
7462306a36Sopenharmony_ci		return NULL;
7562306a36Sopenharmony_ci	return &ei->vfs_inode;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void efs_free_inode(struct inode *inode)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	kmem_cache_free(efs_inode_cachep, INODE_INFO(inode));
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void init_once(void *foo)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct efs_inode_info *ei = (struct efs_inode_info *) foo;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	inode_init_once(&ei->vfs_inode);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int __init init_inodecache(void)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	efs_inode_cachep = kmem_cache_create("efs_inode_cache",
9362306a36Sopenharmony_ci				sizeof(struct efs_inode_info), 0,
9462306a36Sopenharmony_ci				SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
9562306a36Sopenharmony_ci				SLAB_ACCOUNT, init_once);
9662306a36Sopenharmony_ci	if (efs_inode_cachep == NULL)
9762306a36Sopenharmony_ci		return -ENOMEM;
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void destroy_inodecache(void)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	/*
10462306a36Sopenharmony_ci	 * Make sure all delayed rcu free inodes are flushed before we
10562306a36Sopenharmony_ci	 * destroy cache.
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	rcu_barrier();
10862306a36Sopenharmony_ci	kmem_cache_destroy(efs_inode_cachep);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int efs_remount(struct super_block *sb, int *flags, char *data)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	sync_filesystem(sb);
11462306a36Sopenharmony_ci	*flags |= SB_RDONLY;
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic const struct super_operations efs_superblock_operations = {
11962306a36Sopenharmony_ci	.alloc_inode	= efs_alloc_inode,
12062306a36Sopenharmony_ci	.free_inode	= efs_free_inode,
12162306a36Sopenharmony_ci	.statfs		= efs_statfs,
12262306a36Sopenharmony_ci	.remount_fs	= efs_remount,
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct export_operations efs_export_ops = {
12662306a36Sopenharmony_ci	.fh_to_dentry	= efs_fh_to_dentry,
12762306a36Sopenharmony_ci	.fh_to_parent	= efs_fh_to_parent,
12862306a36Sopenharmony_ci	.get_parent	= efs_get_parent,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int __init init_efs_fs(void) {
13262306a36Sopenharmony_ci	int err;
13362306a36Sopenharmony_ci	pr_info(EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n");
13462306a36Sopenharmony_ci	err = init_inodecache();
13562306a36Sopenharmony_ci	if (err)
13662306a36Sopenharmony_ci		goto out1;
13762306a36Sopenharmony_ci	err = register_filesystem(&efs_fs_type);
13862306a36Sopenharmony_ci	if (err)
13962306a36Sopenharmony_ci		goto out;
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ciout:
14262306a36Sopenharmony_ci	destroy_inodecache();
14362306a36Sopenharmony_ciout1:
14462306a36Sopenharmony_ci	return err;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void __exit exit_efs_fs(void) {
14862306a36Sopenharmony_ci	unregister_filesystem(&efs_fs_type);
14962306a36Sopenharmony_ci	destroy_inodecache();
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cimodule_init(init_efs_fs)
15362306a36Sopenharmony_cimodule_exit(exit_efs_fs)
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic efs_block_t efs_validate_vh(struct volume_header *vh) {
15662306a36Sopenharmony_ci	int		i;
15762306a36Sopenharmony_ci	__be32		cs, *ui;
15862306a36Sopenharmony_ci	int		csum;
15962306a36Sopenharmony_ci	efs_block_t	sblock = 0; /* shuts up gcc */
16062306a36Sopenharmony_ci	struct pt_types	*pt_entry;
16162306a36Sopenharmony_ci	int		pt_type, slice = -1;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (be32_to_cpu(vh->vh_magic) != VHMAGIC) {
16462306a36Sopenharmony_ci		/*
16562306a36Sopenharmony_ci		 * assume that we're dealing with a partition and allow
16662306a36Sopenharmony_ci		 * read_super() to try and detect a valid superblock
16762306a36Sopenharmony_ci		 * on the next block.
16862306a36Sopenharmony_ci		 */
16962306a36Sopenharmony_ci		return 0;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	ui = ((__be32 *) (vh + 1)) - 1;
17362306a36Sopenharmony_ci	for(csum = 0; ui >= ((__be32 *) vh);) {
17462306a36Sopenharmony_ci		cs = *ui--;
17562306a36Sopenharmony_ci		csum += be32_to_cpu(cs);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci	if (csum) {
17862306a36Sopenharmony_ci		pr_warn("SGI disklabel: checksum bad, label corrupted\n");
17962306a36Sopenharmony_ci		return 0;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci#ifdef DEBUG
18362306a36Sopenharmony_ci	pr_debug("bf: \"%16s\"\n", vh->vh_bootfile);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	for(i = 0; i < NVDIR; i++) {
18662306a36Sopenharmony_ci		int	j;
18762306a36Sopenharmony_ci		char	name[VDNAMESIZE+1];
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		for(j = 0; j < VDNAMESIZE; j++) {
19062306a36Sopenharmony_ci			name[j] = vh->vh_vd[i].vd_name[j];
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci		name[j] = (char) 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		if (name[0]) {
19562306a36Sopenharmony_ci			pr_debug("vh: %8s block: 0x%08x size: 0x%08x\n",
19662306a36Sopenharmony_ci				name, (int) be32_to_cpu(vh->vh_vd[i].vd_lbn),
19762306a36Sopenharmony_ci				(int) be32_to_cpu(vh->vh_vd[i].vd_nbytes));
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci#endif
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	for(i = 0; i < NPARTAB; i++) {
20362306a36Sopenharmony_ci		pt_type = (int) be32_to_cpu(vh->vh_pt[i].pt_type);
20462306a36Sopenharmony_ci		for(pt_entry = sgi_pt_types; pt_entry->pt_name; pt_entry++) {
20562306a36Sopenharmony_ci			if (pt_type == pt_entry->pt_type) break;
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci#ifdef DEBUG
20862306a36Sopenharmony_ci		if (be32_to_cpu(vh->vh_pt[i].pt_nblks)) {
20962306a36Sopenharmony_ci			pr_debug("pt %2d: start: %08d size: %08d type: 0x%02x (%s)\n",
21062306a36Sopenharmony_ci				 i, (int)be32_to_cpu(vh->vh_pt[i].pt_firstlbn),
21162306a36Sopenharmony_ci				 (int)be32_to_cpu(vh->vh_pt[i].pt_nblks),
21262306a36Sopenharmony_ci				 pt_type, (pt_entry->pt_name) ?
21362306a36Sopenharmony_ci				 pt_entry->pt_name : "unknown");
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci#endif
21662306a36Sopenharmony_ci		if (IS_EFS(pt_type)) {
21762306a36Sopenharmony_ci			sblock = be32_to_cpu(vh->vh_pt[i].pt_firstlbn);
21862306a36Sopenharmony_ci			slice = i;
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (slice == -1) {
22362306a36Sopenharmony_ci		pr_notice("partition table contained no EFS partitions\n");
22462306a36Sopenharmony_ci#ifdef DEBUG
22562306a36Sopenharmony_ci	} else {
22662306a36Sopenharmony_ci		pr_info("using slice %d (type %s, offset 0x%x)\n", slice,
22762306a36Sopenharmony_ci			(pt_entry->pt_name) ? pt_entry->pt_name : "unknown",
22862306a36Sopenharmony_ci			sblock);
22962306a36Sopenharmony_ci#endif
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	return sblock;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int efs_validate_super(struct efs_sb_info *sb, struct efs_super *super) {
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (!IS_EFS_MAGIC(be32_to_cpu(super->fs_magic)))
23762306a36Sopenharmony_ci		return -1;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	sb->fs_magic     = be32_to_cpu(super->fs_magic);
24062306a36Sopenharmony_ci	sb->total_blocks = be32_to_cpu(super->fs_size);
24162306a36Sopenharmony_ci	sb->first_block  = be32_to_cpu(super->fs_firstcg);
24262306a36Sopenharmony_ci	sb->group_size   = be32_to_cpu(super->fs_cgfsize);
24362306a36Sopenharmony_ci	sb->data_free    = be32_to_cpu(super->fs_tfree);
24462306a36Sopenharmony_ci	sb->inode_free   = be32_to_cpu(super->fs_tinode);
24562306a36Sopenharmony_ci	sb->inode_blocks = be16_to_cpu(super->fs_cgisize);
24662306a36Sopenharmony_ci	sb->total_groups = be16_to_cpu(super->fs_ncg);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic int efs_fill_super(struct super_block *s, void *d, int silent)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct efs_sb_info *sb;
25462306a36Sopenharmony_ci	struct buffer_head *bh;
25562306a36Sopenharmony_ci	struct inode *root;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci 	sb = kzalloc(sizeof(struct efs_sb_info), GFP_KERNEL);
25862306a36Sopenharmony_ci	if (!sb)
25962306a36Sopenharmony_ci		return -ENOMEM;
26062306a36Sopenharmony_ci	s->s_fs_info = sb;
26162306a36Sopenharmony_ci	s->s_time_min = 0;
26262306a36Sopenharmony_ci	s->s_time_max = U32_MAX;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	s->s_magic		= EFS_SUPER_MAGIC;
26562306a36Sopenharmony_ci	if (!sb_set_blocksize(s, EFS_BLOCKSIZE)) {
26662306a36Sopenharmony_ci		pr_err("device does not support %d byte blocks\n",
26762306a36Sopenharmony_ci			EFS_BLOCKSIZE);
26862306a36Sopenharmony_ci		return -EINVAL;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* read the vh (volume header) block */
27262306a36Sopenharmony_ci	bh = sb_bread(s, 0);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (!bh) {
27562306a36Sopenharmony_ci		pr_err("cannot read volume header\n");
27662306a36Sopenharmony_ci		return -EIO;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/*
28062306a36Sopenharmony_ci	 * if this returns zero then we didn't find any partition table.
28162306a36Sopenharmony_ci	 * this isn't (yet) an error - just assume for the moment that
28262306a36Sopenharmony_ci	 * the device is valid and go on to search for a superblock.
28362306a36Sopenharmony_ci	 */
28462306a36Sopenharmony_ci	sb->fs_start = efs_validate_vh((struct volume_header *) bh->b_data);
28562306a36Sopenharmony_ci	brelse(bh);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (sb->fs_start == -1) {
28862306a36Sopenharmony_ci		return -EINVAL;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	bh = sb_bread(s, sb->fs_start + EFS_SUPER);
29262306a36Sopenharmony_ci	if (!bh) {
29362306a36Sopenharmony_ci		pr_err("cannot read superblock\n");
29462306a36Sopenharmony_ci		return -EIO;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (efs_validate_super(sb, (struct efs_super *) bh->b_data)) {
29862306a36Sopenharmony_ci#ifdef DEBUG
29962306a36Sopenharmony_ci		pr_warn("invalid superblock at block %u\n",
30062306a36Sopenharmony_ci			sb->fs_start + EFS_SUPER);
30162306a36Sopenharmony_ci#endif
30262306a36Sopenharmony_ci		brelse(bh);
30362306a36Sopenharmony_ci		return -EINVAL;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci	brelse(bh);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (!sb_rdonly(s)) {
30862306a36Sopenharmony_ci#ifdef DEBUG
30962306a36Sopenharmony_ci		pr_info("forcing read-only mode\n");
31062306a36Sopenharmony_ci#endif
31162306a36Sopenharmony_ci		s->s_flags |= SB_RDONLY;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci	s->s_op   = &efs_superblock_operations;
31462306a36Sopenharmony_ci	s->s_export_op = &efs_export_ops;
31562306a36Sopenharmony_ci	root = efs_iget(s, EFS_ROOTINODE);
31662306a36Sopenharmony_ci	if (IS_ERR(root)) {
31762306a36Sopenharmony_ci		pr_err("get root inode failed\n");
31862306a36Sopenharmony_ci		return PTR_ERR(root);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	s->s_root = d_make_root(root);
32262306a36Sopenharmony_ci	if (!(s->s_root)) {
32362306a36Sopenharmony_ci		pr_err("get root dentry failed\n");
32462306a36Sopenharmony_ci		return -ENOMEM;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return 0;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int efs_statfs(struct dentry *dentry, struct kstatfs *buf) {
33162306a36Sopenharmony_ci	struct super_block *sb = dentry->d_sb;
33262306a36Sopenharmony_ci	struct efs_sb_info *sbi = SUPER_INFO(sb);
33362306a36Sopenharmony_ci	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	buf->f_type    = EFS_SUPER_MAGIC;	/* efs magic number */
33662306a36Sopenharmony_ci	buf->f_bsize   = EFS_BLOCKSIZE;		/* blocksize */
33762306a36Sopenharmony_ci	buf->f_blocks  = sbi->total_groups *	/* total data blocks */
33862306a36Sopenharmony_ci			(sbi->group_size - sbi->inode_blocks);
33962306a36Sopenharmony_ci	buf->f_bfree   = sbi->data_free;	/* free data blocks */
34062306a36Sopenharmony_ci	buf->f_bavail  = sbi->data_free;	/* free blocks for non-root */
34162306a36Sopenharmony_ci	buf->f_files   = sbi->total_groups *	/* total inodes */
34262306a36Sopenharmony_ci			sbi->inode_blocks *
34362306a36Sopenharmony_ci			(EFS_BLOCKSIZE / sizeof(struct efs_dinode));
34462306a36Sopenharmony_ci	buf->f_ffree   = sbi->inode_free;	/* free inodes */
34562306a36Sopenharmony_ci	buf->f_fsid    = u64_to_fsid(id);
34662306a36Sopenharmony_ci	buf->f_namelen = EFS_MAXNAMELEN;	/* max filename length */
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
351