162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * inode.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 *              and from work (c) 1998 Mike Shaver.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/buffer_head.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/fs.h>
1462306a36Sopenharmony_ci#include "efs.h"
1562306a36Sopenharmony_ci#include <linux/efs_fs_sb.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int efs_read_folio(struct file *file, struct folio *folio)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	return block_read_full_folio(folio, efs_get_block);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic sector_t _efs_bmap(struct address_space *mapping, sector_t block)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	return generic_block_bmap(mapping,block,efs_get_block);
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct address_space_operations efs_aops = {
2862306a36Sopenharmony_ci	.read_folio = efs_read_folio,
2962306a36Sopenharmony_ci	.bmap = _efs_bmap
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic inline void extent_copy(efs_extent *src, efs_extent *dst) {
3362306a36Sopenharmony_ci	/*
3462306a36Sopenharmony_ci	 * this is slightly evil. it doesn't just copy
3562306a36Sopenharmony_ci	 * efs_extent from src to dst, it also mangles
3662306a36Sopenharmony_ci	 * the bits so that dst ends up in cpu byte-order.
3762306a36Sopenharmony_ci	 */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	dst->cooked.ex_magic  =  (unsigned int) src->raw[0];
4062306a36Sopenharmony_ci	dst->cooked.ex_bn     = ((unsigned int) src->raw[1] << 16) |
4162306a36Sopenharmony_ci				((unsigned int) src->raw[2] <<  8) |
4262306a36Sopenharmony_ci				((unsigned int) src->raw[3] <<  0);
4362306a36Sopenharmony_ci	dst->cooked.ex_length =  (unsigned int) src->raw[4];
4462306a36Sopenharmony_ci	dst->cooked.ex_offset = ((unsigned int) src->raw[5] << 16) |
4562306a36Sopenharmony_ci				((unsigned int) src->raw[6] <<  8) |
4662306a36Sopenharmony_ci				((unsigned int) src->raw[7] <<  0);
4762306a36Sopenharmony_ci	return;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistruct inode *efs_iget(struct super_block *super, unsigned long ino)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	int i, inode_index;
5362306a36Sopenharmony_ci	dev_t device;
5462306a36Sopenharmony_ci	u32 rdev;
5562306a36Sopenharmony_ci	struct buffer_head *bh;
5662306a36Sopenharmony_ci	struct efs_sb_info    *sb = SUPER_INFO(super);
5762306a36Sopenharmony_ci	struct efs_inode_info *in;
5862306a36Sopenharmony_ci	efs_block_t block, offset;
5962306a36Sopenharmony_ci	struct efs_dinode *efs_inode;
6062306a36Sopenharmony_ci	struct inode *inode;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	inode = iget_locked(super, ino);
6362306a36Sopenharmony_ci	if (!inode)
6462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6562306a36Sopenharmony_ci	if (!(inode->i_state & I_NEW))
6662306a36Sopenharmony_ci		return inode;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	in = INODE_INFO(inode);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	** EFS layout:
7262306a36Sopenharmony_ci	**
7362306a36Sopenharmony_ci	** |   cylinder group    |   cylinder group    |   cylinder group ..etc
7462306a36Sopenharmony_ci	** |inodes|data          |inodes|data          |inodes|data       ..etc
7562306a36Sopenharmony_ci	**
7662306a36Sopenharmony_ci	** work out the inode block index, (considering initially that the
7762306a36Sopenharmony_ci	** inodes are stored as consecutive blocks). then work out the block
7862306a36Sopenharmony_ci	** number of that inode given the above layout, and finally the
7962306a36Sopenharmony_ci	** offset of the inode within that block.
8062306a36Sopenharmony_ci	*/
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	inode_index = inode->i_ino /
8362306a36Sopenharmony_ci		(EFS_BLOCKSIZE / sizeof(struct efs_dinode));
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	block = sb->fs_start + sb->first_block +
8662306a36Sopenharmony_ci		(sb->group_size * (inode_index / sb->inode_blocks)) +
8762306a36Sopenharmony_ci		(inode_index % sb->inode_blocks);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	offset = (inode->i_ino %
9062306a36Sopenharmony_ci			(EFS_BLOCKSIZE / sizeof(struct efs_dinode))) *
9162306a36Sopenharmony_ci		sizeof(struct efs_dinode);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	bh = sb_bread(inode->i_sb, block);
9462306a36Sopenharmony_ci	if (!bh) {
9562306a36Sopenharmony_ci		pr_warn("%s() failed at block %d\n", __func__, block);
9662306a36Sopenharmony_ci		goto read_inode_error;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	efs_inode = (struct efs_dinode *) (bh->b_data + offset);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	inode->i_mode  = be16_to_cpu(efs_inode->di_mode);
10262306a36Sopenharmony_ci	set_nlink(inode, be16_to_cpu(efs_inode->di_nlink));
10362306a36Sopenharmony_ci	i_uid_write(inode, (uid_t)be16_to_cpu(efs_inode->di_uid));
10462306a36Sopenharmony_ci	i_gid_write(inode, (gid_t)be16_to_cpu(efs_inode->di_gid));
10562306a36Sopenharmony_ci	inode->i_size  = be32_to_cpu(efs_inode->di_size);
10662306a36Sopenharmony_ci	inode->i_atime.tv_sec = be32_to_cpu(efs_inode->di_atime);
10762306a36Sopenharmony_ci	inode->i_mtime.tv_sec = be32_to_cpu(efs_inode->di_mtime);
10862306a36Sopenharmony_ci	inode_set_ctime(inode, be32_to_cpu(efs_inode->di_ctime), 0);
10962306a36Sopenharmony_ci	inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* this is the number of blocks in the file */
11262306a36Sopenharmony_ci	if (inode->i_size == 0) {
11362306a36Sopenharmony_ci		inode->i_blocks = 0;
11462306a36Sopenharmony_ci	} else {
11562306a36Sopenharmony_ci		inode->i_blocks = ((inode->i_size - 1) >> EFS_BLOCKSIZE_BITS) + 1;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	rdev = be16_to_cpu(efs_inode->di_u.di_dev.odev);
11962306a36Sopenharmony_ci	if (rdev == 0xffff) {
12062306a36Sopenharmony_ci		rdev = be32_to_cpu(efs_inode->di_u.di_dev.ndev);
12162306a36Sopenharmony_ci		if (sysv_major(rdev) > 0xfff)
12262306a36Sopenharmony_ci			device = 0;
12362306a36Sopenharmony_ci		else
12462306a36Sopenharmony_ci			device = MKDEV(sysv_major(rdev), sysv_minor(rdev));
12562306a36Sopenharmony_ci	} else
12662306a36Sopenharmony_ci		device = old_decode_dev(rdev);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* get the number of extents for this object */
12962306a36Sopenharmony_ci	in->numextents = be16_to_cpu(efs_inode->di_numextents);
13062306a36Sopenharmony_ci	in->lastextent = 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* copy the extents contained within the inode to memory */
13362306a36Sopenharmony_ci	for(i = 0; i < EFS_DIRECTEXTENTS; i++) {
13462306a36Sopenharmony_ci		extent_copy(&(efs_inode->di_u.di_extents[i]), &(in->extents[i]));
13562306a36Sopenharmony_ci		if (i < in->numextents && in->extents[i].cooked.ex_magic != 0) {
13662306a36Sopenharmony_ci			pr_warn("extent %d has bad magic number in inode %lu\n",
13762306a36Sopenharmony_ci				i, inode->i_ino);
13862306a36Sopenharmony_ci			brelse(bh);
13962306a36Sopenharmony_ci			goto read_inode_error;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	brelse(bh);
14462306a36Sopenharmony_ci	pr_debug("efs_iget(): inode %lu, extents %d, mode %o\n",
14562306a36Sopenharmony_ci		 inode->i_ino, in->numextents, inode->i_mode);
14662306a36Sopenharmony_ci	switch (inode->i_mode & S_IFMT) {
14762306a36Sopenharmony_ci		case S_IFDIR:
14862306a36Sopenharmony_ci			inode->i_op = &efs_dir_inode_operations;
14962306a36Sopenharmony_ci			inode->i_fop = &efs_dir_operations;
15062306a36Sopenharmony_ci			break;
15162306a36Sopenharmony_ci		case S_IFREG:
15262306a36Sopenharmony_ci			inode->i_fop = &generic_ro_fops;
15362306a36Sopenharmony_ci			inode->i_data.a_ops = &efs_aops;
15462306a36Sopenharmony_ci			break;
15562306a36Sopenharmony_ci		case S_IFLNK:
15662306a36Sopenharmony_ci			inode->i_op = &page_symlink_inode_operations;
15762306a36Sopenharmony_ci			inode_nohighmem(inode);
15862306a36Sopenharmony_ci			inode->i_data.a_ops = &efs_symlink_aops;
15962306a36Sopenharmony_ci			break;
16062306a36Sopenharmony_ci		case S_IFCHR:
16162306a36Sopenharmony_ci		case S_IFBLK:
16262306a36Sopenharmony_ci		case S_IFIFO:
16362306a36Sopenharmony_ci			init_special_inode(inode, inode->i_mode, device);
16462306a36Sopenharmony_ci			break;
16562306a36Sopenharmony_ci		default:
16662306a36Sopenharmony_ci			pr_warn("unsupported inode mode %o\n", inode->i_mode);
16762306a36Sopenharmony_ci			goto read_inode_error;
16862306a36Sopenharmony_ci			break;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	unlock_new_inode(inode);
17262306a36Sopenharmony_ci	return inode;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciread_inode_error:
17562306a36Sopenharmony_ci	pr_warn("failed to read inode %lu\n", inode->i_ino);
17662306a36Sopenharmony_ci	iget_failed(inode);
17762306a36Sopenharmony_ci	return ERR_PTR(-EIO);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic inline efs_block_t
18162306a36Sopenharmony_ciefs_extent_check(efs_extent *ptr, efs_block_t block, struct efs_sb_info *sb) {
18262306a36Sopenharmony_ci	efs_block_t start;
18362306a36Sopenharmony_ci	efs_block_t length;
18462306a36Sopenharmony_ci	efs_block_t offset;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	/*
18762306a36Sopenharmony_ci	 * given an extent and a logical block within a file,
18862306a36Sopenharmony_ci	 * can this block be found within this extent ?
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	start  = ptr->cooked.ex_bn;
19162306a36Sopenharmony_ci	length = ptr->cooked.ex_length;
19262306a36Sopenharmony_ci	offset = ptr->cooked.ex_offset;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if ((block >= offset) && (block < offset+length)) {
19562306a36Sopenharmony_ci		return(sb->fs_start + start + block - offset);
19662306a36Sopenharmony_ci	} else {
19762306a36Sopenharmony_ci		return 0;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciefs_block_t efs_map_block(struct inode *inode, efs_block_t block) {
20262306a36Sopenharmony_ci	struct efs_sb_info    *sb = SUPER_INFO(inode->i_sb);
20362306a36Sopenharmony_ci	struct efs_inode_info *in = INODE_INFO(inode);
20462306a36Sopenharmony_ci	struct buffer_head    *bh = NULL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	int cur, last, first = 1;
20762306a36Sopenharmony_ci	int ibase, ioffset, dirext, direxts, indext, indexts;
20862306a36Sopenharmony_ci	efs_block_t iblock, result = 0, lastblock = 0;
20962306a36Sopenharmony_ci	efs_extent ext, *exts;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	last = in->lastextent;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (in->numextents <= EFS_DIRECTEXTENTS) {
21462306a36Sopenharmony_ci		/* first check the last extent we returned */
21562306a36Sopenharmony_ci		if ((result = efs_extent_check(&in->extents[last], block, sb)))
21662306a36Sopenharmony_ci			return result;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		/* if we only have one extent then nothing can be found */
21962306a36Sopenharmony_ci		if (in->numextents == 1) {
22062306a36Sopenharmony_ci			pr_err("%s() failed to map (1 extent)\n", __func__);
22162306a36Sopenharmony_ci			return 0;
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		direxts = in->numextents;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		/*
22762306a36Sopenharmony_ci		 * check the stored extents in the inode
22862306a36Sopenharmony_ci		 * start with next extent and check forwards
22962306a36Sopenharmony_ci		 */
23062306a36Sopenharmony_ci		for(dirext = 1; dirext < direxts; dirext++) {
23162306a36Sopenharmony_ci			cur = (last + dirext) % in->numextents;
23262306a36Sopenharmony_ci			if ((result = efs_extent_check(&in->extents[cur], block, sb))) {
23362306a36Sopenharmony_ci				in->lastextent = cur;
23462306a36Sopenharmony_ci				return result;
23562306a36Sopenharmony_ci			}
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		pr_err("%s() failed to map block %u (dir)\n", __func__, block);
23962306a36Sopenharmony_ci		return 0;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	pr_debug("%s(): indirect search for logical block %u\n",
24362306a36Sopenharmony_ci		 __func__, block);
24462306a36Sopenharmony_ci	direxts = in->extents[0].cooked.ex_offset;
24562306a36Sopenharmony_ci	indexts = in->numextents;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	for(indext = 0; indext < indexts; indext++) {
24862306a36Sopenharmony_ci		cur = (last + indext) % indexts;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		/*
25162306a36Sopenharmony_ci		 * work out which direct extent contains `cur'.
25262306a36Sopenharmony_ci		 *
25362306a36Sopenharmony_ci		 * also compute ibase: i.e. the number of the first
25462306a36Sopenharmony_ci		 * indirect extent contained within direct extent `cur'.
25562306a36Sopenharmony_ci		 *
25662306a36Sopenharmony_ci		 */
25762306a36Sopenharmony_ci		ibase = 0;
25862306a36Sopenharmony_ci		for(dirext = 0; cur < ibase && dirext < direxts; dirext++) {
25962306a36Sopenharmony_ci			ibase += in->extents[dirext].cooked.ex_length *
26062306a36Sopenharmony_ci				(EFS_BLOCKSIZE / sizeof(efs_extent));
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		if (dirext == direxts) {
26462306a36Sopenharmony_ci			/* should never happen */
26562306a36Sopenharmony_ci			pr_err("couldn't find direct extent for indirect extent %d (block %u)\n",
26662306a36Sopenharmony_ci			       cur, block);
26762306a36Sopenharmony_ci			if (bh) brelse(bh);
26862306a36Sopenharmony_ci			return 0;
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		/* work out block number and offset of this indirect extent */
27262306a36Sopenharmony_ci		iblock = sb->fs_start + in->extents[dirext].cooked.ex_bn +
27362306a36Sopenharmony_ci			(cur - ibase) /
27462306a36Sopenharmony_ci			(EFS_BLOCKSIZE / sizeof(efs_extent));
27562306a36Sopenharmony_ci		ioffset = (cur - ibase) %
27662306a36Sopenharmony_ci			(EFS_BLOCKSIZE / sizeof(efs_extent));
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		if (first || lastblock != iblock) {
27962306a36Sopenharmony_ci			if (bh) brelse(bh);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci			bh = sb_bread(inode->i_sb, iblock);
28262306a36Sopenharmony_ci			if (!bh) {
28362306a36Sopenharmony_ci				pr_err("%s() failed at block %d\n",
28462306a36Sopenharmony_ci				       __func__, iblock);
28562306a36Sopenharmony_ci				return 0;
28662306a36Sopenharmony_ci			}
28762306a36Sopenharmony_ci			pr_debug("%s(): read indirect extent block %d\n",
28862306a36Sopenharmony_ci				 __func__, iblock);
28962306a36Sopenharmony_ci			first = 0;
29062306a36Sopenharmony_ci			lastblock = iblock;
29162306a36Sopenharmony_ci		}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		exts = (efs_extent *) bh->b_data;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		extent_copy(&(exts[ioffset]), &ext);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		if (ext.cooked.ex_magic != 0) {
29862306a36Sopenharmony_ci			pr_err("extent %d has bad magic number in block %d\n",
29962306a36Sopenharmony_ci			       cur, iblock);
30062306a36Sopenharmony_ci			if (bh) brelse(bh);
30162306a36Sopenharmony_ci			return 0;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		if ((result = efs_extent_check(&ext, block, sb))) {
30562306a36Sopenharmony_ci			if (bh) brelse(bh);
30662306a36Sopenharmony_ci			in->lastextent = cur;
30762306a36Sopenharmony_ci			return result;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	if (bh) brelse(bh);
31162306a36Sopenharmony_ci	pr_err("%s() failed to map block %u (indir)\n", __func__, block);
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
316