162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/sysv/inode.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  minix/inode.c
662306a36Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *  xenix/inode.c
962306a36Sopenharmony_ci *  Copyright (C) 1992  Doug Evans
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *  coh/inode.c
1262306a36Sopenharmony_ci *  Copyright (C) 1993  Pascal Haible, Bruno Haible
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *  sysv/inode.c
1562306a36Sopenharmony_ci *  Copyright (C) 1993  Paul B. Monday
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *  sysv/inode.c
1862306a36Sopenharmony_ci *  Copyright (C) 1993  Bruno Haible
1962306a36Sopenharmony_ci *  Copyright (C) 1997, 1998  Krzysztof G. Baranowski
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *  This file contains code for read/parsing the superblock.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/init.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci#include <linux/buffer_head.h>
2862306a36Sopenharmony_ci#include "sysv.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * The following functions try to recognize specific filesystems.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * We recognize:
3462306a36Sopenharmony_ci * - Xenix FS by its magic number.
3562306a36Sopenharmony_ci * - SystemV FS by its magic number.
3662306a36Sopenharmony_ci * - Coherent FS by its funny fname/fpack field.
3762306a36Sopenharmony_ci * - SCO AFS by s_nfree == 0xffff
3862306a36Sopenharmony_ci * - V7 FS has no distinguishing features.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * We discriminate among SystemV4 and SystemV2 FS by the assumption that
4162306a36Sopenharmony_ci * the time stamp is not < 01-01-1980.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cienum {
4562306a36Sopenharmony_ci	JAN_1_1980 = (10*365 + 2) * 24 * 60 * 60
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void detected_xenix(struct sysv_sb_info *sbi, unsigned *max_links)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct buffer_head *bh1 = sbi->s_bh1;
5162306a36Sopenharmony_ci	struct buffer_head *bh2 = sbi->s_bh2;
5262306a36Sopenharmony_ci	struct xenix_super_block * sbd1;
5362306a36Sopenharmony_ci	struct xenix_super_block * sbd2;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (bh1 != bh2)
5662306a36Sopenharmony_ci		sbd1 = sbd2 = (struct xenix_super_block *) bh1->b_data;
5762306a36Sopenharmony_ci	else {
5862306a36Sopenharmony_ci		/* block size = 512, so bh1 != bh2 */
5962306a36Sopenharmony_ci		sbd1 = (struct xenix_super_block *) bh1->b_data;
6062306a36Sopenharmony_ci		sbd2 = (struct xenix_super_block *) (bh2->b_data - 512);
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	*max_links = XENIX_LINK_MAX;
6462306a36Sopenharmony_ci	sbi->s_fic_size = XENIX_NICINOD;
6562306a36Sopenharmony_ci	sbi->s_flc_size = XENIX_NICFREE;
6662306a36Sopenharmony_ci	sbi->s_sbd1 = (char *)sbd1;
6762306a36Sopenharmony_ci	sbi->s_sbd2 = (char *)sbd2;
6862306a36Sopenharmony_ci	sbi->s_sb_fic_count = &sbd1->s_ninode;
6962306a36Sopenharmony_ci	sbi->s_sb_fic_inodes = &sbd1->s_inode[0];
7062306a36Sopenharmony_ci	sbi->s_sb_total_free_inodes = &sbd2->s_tinode;
7162306a36Sopenharmony_ci	sbi->s_bcache_count = &sbd1->s_nfree;
7262306a36Sopenharmony_ci	sbi->s_bcache = &sbd1->s_free[0];
7362306a36Sopenharmony_ci	sbi->s_free_blocks = &sbd2->s_tfree;
7462306a36Sopenharmony_ci	sbi->s_sb_time = &sbd2->s_time;
7562306a36Sopenharmony_ci	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd1->s_isize);
7662306a36Sopenharmony_ci	sbi->s_nzones = fs32_to_cpu(sbi, sbd1->s_fsize);
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void detected_sysv4(struct sysv_sb_info *sbi, unsigned *max_links)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct sysv4_super_block * sbd;
8262306a36Sopenharmony_ci	struct buffer_head *bh1 = sbi->s_bh1;
8362306a36Sopenharmony_ci	struct buffer_head *bh2 = sbi->s_bh2;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (bh1 == bh2)
8662306a36Sopenharmony_ci		sbd = (struct sysv4_super_block *) (bh1->b_data + BLOCK_SIZE/2);
8762306a36Sopenharmony_ci	else
8862306a36Sopenharmony_ci		sbd = (struct sysv4_super_block *) bh2->b_data;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	*max_links = SYSV_LINK_MAX;
9162306a36Sopenharmony_ci	sbi->s_fic_size = SYSV_NICINOD;
9262306a36Sopenharmony_ci	sbi->s_flc_size = SYSV_NICFREE;
9362306a36Sopenharmony_ci	sbi->s_sbd1 = (char *)sbd;
9462306a36Sopenharmony_ci	sbi->s_sbd2 = (char *)sbd;
9562306a36Sopenharmony_ci	sbi->s_sb_fic_count = &sbd->s_ninode;
9662306a36Sopenharmony_ci	sbi->s_sb_fic_inodes = &sbd->s_inode[0];
9762306a36Sopenharmony_ci	sbi->s_sb_total_free_inodes = &sbd->s_tinode;
9862306a36Sopenharmony_ci	sbi->s_bcache_count = &sbd->s_nfree;
9962306a36Sopenharmony_ci	sbi->s_bcache = &sbd->s_free[0];
10062306a36Sopenharmony_ci	sbi->s_free_blocks = &sbd->s_tfree;
10162306a36Sopenharmony_ci	sbi->s_sb_time = &sbd->s_time;
10262306a36Sopenharmony_ci	sbi->s_sb_state = &sbd->s_state;
10362306a36Sopenharmony_ci	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
10462306a36Sopenharmony_ci	sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic void detected_sysv2(struct sysv_sb_info *sbi, unsigned *max_links)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct sysv2_super_block *sbd;
11062306a36Sopenharmony_ci	struct buffer_head *bh1 = sbi->s_bh1;
11162306a36Sopenharmony_ci	struct buffer_head *bh2 = sbi->s_bh2;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (bh1 == bh2)
11462306a36Sopenharmony_ci		sbd = (struct sysv2_super_block *) (bh1->b_data + BLOCK_SIZE/2);
11562306a36Sopenharmony_ci	else
11662306a36Sopenharmony_ci		sbd = (struct sysv2_super_block *) bh2->b_data;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	*max_links = SYSV_LINK_MAX;
11962306a36Sopenharmony_ci	sbi->s_fic_size = SYSV_NICINOD;
12062306a36Sopenharmony_ci	sbi->s_flc_size = SYSV_NICFREE;
12162306a36Sopenharmony_ci	sbi->s_sbd1 = (char *)sbd;
12262306a36Sopenharmony_ci	sbi->s_sbd2 = (char *)sbd;
12362306a36Sopenharmony_ci	sbi->s_sb_fic_count = &sbd->s_ninode;
12462306a36Sopenharmony_ci	sbi->s_sb_fic_inodes = &sbd->s_inode[0];
12562306a36Sopenharmony_ci	sbi->s_sb_total_free_inodes = &sbd->s_tinode;
12662306a36Sopenharmony_ci	sbi->s_bcache_count = &sbd->s_nfree;
12762306a36Sopenharmony_ci	sbi->s_bcache = &sbd->s_free[0];
12862306a36Sopenharmony_ci	sbi->s_free_blocks = &sbd->s_tfree;
12962306a36Sopenharmony_ci	sbi->s_sb_time = &sbd->s_time;
13062306a36Sopenharmony_ci	sbi->s_sb_state = &sbd->s_state;
13162306a36Sopenharmony_ci	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
13262306a36Sopenharmony_ci	sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void detected_coherent(struct sysv_sb_info *sbi, unsigned *max_links)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct coh_super_block * sbd;
13862306a36Sopenharmony_ci	struct buffer_head *bh1 = sbi->s_bh1;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	sbd = (struct coh_super_block *) bh1->b_data;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	*max_links = COH_LINK_MAX;
14362306a36Sopenharmony_ci	sbi->s_fic_size = COH_NICINOD;
14462306a36Sopenharmony_ci	sbi->s_flc_size = COH_NICFREE;
14562306a36Sopenharmony_ci	sbi->s_sbd1 = (char *)sbd;
14662306a36Sopenharmony_ci	sbi->s_sbd2 = (char *)sbd;
14762306a36Sopenharmony_ci	sbi->s_sb_fic_count = &sbd->s_ninode;
14862306a36Sopenharmony_ci	sbi->s_sb_fic_inodes = &sbd->s_inode[0];
14962306a36Sopenharmony_ci	sbi->s_sb_total_free_inodes = &sbd->s_tinode;
15062306a36Sopenharmony_ci	sbi->s_bcache_count = &sbd->s_nfree;
15162306a36Sopenharmony_ci	sbi->s_bcache = &sbd->s_free[0];
15262306a36Sopenharmony_ci	sbi->s_free_blocks = &sbd->s_tfree;
15362306a36Sopenharmony_ci	sbi->s_sb_time = &sbd->s_time;
15462306a36Sopenharmony_ci	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
15562306a36Sopenharmony_ci	sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void detected_v7(struct sysv_sb_info *sbi, unsigned *max_links)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct buffer_head *bh2 = sbi->s_bh2;
16162306a36Sopenharmony_ci	struct v7_super_block *sbd = (struct v7_super_block *)bh2->b_data;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	*max_links = V7_LINK_MAX;
16462306a36Sopenharmony_ci	sbi->s_fic_size = V7_NICINOD;
16562306a36Sopenharmony_ci	sbi->s_flc_size = V7_NICFREE;
16662306a36Sopenharmony_ci	sbi->s_sbd1 = (char *)sbd;
16762306a36Sopenharmony_ci	sbi->s_sbd2 = (char *)sbd;
16862306a36Sopenharmony_ci	sbi->s_sb_fic_count = &sbd->s_ninode;
16962306a36Sopenharmony_ci	sbi->s_sb_fic_inodes = &sbd->s_inode[0];
17062306a36Sopenharmony_ci	sbi->s_sb_total_free_inodes = &sbd->s_tinode;
17162306a36Sopenharmony_ci	sbi->s_bcache_count = &sbd->s_nfree;
17262306a36Sopenharmony_ci	sbi->s_bcache = &sbd->s_free[0];
17362306a36Sopenharmony_ci	sbi->s_free_blocks = &sbd->s_tfree;
17462306a36Sopenharmony_ci	sbi->s_sb_time = &sbd->s_time;
17562306a36Sopenharmony_ci	sbi->s_firstdatazone = fs16_to_cpu(sbi, sbd->s_isize);
17662306a36Sopenharmony_ci	sbi->s_nzones = fs32_to_cpu(sbi, sbd->s_fsize);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int detect_xenix(struct sysv_sb_info *sbi, struct buffer_head *bh)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct xenix_super_block *sbd = (struct xenix_super_block *)bh->b_data;
18262306a36Sopenharmony_ci	if (*(__le32 *)&sbd->s_magic == cpu_to_le32(0x2b5544))
18362306a36Sopenharmony_ci		sbi->s_bytesex = BYTESEX_LE;
18462306a36Sopenharmony_ci	else if (*(__be32 *)&sbd->s_magic == cpu_to_be32(0x2b5544))
18562306a36Sopenharmony_ci		sbi->s_bytesex = BYTESEX_BE;
18662306a36Sopenharmony_ci	else
18762306a36Sopenharmony_ci		return 0;
18862306a36Sopenharmony_ci	switch (fs32_to_cpu(sbi, sbd->s_type)) {
18962306a36Sopenharmony_ci	case 1:
19062306a36Sopenharmony_ci		sbi->s_type = FSTYPE_XENIX;
19162306a36Sopenharmony_ci		return 1;
19262306a36Sopenharmony_ci	case 2:
19362306a36Sopenharmony_ci		sbi->s_type = FSTYPE_XENIX;
19462306a36Sopenharmony_ci		return 2;
19562306a36Sopenharmony_ci	default:
19662306a36Sopenharmony_ci		return 0;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int detect_sysv(struct sysv_sb_info *sbi, struct buffer_head *bh)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct super_block *sb = sbi->s_sb;
20362306a36Sopenharmony_ci	/* All relevant fields are at the same offsets in R2 and R4 */
20462306a36Sopenharmony_ci	struct sysv4_super_block * sbd;
20562306a36Sopenharmony_ci	u32 type;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	sbd = (struct sysv4_super_block *) (bh->b_data + BLOCK_SIZE/2);
20862306a36Sopenharmony_ci	if (*(__le32 *)&sbd->s_magic == cpu_to_le32(0xfd187e20))
20962306a36Sopenharmony_ci		sbi->s_bytesex = BYTESEX_LE;
21062306a36Sopenharmony_ci	else if (*(__be32 *)&sbd->s_magic == cpu_to_be32(0xfd187e20))
21162306a36Sopenharmony_ci		sbi->s_bytesex = BYTESEX_BE;
21262306a36Sopenharmony_ci	else
21362306a36Sopenharmony_ci		return 0;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	type = fs32_to_cpu(sbi, sbd->s_type);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci 	if (fs16_to_cpu(sbi, sbd->s_nfree) == 0xffff) {
21862306a36Sopenharmony_ci 		sbi->s_type = FSTYPE_AFS;
21962306a36Sopenharmony_ci		sbi->s_forced_ro = 1;
22062306a36Sopenharmony_ci 		if (!sb_rdonly(sb)) {
22162306a36Sopenharmony_ci 			printk("SysV FS: SCO EAFS on %s detected, "
22262306a36Sopenharmony_ci 				"forcing read-only mode.\n",
22362306a36Sopenharmony_ci 				sb->s_id);
22462306a36Sopenharmony_ci 		}
22562306a36Sopenharmony_ci 		return type;
22662306a36Sopenharmony_ci 	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (fs32_to_cpu(sbi, sbd->s_time) < JAN_1_1980) {
22962306a36Sopenharmony_ci		/* this is likely to happen on SystemV2 FS */
23062306a36Sopenharmony_ci		if (type > 3 || type < 1)
23162306a36Sopenharmony_ci			return 0;
23262306a36Sopenharmony_ci		sbi->s_type = FSTYPE_SYSV2;
23362306a36Sopenharmony_ci		return type;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci	if ((type > 3 || type < 1) && (type > 0x30 || type < 0x10))
23662306a36Sopenharmony_ci		return 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* On Interactive Unix (ISC) Version 4.0/3.x s_type field = 0x10,
23962306a36Sopenharmony_ci	   0x20 or 0x30 indicates that symbolic links and the 14-character
24062306a36Sopenharmony_ci	   filename limit is gone. Due to lack of information about this
24162306a36Sopenharmony_ci           feature read-only mode seems to be a reasonable approach... -KGB */
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (type >= 0x10) {
24462306a36Sopenharmony_ci		printk("SysV FS: can't handle long file names on %s, "
24562306a36Sopenharmony_ci		       "forcing read-only mode.\n", sb->s_id);
24662306a36Sopenharmony_ci		sbi->s_forced_ro = 1;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	sbi->s_type = FSTYPE_SYSV4;
25062306a36Sopenharmony_ci	return type >= 0x10 ? type >> 4 : type;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic int detect_coherent(struct sysv_sb_info *sbi, struct buffer_head *bh)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct coh_super_block * sbd;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	sbd = (struct coh_super_block *) (bh->b_data + BLOCK_SIZE/2);
25862306a36Sopenharmony_ci	if ((memcmp(sbd->s_fname,"noname",6) && memcmp(sbd->s_fname,"xxxxx ",6))
25962306a36Sopenharmony_ci	    || (memcmp(sbd->s_fpack,"nopack",6) && memcmp(sbd->s_fpack,"xxxxx\n",6)))
26062306a36Sopenharmony_ci		return 0;
26162306a36Sopenharmony_ci	sbi->s_bytesex = BYTESEX_PDP;
26262306a36Sopenharmony_ci	sbi->s_type = FSTYPE_COH;
26362306a36Sopenharmony_ci	return 1;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int detect_sysv_odd(struct sysv_sb_info *sbi, struct buffer_head *bh)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int size = detect_sysv(sbi, bh);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return size>2 ? 0 : size;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct {
27462306a36Sopenharmony_ci	int block;
27562306a36Sopenharmony_ci	int (*test)(struct sysv_sb_info *, struct buffer_head *);
27662306a36Sopenharmony_ci} flavours[] = {
27762306a36Sopenharmony_ci	{1, detect_xenix},
27862306a36Sopenharmony_ci	{0, detect_sysv},
27962306a36Sopenharmony_ci	{0, detect_coherent},
28062306a36Sopenharmony_ci	{9, detect_sysv_odd},
28162306a36Sopenharmony_ci	{15,detect_sysv_odd},
28262306a36Sopenharmony_ci	{18,detect_sysv},
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic char *flavour_names[] = {
28662306a36Sopenharmony_ci	[FSTYPE_XENIX]	= "Xenix",
28762306a36Sopenharmony_ci	[FSTYPE_SYSV4]	= "SystemV",
28862306a36Sopenharmony_ci	[FSTYPE_SYSV2]	= "SystemV Release 2",
28962306a36Sopenharmony_ci	[FSTYPE_COH]	= "Coherent",
29062306a36Sopenharmony_ci	[FSTYPE_V7]	= "V7",
29162306a36Sopenharmony_ci	[FSTYPE_AFS]	= "AFS",
29262306a36Sopenharmony_ci};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void (*flavour_setup[])(struct sysv_sb_info *, unsigned *) = {
29562306a36Sopenharmony_ci	[FSTYPE_XENIX]	= detected_xenix,
29662306a36Sopenharmony_ci	[FSTYPE_SYSV4]	= detected_sysv4,
29762306a36Sopenharmony_ci	[FSTYPE_SYSV2]	= detected_sysv2,
29862306a36Sopenharmony_ci	[FSTYPE_COH]	= detected_coherent,
29962306a36Sopenharmony_ci	[FSTYPE_V7]	= detected_v7,
30062306a36Sopenharmony_ci	[FSTYPE_AFS]	= detected_sysv4,
30162306a36Sopenharmony_ci};
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int complete_read_super(struct super_block *sb, int silent, int size)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct sysv_sb_info *sbi = SYSV_SB(sb);
30662306a36Sopenharmony_ci	struct inode *root_inode;
30762306a36Sopenharmony_ci	char *found = flavour_names[sbi->s_type];
30862306a36Sopenharmony_ci	u_char n_bits = size+8;
30962306a36Sopenharmony_ci	int bsize = 1 << n_bits;
31062306a36Sopenharmony_ci	int bsize_4 = bsize >> 2;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	sbi->s_firstinodezone = 2;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	flavour_setup[sbi->s_type](sbi, &sb->s_max_links);
31562306a36Sopenharmony_ci	if (sbi->s_firstdatazone < sbi->s_firstinodezone)
31662306a36Sopenharmony_ci		return 0;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	sbi->s_ndatazones = sbi->s_nzones - sbi->s_firstdatazone;
31962306a36Sopenharmony_ci	sbi->s_inodes_per_block = bsize >> 6;
32062306a36Sopenharmony_ci	sbi->s_inodes_per_block_1 = (bsize >> 6)-1;
32162306a36Sopenharmony_ci	sbi->s_inodes_per_block_bits = n_bits-6;
32262306a36Sopenharmony_ci	sbi->s_ind_per_block = bsize_4;
32362306a36Sopenharmony_ci	sbi->s_ind_per_block_2 = bsize_4*bsize_4;
32462306a36Sopenharmony_ci	sbi->s_toobig_block = 10 + bsize_4 * (1 + bsize_4 * (1 + bsize_4));
32562306a36Sopenharmony_ci	sbi->s_ind_per_block_bits = n_bits-2;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	sbi->s_ninodes = (sbi->s_firstdatazone - sbi->s_firstinodezone)
32862306a36Sopenharmony_ci		<< sbi->s_inodes_per_block_bits;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!silent)
33162306a36Sopenharmony_ci		printk("VFS: Found a %s FS (block size = %ld) on device %s\n",
33262306a36Sopenharmony_ci		       found, sb->s_blocksize, sb->s_id);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	sb->s_magic = SYSV_MAGIC_BASE + sbi->s_type;
33562306a36Sopenharmony_ci	/* set up enough so that it can read an inode */
33662306a36Sopenharmony_ci	sb->s_op = &sysv_sops;
33762306a36Sopenharmony_ci	if (sbi->s_forced_ro)
33862306a36Sopenharmony_ci		sb->s_flags |= SB_RDONLY;
33962306a36Sopenharmony_ci	root_inode = sysv_iget(sb, SYSV_ROOT_INO);
34062306a36Sopenharmony_ci	if (IS_ERR(root_inode)) {
34162306a36Sopenharmony_ci		printk("SysV FS: get root inode failed\n");
34262306a36Sopenharmony_ci		return 0;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	sb->s_root = d_make_root(root_inode);
34562306a36Sopenharmony_ci	if (!sb->s_root) {
34662306a36Sopenharmony_ci		printk("SysV FS: get root dentry failed\n");
34762306a36Sopenharmony_ci		return 0;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	return 1;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int sysv_fill_super(struct super_block *sb, void *data, int silent)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct buffer_head *bh1, *bh = NULL;
35562306a36Sopenharmony_ci	struct sysv_sb_info *sbi;
35662306a36Sopenharmony_ci	unsigned long blocknr;
35762306a36Sopenharmony_ci	int size = 0, i;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	BUILD_BUG_ON(1024 != sizeof (struct xenix_super_block));
36062306a36Sopenharmony_ci	BUILD_BUG_ON(512 != sizeof (struct sysv4_super_block));
36162306a36Sopenharmony_ci	BUILD_BUG_ON(512 != sizeof (struct sysv2_super_block));
36262306a36Sopenharmony_ci	BUILD_BUG_ON(500 != sizeof (struct coh_super_block));
36362306a36Sopenharmony_ci	BUILD_BUG_ON(64 != sizeof (struct sysv_inode));
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	sbi = kzalloc(sizeof(struct sysv_sb_info), GFP_KERNEL);
36662306a36Sopenharmony_ci	if (!sbi)
36762306a36Sopenharmony_ci		return -ENOMEM;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	sbi->s_sb = sb;
37062306a36Sopenharmony_ci	sbi->s_block_base = 0;
37162306a36Sopenharmony_ci	mutex_init(&sbi->s_lock);
37262306a36Sopenharmony_ci	sb->s_fs_info = sbi;
37362306a36Sopenharmony_ci	sb->s_time_min = 0;
37462306a36Sopenharmony_ci	sb->s_time_max = U32_MAX;
37562306a36Sopenharmony_ci	sb_set_blocksize(sb, BLOCK_SIZE);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(flavours) && !size; i++) {
37862306a36Sopenharmony_ci		brelse(bh);
37962306a36Sopenharmony_ci		bh = sb_bread(sb, flavours[i].block);
38062306a36Sopenharmony_ci		if (!bh)
38162306a36Sopenharmony_ci			continue;
38262306a36Sopenharmony_ci		size = flavours[i].test(SYSV_SB(sb), bh);
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (!size)
38662306a36Sopenharmony_ci		goto Eunknown;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	switch (size) {
38962306a36Sopenharmony_ci		case 1:
39062306a36Sopenharmony_ci			blocknr = bh->b_blocknr << 1;
39162306a36Sopenharmony_ci			brelse(bh);
39262306a36Sopenharmony_ci			sb_set_blocksize(sb, 512);
39362306a36Sopenharmony_ci			bh1 = sb_bread(sb, blocknr);
39462306a36Sopenharmony_ci			bh = sb_bread(sb, blocknr + 1);
39562306a36Sopenharmony_ci			break;
39662306a36Sopenharmony_ci		case 2:
39762306a36Sopenharmony_ci			bh1 = bh;
39862306a36Sopenharmony_ci			break;
39962306a36Sopenharmony_ci		case 3:
40062306a36Sopenharmony_ci			blocknr = bh->b_blocknr >> 1;
40162306a36Sopenharmony_ci			brelse(bh);
40262306a36Sopenharmony_ci			sb_set_blocksize(sb, 2048);
40362306a36Sopenharmony_ci			bh1 = bh = sb_bread(sb, blocknr);
40462306a36Sopenharmony_ci			break;
40562306a36Sopenharmony_ci		default:
40662306a36Sopenharmony_ci			goto Ebadsize;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (bh && bh1) {
41062306a36Sopenharmony_ci		sbi->s_bh1 = bh1;
41162306a36Sopenharmony_ci		sbi->s_bh2 = bh;
41262306a36Sopenharmony_ci		if (complete_read_super(sb, silent, size))
41362306a36Sopenharmony_ci			return 0;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	brelse(bh1);
41762306a36Sopenharmony_ci	brelse(bh);
41862306a36Sopenharmony_ci	sb_set_blocksize(sb, BLOCK_SIZE);
41962306a36Sopenharmony_ci	printk("oldfs: cannot read superblock\n");
42062306a36Sopenharmony_cifailed:
42162306a36Sopenharmony_ci	kfree(sbi);
42262306a36Sopenharmony_ci	return -EINVAL;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ciEunknown:
42562306a36Sopenharmony_ci	brelse(bh);
42662306a36Sopenharmony_ci	if (!silent)
42762306a36Sopenharmony_ci		printk("VFS: unable to find oldfs superblock on device %s\n",
42862306a36Sopenharmony_ci			sb->s_id);
42962306a36Sopenharmony_ci	goto failed;
43062306a36Sopenharmony_ciEbadsize:
43162306a36Sopenharmony_ci	brelse(bh);
43262306a36Sopenharmony_ci	if (!silent)
43362306a36Sopenharmony_ci		printk("VFS: oldfs: unsupported block size (%dKb)\n",
43462306a36Sopenharmony_ci			1<<(size-2));
43562306a36Sopenharmony_ci	goto failed;
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic int v7_sanity_check(struct super_block *sb, struct buffer_head *bh)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct v7_super_block *v7sb;
44162306a36Sopenharmony_ci	struct sysv_inode *v7i;
44262306a36Sopenharmony_ci	struct buffer_head *bh2;
44362306a36Sopenharmony_ci	struct sysv_sb_info *sbi;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	sbi = sb->s_fs_info;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* plausibility check on superblock */
44862306a36Sopenharmony_ci	v7sb = (struct v7_super_block *) bh->b_data;
44962306a36Sopenharmony_ci	if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE ||
45062306a36Sopenharmony_ci	    fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD ||
45162306a36Sopenharmony_ci	    fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE)
45262306a36Sopenharmony_ci		return 0;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* plausibility check on root inode: it is a directory,
45562306a36Sopenharmony_ci	   with a nonzero size that is a multiple of 16 */
45662306a36Sopenharmony_ci	bh2 = sb_bread(sb, 2);
45762306a36Sopenharmony_ci	if (bh2 == NULL)
45862306a36Sopenharmony_ci		return 0;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	v7i = (struct sysv_inode *)(bh2->b_data + 64);
46162306a36Sopenharmony_ci	if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR ||
46262306a36Sopenharmony_ci	    (fs32_to_cpu(sbi, v7i->i_size) == 0) ||
46362306a36Sopenharmony_ci	    (fs32_to_cpu(sbi, v7i->i_size) & 017) ||
46462306a36Sopenharmony_ci	    (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES *
46562306a36Sopenharmony_ci	     sizeof(struct sysv_dir_entry))) {
46662306a36Sopenharmony_ci		brelse(bh2);
46762306a36Sopenharmony_ci		return 0;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	brelse(bh2);
47162306a36Sopenharmony_ci	return 1;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int v7_fill_super(struct super_block *sb, void *data, int silent)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	struct sysv_sb_info *sbi;
47762306a36Sopenharmony_ci	struct buffer_head *bh;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct v7_super_block) != 440);
48062306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct sysv_inode) != 64);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	sbi = kzalloc(sizeof(struct sysv_sb_info), GFP_KERNEL);
48362306a36Sopenharmony_ci	if (!sbi)
48462306a36Sopenharmony_ci		return -ENOMEM;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	sbi->s_sb = sb;
48762306a36Sopenharmony_ci	sbi->s_block_base = 0;
48862306a36Sopenharmony_ci	sbi->s_type = FSTYPE_V7;
48962306a36Sopenharmony_ci	mutex_init(&sbi->s_lock);
49062306a36Sopenharmony_ci	sb->s_fs_info = sbi;
49162306a36Sopenharmony_ci	sb->s_time_min = 0;
49262306a36Sopenharmony_ci	sb->s_time_max = U32_MAX;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	sb_set_blocksize(sb, 512);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if ((bh = sb_bread(sb, 1)) == NULL) {
49762306a36Sopenharmony_ci		if (!silent)
49862306a36Sopenharmony_ci			printk("VFS: unable to read V7 FS superblock on "
49962306a36Sopenharmony_ci			       "device %s.\n", sb->s_id);
50062306a36Sopenharmony_ci		goto failed;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/* Try PDP-11 UNIX */
50462306a36Sopenharmony_ci	sbi->s_bytesex = BYTESEX_PDP;
50562306a36Sopenharmony_ci	if (v7_sanity_check(sb, bh))
50662306a36Sopenharmony_ci		goto detected;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Try PC/IX, v7/x86 */
50962306a36Sopenharmony_ci	sbi->s_bytesex = BYTESEX_LE;
51062306a36Sopenharmony_ci	if (v7_sanity_check(sb, bh))
51162306a36Sopenharmony_ci		goto detected;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	goto failed;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cidetected:
51662306a36Sopenharmony_ci	sbi->s_bh1 = bh;
51762306a36Sopenharmony_ci	sbi->s_bh2 = bh;
51862306a36Sopenharmony_ci	if (complete_read_super(sb, silent, 1))
51962306a36Sopenharmony_ci		return 0;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cifailed:
52262306a36Sopenharmony_ci	printk(KERN_ERR "VFS: could not find a valid V7 on %s.\n",
52362306a36Sopenharmony_ci		sb->s_id);
52462306a36Sopenharmony_ci	brelse(bh);
52562306a36Sopenharmony_ci	kfree(sbi);
52662306a36Sopenharmony_ci	return -EINVAL;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/* Every kernel module contains stuff like this. */
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic struct dentry *sysv_mount(struct file_system_type *fs_type,
53262306a36Sopenharmony_ci	int flags, const char *dev_name, void *data)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	return mount_bdev(fs_type, flags, dev_name, data, sysv_fill_super);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic struct dentry *v7_mount(struct file_system_type *fs_type,
53862306a36Sopenharmony_ci	int flags, const char *dev_name, void *data)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	return mount_bdev(fs_type, flags, dev_name, data, v7_fill_super);
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic struct file_system_type sysv_fs_type = {
54462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
54562306a36Sopenharmony_ci	.name		= "sysv",
54662306a36Sopenharmony_ci	.mount		= sysv_mount,
54762306a36Sopenharmony_ci	.kill_sb	= kill_block_super,
54862306a36Sopenharmony_ci	.fs_flags	= FS_REQUIRES_DEV,
54962306a36Sopenharmony_ci};
55062306a36Sopenharmony_ciMODULE_ALIAS_FS("sysv");
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic struct file_system_type v7_fs_type = {
55362306a36Sopenharmony_ci	.owner		= THIS_MODULE,
55462306a36Sopenharmony_ci	.name		= "v7",
55562306a36Sopenharmony_ci	.mount		= v7_mount,
55662306a36Sopenharmony_ci	.kill_sb	= kill_block_super,
55762306a36Sopenharmony_ci	.fs_flags	= FS_REQUIRES_DEV,
55862306a36Sopenharmony_ci};
55962306a36Sopenharmony_ciMODULE_ALIAS_FS("v7");
56062306a36Sopenharmony_ciMODULE_ALIAS("v7");
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic int __init init_sysv_fs(void)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	int error;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	error = sysv_init_icache();
56762306a36Sopenharmony_ci	if (error)
56862306a36Sopenharmony_ci		goto out;
56962306a36Sopenharmony_ci	error = register_filesystem(&sysv_fs_type);
57062306a36Sopenharmony_ci	if (error)
57162306a36Sopenharmony_ci		goto destroy_icache;
57262306a36Sopenharmony_ci	error = register_filesystem(&v7_fs_type);
57362306a36Sopenharmony_ci	if (error)
57462306a36Sopenharmony_ci		goto unregister;
57562306a36Sopenharmony_ci	return 0;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ciunregister:
57862306a36Sopenharmony_ci	unregister_filesystem(&sysv_fs_type);
57962306a36Sopenharmony_cidestroy_icache:
58062306a36Sopenharmony_ci	sysv_destroy_icache();
58162306a36Sopenharmony_ciout:
58262306a36Sopenharmony_ci	return error;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic void __exit exit_sysv_fs(void)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	unregister_filesystem(&sysv_fs_type);
58862306a36Sopenharmony_ci	unregister_filesystem(&v7_fs_type);
58962306a36Sopenharmony_ci	sysv_destroy_icache();
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cimodule_init(init_sysv_fs)
59362306a36Sopenharmony_cimodule_exit(exit_sysv_fs)
59462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
595