162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2000-2006 Silicon Graphics, Inc.
462306a36Sopenharmony_ci * All Rights Reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "xfs.h"
862306a36Sopenharmony_ci#include "xfs_fs.h"
962306a36Sopenharmony_ci#include "xfs_shared.h"
1062306a36Sopenharmony_ci#include "xfs_format.h"
1162306a36Sopenharmony_ci#include "xfs_log_format.h"
1262306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1362306a36Sopenharmony_ci#include "xfs_mount.h"
1462306a36Sopenharmony_ci#include "xfs_inode.h"
1562306a36Sopenharmony_ci#include "xfs_trans.h"
1662306a36Sopenharmony_ci#include "xfs_inode_item.h"
1762306a36Sopenharmony_ci#include "xfs_btree.h"
1862306a36Sopenharmony_ci#include "xfs_bmap_btree.h"
1962306a36Sopenharmony_ci#include "xfs_bmap.h"
2062306a36Sopenharmony_ci#include "xfs_error.h"
2162306a36Sopenharmony_ci#include "xfs_trace.h"
2262306a36Sopenharmony_ci#include "xfs_da_format.h"
2362306a36Sopenharmony_ci#include "xfs_da_btree.h"
2462306a36Sopenharmony_ci#include "xfs_dir2_priv.h"
2562306a36Sopenharmony_ci#include "xfs_attr_leaf.h"
2662306a36Sopenharmony_ci#include "xfs_types.h"
2762306a36Sopenharmony_ci#include "xfs_errortag.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct kmem_cache *xfs_ifork_cache;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_civoid
3262306a36Sopenharmony_cixfs_init_local_fork(
3362306a36Sopenharmony_ci	struct xfs_inode	*ip,
3462306a36Sopenharmony_ci	int			whichfork,
3562306a36Sopenharmony_ci	const void		*data,
3662306a36Sopenharmony_ci	int64_t			size)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
3962306a36Sopenharmony_ci	int			mem_size = size;
4062306a36Sopenharmony_ci	bool			zero_terminate;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/*
4362306a36Sopenharmony_ci	 * If we are using the local fork to store a symlink body we need to
4462306a36Sopenharmony_ci	 * zero-terminate it so that we can pass it back to the VFS directly.
4562306a36Sopenharmony_ci	 * Overallocate the in-memory fork by one for that and add a zero
4662306a36Sopenharmony_ci	 * to terminate it below.
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	zero_terminate = S_ISLNK(VFS_I(ip)->i_mode);
4962306a36Sopenharmony_ci	if (zero_terminate)
5062306a36Sopenharmony_ci		mem_size++;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (size) {
5362306a36Sopenharmony_ci		ifp->if_u1.if_data = kmem_alloc(mem_size, KM_NOFS);
5462306a36Sopenharmony_ci		memcpy(ifp->if_u1.if_data, data, size);
5562306a36Sopenharmony_ci		if (zero_terminate)
5662306a36Sopenharmony_ci			ifp->if_u1.if_data[size] = '\0';
5762306a36Sopenharmony_ci	} else {
5862306a36Sopenharmony_ci		ifp->if_u1.if_data = NULL;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	ifp->if_bytes = size;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * The file is in-lined in the on-disk inode.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_ciSTATIC int
6862306a36Sopenharmony_cixfs_iformat_local(
6962306a36Sopenharmony_ci	struct xfs_inode	*ip,
7062306a36Sopenharmony_ci	struct xfs_dinode	*dip,
7162306a36Sopenharmony_ci	int			whichfork,
7262306a36Sopenharmony_ci	int			size)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	/*
7562306a36Sopenharmony_ci	 * If the size is unreasonable, then something
7662306a36Sopenharmony_ci	 * is wrong and we just bail out rather than crash in
7762306a36Sopenharmony_ci	 * kmem_alloc() or memcpy() below.
7862306a36Sopenharmony_ci	 */
7962306a36Sopenharmony_ci	if (unlikely(size > XFS_DFORK_SIZE(dip, ip->i_mount, whichfork))) {
8062306a36Sopenharmony_ci		xfs_warn(ip->i_mount,
8162306a36Sopenharmony_ci	"corrupt inode %llu (bad size %d for local fork, size = %zd).",
8262306a36Sopenharmony_ci			(unsigned long long) ip->i_ino, size,
8362306a36Sopenharmony_ci			XFS_DFORK_SIZE(dip, ip->i_mount, whichfork));
8462306a36Sopenharmony_ci		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
8562306a36Sopenharmony_ci				"xfs_iformat_local", dip, sizeof(*dip),
8662306a36Sopenharmony_ci				__this_address);
8762306a36Sopenharmony_ci		return -EFSCORRUPTED;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	xfs_init_local_fork(ip, whichfork, XFS_DFORK_PTR(dip, whichfork), size);
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/*
9562306a36Sopenharmony_ci * The file consists of a set of extents all of which fit into the on-disk
9662306a36Sopenharmony_ci * inode.
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_ciSTATIC int
9962306a36Sopenharmony_cixfs_iformat_extents(
10062306a36Sopenharmony_ci	struct xfs_inode	*ip,
10162306a36Sopenharmony_ci	struct xfs_dinode	*dip,
10262306a36Sopenharmony_ci	int			whichfork)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
10562306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
10662306a36Sopenharmony_ci	int			state = xfs_bmap_fork_to_state(whichfork);
10762306a36Sopenharmony_ci	xfs_extnum_t		nex = xfs_dfork_nextents(dip, whichfork);
10862306a36Sopenharmony_ci	int			size = nex * sizeof(xfs_bmbt_rec_t);
10962306a36Sopenharmony_ci	struct xfs_iext_cursor	icur;
11062306a36Sopenharmony_ci	struct xfs_bmbt_rec	*dp;
11162306a36Sopenharmony_ci	struct xfs_bmbt_irec	new;
11262306a36Sopenharmony_ci	int			i;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/*
11562306a36Sopenharmony_ci	 * If the number of extents is unreasonable, then something is wrong and
11662306a36Sopenharmony_ci	 * we just bail out rather than crash in kmem_alloc() or memcpy() below.
11762306a36Sopenharmony_ci	 */
11862306a36Sopenharmony_ci	if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, mp, whichfork))) {
11962306a36Sopenharmony_ci		xfs_warn(ip->i_mount, "corrupt inode %llu ((a)extents = %llu).",
12062306a36Sopenharmony_ci			ip->i_ino, nex);
12162306a36Sopenharmony_ci		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
12262306a36Sopenharmony_ci				"xfs_iformat_extents(1)", dip, sizeof(*dip),
12362306a36Sopenharmony_ci				__this_address);
12462306a36Sopenharmony_ci		return -EFSCORRUPTED;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	ifp->if_bytes = 0;
12862306a36Sopenharmony_ci	ifp->if_u1.if_root = NULL;
12962306a36Sopenharmony_ci	ifp->if_height = 0;
13062306a36Sopenharmony_ci	if (size) {
13162306a36Sopenharmony_ci		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		xfs_iext_first(ifp, &icur);
13462306a36Sopenharmony_ci		for (i = 0; i < nex; i++, dp++) {
13562306a36Sopenharmony_ci			xfs_failaddr_t	fa;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci			xfs_bmbt_disk_get_all(dp, &new);
13862306a36Sopenharmony_ci			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
13962306a36Sopenharmony_ci			if (fa) {
14062306a36Sopenharmony_ci				xfs_inode_verifier_error(ip, -EFSCORRUPTED,
14162306a36Sopenharmony_ci						"xfs_iformat_extents(2)",
14262306a36Sopenharmony_ci						dp, sizeof(*dp), fa);
14362306a36Sopenharmony_ci				return xfs_bmap_complain_bad_rec(ip, whichfork,
14462306a36Sopenharmony_ci						fa, &new);
14562306a36Sopenharmony_ci			}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci			xfs_iext_insert(ip, &icur, &new, state);
14862306a36Sopenharmony_ci			trace_xfs_read_extent(ip, &icur, state, _THIS_IP_);
14962306a36Sopenharmony_ci			xfs_iext_next(ifp, &icur);
15062306a36Sopenharmony_ci		}
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * The file has too many extents to fit into
15762306a36Sopenharmony_ci * the inode, so they are in B-tree format.
15862306a36Sopenharmony_ci * Allocate a buffer for the root of the B-tree
15962306a36Sopenharmony_ci * and copy the root into it.  The i_extents
16062306a36Sopenharmony_ci * field will remain NULL until all of the
16162306a36Sopenharmony_ci * extents are read in (when they are needed).
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ciSTATIC int
16462306a36Sopenharmony_cixfs_iformat_btree(
16562306a36Sopenharmony_ci	struct xfs_inode	*ip,
16662306a36Sopenharmony_ci	struct xfs_dinode	*dip,
16762306a36Sopenharmony_ci	int			whichfork)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
17062306a36Sopenharmony_ci	xfs_bmdr_block_t	*dfp;
17162306a36Sopenharmony_ci	struct xfs_ifork	*ifp;
17262306a36Sopenharmony_ci	/* REFERENCED */
17362306a36Sopenharmony_ci	int			nrecs;
17462306a36Sopenharmony_ci	int			size;
17562306a36Sopenharmony_ci	int			level;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	ifp = xfs_ifork_ptr(ip, whichfork);
17862306a36Sopenharmony_ci	dfp = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
17962306a36Sopenharmony_ci	size = XFS_BMAP_BROOT_SPACE(mp, dfp);
18062306a36Sopenharmony_ci	nrecs = be16_to_cpu(dfp->bb_numrecs);
18162306a36Sopenharmony_ci	level = be16_to_cpu(dfp->bb_level);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * blow out if -- fork has less extents than can fit in
18562306a36Sopenharmony_ci	 * fork (fork shouldn't be a btree format), root btree
18662306a36Sopenharmony_ci	 * block has more records than can fit into the fork,
18762306a36Sopenharmony_ci	 * or the number of extents is greater than the number of
18862306a36Sopenharmony_ci	 * blocks.
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	if (unlikely(ifp->if_nextents <= XFS_IFORK_MAXEXT(ip, whichfork) ||
19162306a36Sopenharmony_ci		     nrecs == 0 ||
19262306a36Sopenharmony_ci		     XFS_BMDR_SPACE_CALC(nrecs) >
19362306a36Sopenharmony_ci					XFS_DFORK_SIZE(dip, mp, whichfork) ||
19462306a36Sopenharmony_ci		     ifp->if_nextents > ip->i_nblocks) ||
19562306a36Sopenharmony_ci		     level == 0 || level > XFS_BM_MAXLEVELS(mp, whichfork)) {
19662306a36Sopenharmony_ci		xfs_warn(mp, "corrupt inode %llu (btree).",
19762306a36Sopenharmony_ci					(unsigned long long) ip->i_ino);
19862306a36Sopenharmony_ci		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
19962306a36Sopenharmony_ci				"xfs_iformat_btree", dfp, size,
20062306a36Sopenharmony_ci				__this_address);
20162306a36Sopenharmony_ci		return -EFSCORRUPTED;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	ifp->if_broot_bytes = size;
20562306a36Sopenharmony_ci	ifp->if_broot = kmem_alloc(size, KM_NOFS);
20662306a36Sopenharmony_ci	ASSERT(ifp->if_broot != NULL);
20762306a36Sopenharmony_ci	/*
20862306a36Sopenharmony_ci	 * Copy and convert from the on-disk structure
20962306a36Sopenharmony_ci	 * to the in-memory structure.
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	xfs_bmdr_to_bmbt(ip, dfp, XFS_DFORK_SIZE(dip, ip->i_mount, whichfork),
21262306a36Sopenharmony_ci			 ifp->if_broot, size);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ifp->if_bytes = 0;
21562306a36Sopenharmony_ci	ifp->if_u1.if_root = NULL;
21662306a36Sopenharmony_ci	ifp->if_height = 0;
21762306a36Sopenharmony_ci	return 0;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciint
22162306a36Sopenharmony_cixfs_iformat_data_fork(
22262306a36Sopenharmony_ci	struct xfs_inode	*ip,
22362306a36Sopenharmony_ci	struct xfs_dinode	*dip)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct inode		*inode = VFS_I(ip);
22662306a36Sopenharmony_ci	int			error;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/*
22962306a36Sopenharmony_ci	 * Initialize the extent count early, as the per-format routines may
23062306a36Sopenharmony_ci	 * depend on it.  Use release semantics to set needextents /after/ we
23162306a36Sopenharmony_ci	 * set the format. This ensures that we can use acquire semantics on
23262306a36Sopenharmony_ci	 * needextents in xfs_need_iread_extents() and be guaranteed to see a
23362306a36Sopenharmony_ci	 * valid format value after that load.
23462306a36Sopenharmony_ci	 */
23562306a36Sopenharmony_ci	ip->i_df.if_format = dip->di_format;
23662306a36Sopenharmony_ci	ip->i_df.if_nextents = xfs_dfork_data_extents(dip);
23762306a36Sopenharmony_ci	smp_store_release(&ip->i_df.if_needextents,
23862306a36Sopenharmony_ci			   ip->i_df.if_format == XFS_DINODE_FMT_BTREE ? 1 : 0);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	switch (inode->i_mode & S_IFMT) {
24162306a36Sopenharmony_ci	case S_IFIFO:
24262306a36Sopenharmony_ci	case S_IFCHR:
24362306a36Sopenharmony_ci	case S_IFBLK:
24462306a36Sopenharmony_ci	case S_IFSOCK:
24562306a36Sopenharmony_ci		ip->i_disk_size = 0;
24662306a36Sopenharmony_ci		inode->i_rdev = xfs_to_linux_dev_t(xfs_dinode_get_rdev(dip));
24762306a36Sopenharmony_ci		return 0;
24862306a36Sopenharmony_ci	case S_IFREG:
24962306a36Sopenharmony_ci	case S_IFLNK:
25062306a36Sopenharmony_ci	case S_IFDIR:
25162306a36Sopenharmony_ci		switch (ip->i_df.if_format) {
25262306a36Sopenharmony_ci		case XFS_DINODE_FMT_LOCAL:
25362306a36Sopenharmony_ci			error = xfs_iformat_local(ip, dip, XFS_DATA_FORK,
25462306a36Sopenharmony_ci					be64_to_cpu(dip->di_size));
25562306a36Sopenharmony_ci			if (!error)
25662306a36Sopenharmony_ci				error = xfs_ifork_verify_local_data(ip);
25762306a36Sopenharmony_ci			return error;
25862306a36Sopenharmony_ci		case XFS_DINODE_FMT_EXTENTS:
25962306a36Sopenharmony_ci			return xfs_iformat_extents(ip, dip, XFS_DATA_FORK);
26062306a36Sopenharmony_ci		case XFS_DINODE_FMT_BTREE:
26162306a36Sopenharmony_ci			return xfs_iformat_btree(ip, dip, XFS_DATA_FORK);
26262306a36Sopenharmony_ci		default:
26362306a36Sopenharmony_ci			xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__,
26462306a36Sopenharmony_ci					dip, sizeof(*dip), __this_address);
26562306a36Sopenharmony_ci			return -EFSCORRUPTED;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci		break;
26862306a36Sopenharmony_ci	default:
26962306a36Sopenharmony_ci		xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip,
27062306a36Sopenharmony_ci				sizeof(*dip), __this_address);
27162306a36Sopenharmony_ci		return -EFSCORRUPTED;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic uint16_t
27662306a36Sopenharmony_cixfs_dfork_attr_shortform_size(
27762306a36Sopenharmony_ci	struct xfs_dinode		*dip)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct xfs_attr_shortform	*atp =
28062306a36Sopenharmony_ci		(struct xfs_attr_shortform *)XFS_DFORK_APTR(dip);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return be16_to_cpu(atp->hdr.totsize);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_civoid
28662306a36Sopenharmony_cixfs_ifork_init_attr(
28762306a36Sopenharmony_ci	struct xfs_inode	*ip,
28862306a36Sopenharmony_ci	enum xfs_dinode_fmt	format,
28962306a36Sopenharmony_ci	xfs_extnum_t		nextents)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	/*
29262306a36Sopenharmony_ci	 * Initialize the extent count early, as the per-format routines may
29362306a36Sopenharmony_ci	 * depend on it.  Use release semantics to set needextents /after/ we
29462306a36Sopenharmony_ci	 * set the format. This ensures that we can use acquire semantics on
29562306a36Sopenharmony_ci	 * needextents in xfs_need_iread_extents() and be guaranteed to see a
29662306a36Sopenharmony_ci	 * valid format value after that load.
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	ip->i_af.if_format = format;
29962306a36Sopenharmony_ci	ip->i_af.if_nextents = nextents;
30062306a36Sopenharmony_ci	smp_store_release(&ip->i_af.if_needextents,
30162306a36Sopenharmony_ci			   ip->i_af.if_format == XFS_DINODE_FMT_BTREE ? 1 : 0);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_civoid
30562306a36Sopenharmony_cixfs_ifork_zap_attr(
30662306a36Sopenharmony_ci	struct xfs_inode	*ip)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	xfs_idestroy_fork(&ip->i_af);
30962306a36Sopenharmony_ci	memset(&ip->i_af, 0, sizeof(struct xfs_ifork));
31062306a36Sopenharmony_ci	ip->i_af.if_format = XFS_DINODE_FMT_EXTENTS;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciint
31462306a36Sopenharmony_cixfs_iformat_attr_fork(
31562306a36Sopenharmony_ci	struct xfs_inode	*ip,
31662306a36Sopenharmony_ci	struct xfs_dinode	*dip)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	xfs_extnum_t		naextents = xfs_dfork_attr_extents(dip);
31962306a36Sopenharmony_ci	int			error = 0;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/*
32262306a36Sopenharmony_ci	 * Initialize the extent count early, as the per-format routines may
32362306a36Sopenharmony_ci	 * depend on it.
32462306a36Sopenharmony_ci	 */
32562306a36Sopenharmony_ci	xfs_ifork_init_attr(ip, dip->di_aformat, naextents);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	switch (ip->i_af.if_format) {
32862306a36Sopenharmony_ci	case XFS_DINODE_FMT_LOCAL:
32962306a36Sopenharmony_ci		error = xfs_iformat_local(ip, dip, XFS_ATTR_FORK,
33062306a36Sopenharmony_ci				xfs_dfork_attr_shortform_size(dip));
33162306a36Sopenharmony_ci		if (!error)
33262306a36Sopenharmony_ci			error = xfs_ifork_verify_local_attr(ip);
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	case XFS_DINODE_FMT_EXTENTS:
33562306a36Sopenharmony_ci		error = xfs_iformat_extents(ip, dip, XFS_ATTR_FORK);
33662306a36Sopenharmony_ci		break;
33762306a36Sopenharmony_ci	case XFS_DINODE_FMT_BTREE:
33862306a36Sopenharmony_ci		error = xfs_iformat_btree(ip, dip, XFS_ATTR_FORK);
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	default:
34162306a36Sopenharmony_ci		xfs_inode_verifier_error(ip, error, __func__, dip,
34262306a36Sopenharmony_ci				sizeof(*dip), __this_address);
34362306a36Sopenharmony_ci		error = -EFSCORRUPTED;
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (error)
34862306a36Sopenharmony_ci		xfs_ifork_zap_attr(ip);
34962306a36Sopenharmony_ci	return error;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/*
35362306a36Sopenharmony_ci * Reallocate the space for if_broot based on the number of records
35462306a36Sopenharmony_ci * being added or deleted as indicated in rec_diff.  Move the records
35562306a36Sopenharmony_ci * and pointers in if_broot to fit the new size.  When shrinking this
35662306a36Sopenharmony_ci * will eliminate holes between the records and pointers created by
35762306a36Sopenharmony_ci * the caller.  When growing this will create holes to be filled in
35862306a36Sopenharmony_ci * by the caller.
35962306a36Sopenharmony_ci *
36062306a36Sopenharmony_ci * The caller must not request to add more records than would fit in
36162306a36Sopenharmony_ci * the on-disk inode root.  If the if_broot is currently NULL, then
36262306a36Sopenharmony_ci * if we are adding records, one will be allocated.  The caller must also
36362306a36Sopenharmony_ci * not request that the number of records go below zero, although
36462306a36Sopenharmony_ci * it can go to zero.
36562306a36Sopenharmony_ci *
36662306a36Sopenharmony_ci * ip -- the inode whose if_broot area is changing
36762306a36Sopenharmony_ci * ext_diff -- the change in the number of records, positive or negative,
36862306a36Sopenharmony_ci *	 requested for the if_broot array.
36962306a36Sopenharmony_ci */
37062306a36Sopenharmony_civoid
37162306a36Sopenharmony_cixfs_iroot_realloc(
37262306a36Sopenharmony_ci	xfs_inode_t		*ip,
37362306a36Sopenharmony_ci	int			rec_diff,
37462306a36Sopenharmony_ci	int			whichfork)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
37762306a36Sopenharmony_ci	int			cur_max;
37862306a36Sopenharmony_ci	struct xfs_ifork	*ifp;
37962306a36Sopenharmony_ci	struct xfs_btree_block	*new_broot;
38062306a36Sopenharmony_ci	int			new_max;
38162306a36Sopenharmony_ci	size_t			new_size;
38262306a36Sopenharmony_ci	char			*np;
38362306a36Sopenharmony_ci	char			*op;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/*
38662306a36Sopenharmony_ci	 * Handle the degenerate case quietly.
38762306a36Sopenharmony_ci	 */
38862306a36Sopenharmony_ci	if (rec_diff == 0) {
38962306a36Sopenharmony_ci		return;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ifp = xfs_ifork_ptr(ip, whichfork);
39362306a36Sopenharmony_ci	if (rec_diff > 0) {
39462306a36Sopenharmony_ci		/*
39562306a36Sopenharmony_ci		 * If there wasn't any memory allocated before, just
39662306a36Sopenharmony_ci		 * allocate it now and get out.
39762306a36Sopenharmony_ci		 */
39862306a36Sopenharmony_ci		if (ifp->if_broot_bytes == 0) {
39962306a36Sopenharmony_ci			new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, rec_diff);
40062306a36Sopenharmony_ci			ifp->if_broot = kmem_alloc(new_size, KM_NOFS);
40162306a36Sopenharmony_ci			ifp->if_broot_bytes = (int)new_size;
40262306a36Sopenharmony_ci			return;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		/*
40662306a36Sopenharmony_ci		 * If there is already an existing if_broot, then we need
40762306a36Sopenharmony_ci		 * to realloc() it and shift the pointers to their new
40862306a36Sopenharmony_ci		 * location.  The records don't change location because
40962306a36Sopenharmony_ci		 * they are kept butted up against the btree block header.
41062306a36Sopenharmony_ci		 */
41162306a36Sopenharmony_ci		cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0);
41262306a36Sopenharmony_ci		new_max = cur_max + rec_diff;
41362306a36Sopenharmony_ci		new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, new_max);
41462306a36Sopenharmony_ci		ifp->if_broot = krealloc(ifp->if_broot, new_size,
41562306a36Sopenharmony_ci					 GFP_NOFS | __GFP_NOFAIL);
41662306a36Sopenharmony_ci		op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
41762306a36Sopenharmony_ci						     ifp->if_broot_bytes);
41862306a36Sopenharmony_ci		np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
41962306a36Sopenharmony_ci						     (int)new_size);
42062306a36Sopenharmony_ci		ifp->if_broot_bytes = (int)new_size;
42162306a36Sopenharmony_ci		ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
42262306a36Sopenharmony_ci			xfs_inode_fork_size(ip, whichfork));
42362306a36Sopenharmony_ci		memmove(np, op, cur_max * (uint)sizeof(xfs_fsblock_t));
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/*
42862306a36Sopenharmony_ci	 * rec_diff is less than 0.  In this case, we are shrinking the
42962306a36Sopenharmony_ci	 * if_broot buffer.  It must already exist.  If we go to zero
43062306a36Sopenharmony_ci	 * records, just get rid of the root and clear the status bit.
43162306a36Sopenharmony_ci	 */
43262306a36Sopenharmony_ci	ASSERT((ifp->if_broot != NULL) && (ifp->if_broot_bytes > 0));
43362306a36Sopenharmony_ci	cur_max = xfs_bmbt_maxrecs(mp, ifp->if_broot_bytes, 0);
43462306a36Sopenharmony_ci	new_max = cur_max + rec_diff;
43562306a36Sopenharmony_ci	ASSERT(new_max >= 0);
43662306a36Sopenharmony_ci	if (new_max > 0)
43762306a36Sopenharmony_ci		new_size = XFS_BMAP_BROOT_SPACE_CALC(mp, new_max);
43862306a36Sopenharmony_ci	else
43962306a36Sopenharmony_ci		new_size = 0;
44062306a36Sopenharmony_ci	if (new_size > 0) {
44162306a36Sopenharmony_ci		new_broot = kmem_alloc(new_size, KM_NOFS);
44262306a36Sopenharmony_ci		/*
44362306a36Sopenharmony_ci		 * First copy over the btree block header.
44462306a36Sopenharmony_ci		 */
44562306a36Sopenharmony_ci		memcpy(new_broot, ifp->if_broot,
44662306a36Sopenharmony_ci			XFS_BMBT_BLOCK_LEN(ip->i_mount));
44762306a36Sopenharmony_ci	} else {
44862306a36Sopenharmony_ci		new_broot = NULL;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/*
45262306a36Sopenharmony_ci	 * Only copy the records and pointers if there are any.
45362306a36Sopenharmony_ci	 */
45462306a36Sopenharmony_ci	if (new_max > 0) {
45562306a36Sopenharmony_ci		/*
45662306a36Sopenharmony_ci		 * First copy the records.
45762306a36Sopenharmony_ci		 */
45862306a36Sopenharmony_ci		op = (char *)XFS_BMBT_REC_ADDR(mp, ifp->if_broot, 1);
45962306a36Sopenharmony_ci		np = (char *)XFS_BMBT_REC_ADDR(mp, new_broot, 1);
46062306a36Sopenharmony_ci		memcpy(np, op, new_max * (uint)sizeof(xfs_bmbt_rec_t));
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		/*
46362306a36Sopenharmony_ci		 * Then copy the pointers.
46462306a36Sopenharmony_ci		 */
46562306a36Sopenharmony_ci		op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
46662306a36Sopenharmony_ci						     ifp->if_broot_bytes);
46762306a36Sopenharmony_ci		np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, new_broot, 1,
46862306a36Sopenharmony_ci						     (int)new_size);
46962306a36Sopenharmony_ci		memcpy(np, op, new_max * (uint)sizeof(xfs_fsblock_t));
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci	kmem_free(ifp->if_broot);
47262306a36Sopenharmony_ci	ifp->if_broot = new_broot;
47362306a36Sopenharmony_ci	ifp->if_broot_bytes = (int)new_size;
47462306a36Sopenharmony_ci	if (ifp->if_broot)
47562306a36Sopenharmony_ci		ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
47662306a36Sopenharmony_ci			xfs_inode_fork_size(ip, whichfork));
47762306a36Sopenharmony_ci	return;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci/*
48262306a36Sopenharmony_ci * This is called when the amount of space needed for if_data
48362306a36Sopenharmony_ci * is increased or decreased.  The change in size is indicated by
48462306a36Sopenharmony_ci * the number of bytes that need to be added or deleted in the
48562306a36Sopenharmony_ci * byte_diff parameter.
48662306a36Sopenharmony_ci *
48762306a36Sopenharmony_ci * If the amount of space needed has decreased below the size of the
48862306a36Sopenharmony_ci * inline buffer, then switch to using the inline buffer.  Otherwise,
48962306a36Sopenharmony_ci * use kmem_realloc() or kmem_alloc() to adjust the size of the buffer
49062306a36Sopenharmony_ci * to what is needed.
49162306a36Sopenharmony_ci *
49262306a36Sopenharmony_ci * ip -- the inode whose if_data area is changing
49362306a36Sopenharmony_ci * byte_diff -- the change in the number of bytes, positive or negative,
49462306a36Sopenharmony_ci *	 requested for the if_data array.
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_civoid
49762306a36Sopenharmony_cixfs_idata_realloc(
49862306a36Sopenharmony_ci	struct xfs_inode	*ip,
49962306a36Sopenharmony_ci	int64_t			byte_diff,
50062306a36Sopenharmony_ci	int			whichfork)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
50362306a36Sopenharmony_ci	int64_t			new_size = ifp->if_bytes + byte_diff;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ASSERT(new_size >= 0);
50662306a36Sopenharmony_ci	ASSERT(new_size <= xfs_inode_fork_size(ip, whichfork));
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (byte_diff == 0)
50962306a36Sopenharmony_ci		return;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (new_size == 0) {
51262306a36Sopenharmony_ci		kmem_free(ifp->if_u1.if_data);
51362306a36Sopenharmony_ci		ifp->if_u1.if_data = NULL;
51462306a36Sopenharmony_ci		ifp->if_bytes = 0;
51562306a36Sopenharmony_ci		return;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	ifp->if_u1.if_data = krealloc(ifp->if_u1.if_data, new_size,
51962306a36Sopenharmony_ci				      GFP_NOFS | __GFP_NOFAIL);
52062306a36Sopenharmony_ci	ifp->if_bytes = new_size;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_civoid
52462306a36Sopenharmony_cixfs_idestroy_fork(
52562306a36Sopenharmony_ci	struct xfs_ifork	*ifp)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	if (ifp->if_broot != NULL) {
52862306a36Sopenharmony_ci		kmem_free(ifp->if_broot);
52962306a36Sopenharmony_ci		ifp->if_broot = NULL;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	switch (ifp->if_format) {
53362306a36Sopenharmony_ci	case XFS_DINODE_FMT_LOCAL:
53462306a36Sopenharmony_ci		kmem_free(ifp->if_u1.if_data);
53562306a36Sopenharmony_ci		ifp->if_u1.if_data = NULL;
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci	case XFS_DINODE_FMT_EXTENTS:
53862306a36Sopenharmony_ci	case XFS_DINODE_FMT_BTREE:
53962306a36Sopenharmony_ci		if (ifp->if_height)
54062306a36Sopenharmony_ci			xfs_iext_destroy(ifp);
54162306a36Sopenharmony_ci		break;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/*
54662306a36Sopenharmony_ci * Convert in-core extents to on-disk form
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci * In the case of the data fork, the in-core and on-disk fork sizes can be
54962306a36Sopenharmony_ci * different due to delayed allocation extents. We only copy on-disk extents
55062306a36Sopenharmony_ci * here, so callers must always use the physical fork size to determine the
55162306a36Sopenharmony_ci * size of the buffer passed to this routine.  We will return the size actually
55262306a36Sopenharmony_ci * used.
55362306a36Sopenharmony_ci */
55462306a36Sopenharmony_ciint
55562306a36Sopenharmony_cixfs_iextents_copy(
55662306a36Sopenharmony_ci	struct xfs_inode	*ip,
55762306a36Sopenharmony_ci	struct xfs_bmbt_rec	*dp,
55862306a36Sopenharmony_ci	int			whichfork)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	int			state = xfs_bmap_fork_to_state(whichfork);
56162306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
56262306a36Sopenharmony_ci	struct xfs_iext_cursor	icur;
56362306a36Sopenharmony_ci	struct xfs_bmbt_irec	rec;
56462306a36Sopenharmony_ci	int64_t			copied = 0;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
56762306a36Sopenharmony_ci	ASSERT(ifp->if_bytes > 0);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	for_each_xfs_iext(ifp, &icur, &rec) {
57062306a36Sopenharmony_ci		if (isnullstartblock(rec.br_startblock))
57162306a36Sopenharmony_ci			continue;
57262306a36Sopenharmony_ci		ASSERT(xfs_bmap_validate_extent(ip, whichfork, &rec) == NULL);
57362306a36Sopenharmony_ci		xfs_bmbt_disk_set_all(dp, &rec);
57462306a36Sopenharmony_ci		trace_xfs_write_extent(ip, &icur, state, _RET_IP_);
57562306a36Sopenharmony_ci		copied += sizeof(struct xfs_bmbt_rec);
57662306a36Sopenharmony_ci		dp++;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	ASSERT(copied > 0);
58062306a36Sopenharmony_ci	ASSERT(copied <= ifp->if_bytes);
58162306a36Sopenharmony_ci	return copied;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci/*
58562306a36Sopenharmony_ci * Each of the following cases stores data into the same region
58662306a36Sopenharmony_ci * of the on-disk inode, so only one of them can be valid at
58762306a36Sopenharmony_ci * any given time. While it is possible to have conflicting formats
58862306a36Sopenharmony_ci * and log flags, e.g. having XFS_ILOG_?DATA set when the fork is
58962306a36Sopenharmony_ci * in EXTENTS format, this can only happen when the fork has
59062306a36Sopenharmony_ci * changed formats after being modified but before being flushed.
59162306a36Sopenharmony_ci * In these cases, the format always takes precedence, because the
59262306a36Sopenharmony_ci * format indicates the current state of the fork.
59362306a36Sopenharmony_ci */
59462306a36Sopenharmony_civoid
59562306a36Sopenharmony_cixfs_iflush_fork(
59662306a36Sopenharmony_ci	struct xfs_inode	*ip,
59762306a36Sopenharmony_ci	struct xfs_dinode	*dip,
59862306a36Sopenharmony_ci	struct xfs_inode_log_item *iip,
59962306a36Sopenharmony_ci	int			whichfork)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	char			*cp;
60262306a36Sopenharmony_ci	struct xfs_ifork	*ifp;
60362306a36Sopenharmony_ci	xfs_mount_t		*mp;
60462306a36Sopenharmony_ci	static const short	brootflag[2] =
60562306a36Sopenharmony_ci		{ XFS_ILOG_DBROOT, XFS_ILOG_ABROOT };
60662306a36Sopenharmony_ci	static const short	dataflag[2] =
60762306a36Sopenharmony_ci		{ XFS_ILOG_DDATA, XFS_ILOG_ADATA };
60862306a36Sopenharmony_ci	static const short	extflag[2] =
60962306a36Sopenharmony_ci		{ XFS_ILOG_DEXT, XFS_ILOG_AEXT };
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (!iip)
61262306a36Sopenharmony_ci		return;
61362306a36Sopenharmony_ci	ifp = xfs_ifork_ptr(ip, whichfork);
61462306a36Sopenharmony_ci	/*
61562306a36Sopenharmony_ci	 * This can happen if we gave up in iformat in an error path,
61662306a36Sopenharmony_ci	 * for the attribute fork.
61762306a36Sopenharmony_ci	 */
61862306a36Sopenharmony_ci	if (!ifp) {
61962306a36Sopenharmony_ci		ASSERT(whichfork == XFS_ATTR_FORK);
62062306a36Sopenharmony_ci		return;
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci	cp = XFS_DFORK_PTR(dip, whichfork);
62362306a36Sopenharmony_ci	mp = ip->i_mount;
62462306a36Sopenharmony_ci	switch (ifp->if_format) {
62562306a36Sopenharmony_ci	case XFS_DINODE_FMT_LOCAL:
62662306a36Sopenharmony_ci		if ((iip->ili_fields & dataflag[whichfork]) &&
62762306a36Sopenharmony_ci		    (ifp->if_bytes > 0)) {
62862306a36Sopenharmony_ci			ASSERT(ifp->if_u1.if_data != NULL);
62962306a36Sopenharmony_ci			ASSERT(ifp->if_bytes <= xfs_inode_fork_size(ip, whichfork));
63062306a36Sopenharmony_ci			memcpy(cp, ifp->if_u1.if_data, ifp->if_bytes);
63162306a36Sopenharmony_ci		}
63262306a36Sopenharmony_ci		break;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	case XFS_DINODE_FMT_EXTENTS:
63562306a36Sopenharmony_ci		if ((iip->ili_fields & extflag[whichfork]) &&
63662306a36Sopenharmony_ci		    (ifp->if_bytes > 0)) {
63762306a36Sopenharmony_ci			ASSERT(ifp->if_nextents > 0);
63862306a36Sopenharmony_ci			(void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
63962306a36Sopenharmony_ci				whichfork);
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci		break;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	case XFS_DINODE_FMT_BTREE:
64462306a36Sopenharmony_ci		if ((iip->ili_fields & brootflag[whichfork]) &&
64562306a36Sopenharmony_ci		    (ifp->if_broot_bytes > 0)) {
64662306a36Sopenharmony_ci			ASSERT(ifp->if_broot != NULL);
64762306a36Sopenharmony_ci			ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
64862306a36Sopenharmony_ci			        xfs_inode_fork_size(ip, whichfork));
64962306a36Sopenharmony_ci			xfs_bmbt_to_bmdr(mp, ifp->if_broot, ifp->if_broot_bytes,
65062306a36Sopenharmony_ci				(xfs_bmdr_block_t *)cp,
65162306a36Sopenharmony_ci				XFS_DFORK_SIZE(dip, mp, whichfork));
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci		break;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	case XFS_DINODE_FMT_DEV:
65662306a36Sopenharmony_ci		if (iip->ili_fields & XFS_ILOG_DEV) {
65762306a36Sopenharmony_ci			ASSERT(whichfork == XFS_DATA_FORK);
65862306a36Sopenharmony_ci			xfs_dinode_put_rdev(dip,
65962306a36Sopenharmony_ci					linux_to_xfs_dev_t(VFS_I(ip)->i_rdev));
66062306a36Sopenharmony_ci		}
66162306a36Sopenharmony_ci		break;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	default:
66462306a36Sopenharmony_ci		ASSERT(0);
66562306a36Sopenharmony_ci		break;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci/* Convert bmap state flags to an inode fork. */
67062306a36Sopenharmony_cistruct xfs_ifork *
67162306a36Sopenharmony_cixfs_iext_state_to_fork(
67262306a36Sopenharmony_ci	struct xfs_inode	*ip,
67362306a36Sopenharmony_ci	int			state)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	if (state & BMAP_COWFORK)
67662306a36Sopenharmony_ci		return ip->i_cowfp;
67762306a36Sopenharmony_ci	else if (state & BMAP_ATTRFORK)
67862306a36Sopenharmony_ci		return &ip->i_af;
67962306a36Sopenharmony_ci	return &ip->i_df;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/*
68362306a36Sopenharmony_ci * Initialize an inode's copy-on-write fork.
68462306a36Sopenharmony_ci */
68562306a36Sopenharmony_civoid
68662306a36Sopenharmony_cixfs_ifork_init_cow(
68762306a36Sopenharmony_ci	struct xfs_inode	*ip)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	if (ip->i_cowfp)
69062306a36Sopenharmony_ci		return;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	ip->i_cowfp = kmem_cache_zalloc(xfs_ifork_cache,
69362306a36Sopenharmony_ci				       GFP_NOFS | __GFP_NOFAIL);
69462306a36Sopenharmony_ci	ip->i_cowfp->if_format = XFS_DINODE_FMT_EXTENTS;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci/* Verify the inline contents of the data fork of an inode. */
69862306a36Sopenharmony_ciint
69962306a36Sopenharmony_cixfs_ifork_verify_local_data(
70062306a36Sopenharmony_ci	struct xfs_inode	*ip)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	xfs_failaddr_t		fa = NULL;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	switch (VFS_I(ip)->i_mode & S_IFMT) {
70562306a36Sopenharmony_ci	case S_IFDIR:
70662306a36Sopenharmony_ci		fa = xfs_dir2_sf_verify(ip);
70762306a36Sopenharmony_ci		break;
70862306a36Sopenharmony_ci	case S_IFLNK:
70962306a36Sopenharmony_ci		fa = xfs_symlink_shortform_verify(ip);
71062306a36Sopenharmony_ci		break;
71162306a36Sopenharmony_ci	default:
71262306a36Sopenharmony_ci		break;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (fa) {
71662306a36Sopenharmony_ci		xfs_inode_verifier_error(ip, -EFSCORRUPTED, "data fork",
71762306a36Sopenharmony_ci				ip->i_df.if_u1.if_data, ip->i_df.if_bytes, fa);
71862306a36Sopenharmony_ci		return -EFSCORRUPTED;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	return 0;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci/* Verify the inline contents of the attr fork of an inode. */
72562306a36Sopenharmony_ciint
72662306a36Sopenharmony_cixfs_ifork_verify_local_attr(
72762306a36Sopenharmony_ci	struct xfs_inode	*ip)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct xfs_ifork	*ifp = &ip->i_af;
73062306a36Sopenharmony_ci	xfs_failaddr_t		fa;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (!xfs_inode_has_attr_fork(ip))
73362306a36Sopenharmony_ci		fa = __this_address;
73462306a36Sopenharmony_ci	else
73562306a36Sopenharmony_ci		fa = xfs_attr_shortform_verify(ip);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (fa) {
73862306a36Sopenharmony_ci		xfs_inode_verifier_error(ip, -EFSCORRUPTED, "attr fork",
73962306a36Sopenharmony_ci				ifp->if_u1.if_data, ifp->if_bytes, fa);
74062306a36Sopenharmony_ci		return -EFSCORRUPTED;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	return 0;
74462306a36Sopenharmony_ci}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ciint
74762306a36Sopenharmony_cixfs_iext_count_may_overflow(
74862306a36Sopenharmony_ci	struct xfs_inode	*ip,
74962306a36Sopenharmony_ci	int			whichfork,
75062306a36Sopenharmony_ci	int			nr_to_add)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
75362306a36Sopenharmony_ci	uint64_t		max_exts;
75462306a36Sopenharmony_ci	uint64_t		nr_exts;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	if (whichfork == XFS_COW_FORK)
75762306a36Sopenharmony_ci		return 0;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	max_exts = xfs_iext_max_nextents(xfs_inode_has_large_extent_counts(ip),
76062306a36Sopenharmony_ci				whichfork);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (XFS_TEST_ERROR(false, ip->i_mount, XFS_ERRTAG_REDUCE_MAX_IEXTENTS))
76362306a36Sopenharmony_ci		max_exts = 10;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	nr_exts = ifp->if_nextents + nr_to_add;
76662306a36Sopenharmony_ci	if (nr_exts < ifp->if_nextents || nr_exts > max_exts)
76762306a36Sopenharmony_ci		return -EFBIG;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	return 0;
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci/*
77362306a36Sopenharmony_ci * Upgrade this inode's extent counter fields to be able to handle a potential
77462306a36Sopenharmony_ci * increase in the extent count by nr_to_add.  Normally this is the same
77562306a36Sopenharmony_ci * quantity that caused xfs_iext_count_may_overflow() to return -EFBIG.
77662306a36Sopenharmony_ci */
77762306a36Sopenharmony_ciint
77862306a36Sopenharmony_cixfs_iext_count_upgrade(
77962306a36Sopenharmony_ci	struct xfs_trans	*tp,
78062306a36Sopenharmony_ci	struct xfs_inode	*ip,
78162306a36Sopenharmony_ci	uint			nr_to_add)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	ASSERT(nr_to_add <= XFS_MAX_EXTCNT_UPGRADE_NR);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (!xfs_has_large_extent_counts(ip->i_mount) ||
78662306a36Sopenharmony_ci	    xfs_inode_has_large_extent_counts(ip) ||
78762306a36Sopenharmony_ci	    XFS_TEST_ERROR(false, ip->i_mount, XFS_ERRTAG_REDUCE_MAX_IEXTENTS))
78862306a36Sopenharmony_ci		return -EFBIG;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	ip->i_diflags2 |= XFS_DIFLAG2_NREXT64;
79162306a36Sopenharmony_ci	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	return 0;
79462306a36Sopenharmony_ci}
795