162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2000-2005 Silicon Graphics, Inc.
462306a36Sopenharmony_ci * All Rights Reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "xfs.h"
762306a36Sopenharmony_ci#include "xfs_fs.h"
862306a36Sopenharmony_ci#include "xfs_shared.h"
962306a36Sopenharmony_ci#include "xfs_format.h"
1062306a36Sopenharmony_ci#include "xfs_log_format.h"
1162306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1262306a36Sopenharmony_ci#include "xfs_mount.h"
1362306a36Sopenharmony_ci#include "xfs_defer.h"
1462306a36Sopenharmony_ci#include "xfs_da_format.h"
1562306a36Sopenharmony_ci#include "xfs_da_btree.h"
1662306a36Sopenharmony_ci#include "xfs_attr_sf.h"
1762306a36Sopenharmony_ci#include "xfs_inode.h"
1862306a36Sopenharmony_ci#include "xfs_trans.h"
1962306a36Sopenharmony_ci#include "xfs_bmap.h"
2062306a36Sopenharmony_ci#include "xfs_bmap_btree.h"
2162306a36Sopenharmony_ci#include "xfs_attr.h"
2262306a36Sopenharmony_ci#include "xfs_attr_leaf.h"
2362306a36Sopenharmony_ci#include "xfs_attr_remote.h"
2462306a36Sopenharmony_ci#include "xfs_quota.h"
2562306a36Sopenharmony_ci#include "xfs_trans_space.h"
2662306a36Sopenharmony_ci#include "xfs_trace.h"
2762306a36Sopenharmony_ci#include "xfs_attr_item.h"
2862306a36Sopenharmony_ci#include "xfs_xattr.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct kmem_cache		*xfs_attr_intent_cache;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * xfs_attr.c
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Provide the external interfaces to manage attribute lists.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*========================================================================
3962306a36Sopenharmony_ci * Function prototypes for the kernel.
4062306a36Sopenharmony_ci *========================================================================*/
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * Internal routines when attribute list fits inside the inode.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_ciSTATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Internal routines when attribute list is one block.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ciSTATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
5162306a36Sopenharmony_ciSTATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
5262306a36Sopenharmony_ciSTATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
5362306a36Sopenharmony_ciSTATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci * Internal routines when attribute list is more than one block.
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ciSTATIC int xfs_attr_node_get(xfs_da_args_t *args);
5962306a36Sopenharmony_ciSTATIC void xfs_attr_restore_rmt_blk(struct xfs_da_args *args);
6062306a36Sopenharmony_cistatic int xfs_attr_node_try_addname(struct xfs_attr_intent *attr);
6162306a36Sopenharmony_ciSTATIC int xfs_attr_node_addname_find_attr(struct xfs_attr_intent *attr);
6262306a36Sopenharmony_ciSTATIC int xfs_attr_node_remove_attr(struct xfs_attr_intent *attr);
6362306a36Sopenharmony_ciSTATIC int xfs_attr_node_lookup(struct xfs_da_args *args,
6462306a36Sopenharmony_ci		struct xfs_da_state *state);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciint
6762306a36Sopenharmony_cixfs_inode_hasattr(
6862306a36Sopenharmony_ci	struct xfs_inode	*ip)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	if (!xfs_inode_has_attr_fork(ip))
7162306a36Sopenharmony_ci		return 0;
7262306a36Sopenharmony_ci	if (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&
7362306a36Sopenharmony_ci	    ip->i_af.if_nextents == 0)
7462306a36Sopenharmony_ci		return 0;
7562306a36Sopenharmony_ci	return 1;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * Returns true if the there is exactly only block in the attr fork, in which
8062306a36Sopenharmony_ci * case the attribute fork consists of a single leaf block entry.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cibool
8362306a36Sopenharmony_cixfs_attr_is_leaf(
8462306a36Sopenharmony_ci	struct xfs_inode	*ip)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct xfs_ifork	*ifp = &ip->i_af;
8762306a36Sopenharmony_ci	struct xfs_iext_cursor	icur;
8862306a36Sopenharmony_ci	struct xfs_bmbt_irec	imap;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (ifp->if_nextents != 1 || ifp->if_format != XFS_DINODE_FMT_EXTENTS)
9162306a36Sopenharmony_ci		return false;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	xfs_iext_first(ifp, &icur);
9462306a36Sopenharmony_ci	xfs_iext_get_extent(ifp, &icur, &imap);
9562306a36Sopenharmony_ci	return imap.br_startoff == 0 && imap.br_blockcount == 1;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/*
9962306a36Sopenharmony_ci * XXX (dchinner): name path state saving and refilling is an optimisation to
10062306a36Sopenharmony_ci * avoid needing to look up name entries after rolling transactions removing
10162306a36Sopenharmony_ci * remote xattr blocks between the name entry lookup and name entry removal.
10262306a36Sopenharmony_ci * This optimisation got sidelined when combining the set and remove state
10362306a36Sopenharmony_ci * machines, but the code has been left in place because it is worthwhile to
10462306a36Sopenharmony_ci * restore the optimisation once the combined state machine paths have settled.
10562306a36Sopenharmony_ci *
10662306a36Sopenharmony_ci * This comment is a public service announcement to remind Future Dave that he
10762306a36Sopenharmony_ci * still needs to restore this code to working order.
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_ci#if 0
11062306a36Sopenharmony_ci/*
11162306a36Sopenharmony_ci * Fill in the disk block numbers in the state structure for the buffers
11262306a36Sopenharmony_ci * that are attached to the state structure.
11362306a36Sopenharmony_ci * This is done so that we can quickly reattach ourselves to those buffers
11462306a36Sopenharmony_ci * after some set of transaction commits have released these buffers.
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_cistatic int
11762306a36Sopenharmony_cixfs_attr_fillstate(xfs_da_state_t *state)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	xfs_da_state_path_t *path;
12062306a36Sopenharmony_ci	xfs_da_state_blk_t *blk;
12162306a36Sopenharmony_ci	int level;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	trace_xfs_attr_fillstate(state->args);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/*
12662306a36Sopenharmony_ci	 * Roll down the "path" in the state structure, storing the on-disk
12762306a36Sopenharmony_ci	 * block number for those buffers in the "path".
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	path = &state->path;
13062306a36Sopenharmony_ci	ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
13162306a36Sopenharmony_ci	for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
13262306a36Sopenharmony_ci		if (blk->bp) {
13362306a36Sopenharmony_ci			blk->disk_blkno = xfs_buf_daddr(blk->bp);
13462306a36Sopenharmony_ci			blk->bp = NULL;
13562306a36Sopenharmony_ci		} else {
13662306a36Sopenharmony_ci			blk->disk_blkno = 0;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * Roll down the "altpath" in the state structure, storing the on-disk
14262306a36Sopenharmony_ci	 * block number for those buffers in the "altpath".
14362306a36Sopenharmony_ci	 */
14462306a36Sopenharmony_ci	path = &state->altpath;
14562306a36Sopenharmony_ci	ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
14662306a36Sopenharmony_ci	for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
14762306a36Sopenharmony_ci		if (blk->bp) {
14862306a36Sopenharmony_ci			blk->disk_blkno = xfs_buf_daddr(blk->bp);
14962306a36Sopenharmony_ci			blk->bp = NULL;
15062306a36Sopenharmony_ci		} else {
15162306a36Sopenharmony_ci			blk->disk_blkno = 0;
15262306a36Sopenharmony_ci		}
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * Reattach the buffers to the state structure based on the disk block
16062306a36Sopenharmony_ci * numbers stored in the state structure.
16162306a36Sopenharmony_ci * This is done after some set of transaction commits have released those
16262306a36Sopenharmony_ci * buffers from our grip.
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_cistatic int
16562306a36Sopenharmony_cixfs_attr_refillstate(xfs_da_state_t *state)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	xfs_da_state_path_t *path;
16862306a36Sopenharmony_ci	xfs_da_state_blk_t *blk;
16962306a36Sopenharmony_ci	int level, error;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	trace_xfs_attr_refillstate(state->args);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/*
17462306a36Sopenharmony_ci	 * Roll down the "path" in the state structure, storing the on-disk
17562306a36Sopenharmony_ci	 * block number for those buffers in the "path".
17662306a36Sopenharmony_ci	 */
17762306a36Sopenharmony_ci	path = &state->path;
17862306a36Sopenharmony_ci	ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
17962306a36Sopenharmony_ci	for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
18062306a36Sopenharmony_ci		if (blk->disk_blkno) {
18162306a36Sopenharmony_ci			error = xfs_da3_node_read_mapped(state->args->trans,
18262306a36Sopenharmony_ci					state->args->dp, blk->disk_blkno,
18362306a36Sopenharmony_ci					&blk->bp, XFS_ATTR_FORK);
18462306a36Sopenharmony_ci			if (error)
18562306a36Sopenharmony_ci				return error;
18662306a36Sopenharmony_ci		} else {
18762306a36Sopenharmony_ci			blk->bp = NULL;
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/*
19262306a36Sopenharmony_ci	 * Roll down the "altpath" in the state structure, storing the on-disk
19362306a36Sopenharmony_ci	 * block number for those buffers in the "altpath".
19462306a36Sopenharmony_ci	 */
19562306a36Sopenharmony_ci	path = &state->altpath;
19662306a36Sopenharmony_ci	ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
19762306a36Sopenharmony_ci	for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
19862306a36Sopenharmony_ci		if (blk->disk_blkno) {
19962306a36Sopenharmony_ci			error = xfs_da3_node_read_mapped(state->args->trans,
20062306a36Sopenharmony_ci					state->args->dp, blk->disk_blkno,
20162306a36Sopenharmony_ci					&blk->bp, XFS_ATTR_FORK);
20262306a36Sopenharmony_ci			if (error)
20362306a36Sopenharmony_ci				return error;
20462306a36Sopenharmony_ci		} else {
20562306a36Sopenharmony_ci			blk->bp = NULL;
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci#else
21262306a36Sopenharmony_cistatic int xfs_attr_fillstate(xfs_da_state_t *state) { return 0; }
21362306a36Sopenharmony_ci#endif
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/*========================================================================
21662306a36Sopenharmony_ci * Overall external interface routines.
21762306a36Sopenharmony_ci *========================================================================*/
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/*
22062306a36Sopenharmony_ci * Retrieve an extended attribute and its value.  Must have ilock.
22162306a36Sopenharmony_ci * Returns 0 on successful retrieval, otherwise an error.
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_ciint
22462306a36Sopenharmony_cixfs_attr_get_ilocked(
22562306a36Sopenharmony_ci	struct xfs_da_args	*args)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	ASSERT(xfs_isilocked(args->dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (!xfs_inode_hasattr(args->dp))
23062306a36Sopenharmony_ci		return -ENOATTR;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL)
23362306a36Sopenharmony_ci		return xfs_attr_shortform_getvalue(args);
23462306a36Sopenharmony_ci	if (xfs_attr_is_leaf(args->dp))
23562306a36Sopenharmony_ci		return xfs_attr_leaf_get(args);
23662306a36Sopenharmony_ci	return xfs_attr_node_get(args);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/*
24062306a36Sopenharmony_ci * Retrieve an extended attribute by name, and its value if requested.
24162306a36Sopenharmony_ci *
24262306a36Sopenharmony_ci * If args->valuelen is zero, then the caller does not want the value, just an
24362306a36Sopenharmony_ci * indication whether the attribute exists and the size of the value if it
24462306a36Sopenharmony_ci * exists. The size is returned in args.valuelen.
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * If args->value is NULL but args->valuelen is non-zero, allocate the buffer
24762306a36Sopenharmony_ci * for the value after existence of the attribute has been determined. The
24862306a36Sopenharmony_ci * caller always has to free args->value if it is set, no matter if this
24962306a36Sopenharmony_ci * function was successful or not.
25062306a36Sopenharmony_ci *
25162306a36Sopenharmony_ci * If the attribute is found, but exceeds the size limit set by the caller in
25262306a36Sopenharmony_ci * args->valuelen, return -ERANGE with the size of the attribute that was found
25362306a36Sopenharmony_ci * in args->valuelen.
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_ciint
25662306a36Sopenharmony_cixfs_attr_get(
25762306a36Sopenharmony_ci	struct xfs_da_args	*args)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	uint			lock_mode;
26062306a36Sopenharmony_ci	int			error;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	XFS_STATS_INC(args->dp->i_mount, xs_attr_get);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (xfs_is_shutdown(args->dp->i_mount))
26562306a36Sopenharmony_ci		return -EIO;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	args->geo = args->dp->i_mount->m_attr_geo;
26862306a36Sopenharmony_ci	args->whichfork = XFS_ATTR_FORK;
26962306a36Sopenharmony_ci	args->hashval = xfs_da_hashname(args->name, args->namelen);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* Entirely possible to look up a name which doesn't exist */
27262306a36Sopenharmony_ci	args->op_flags = XFS_DA_OP_OKNOENT;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	lock_mode = xfs_ilock_attr_map_shared(args->dp);
27562306a36Sopenharmony_ci	error = xfs_attr_get_ilocked(args);
27662306a36Sopenharmony_ci	xfs_iunlock(args->dp, lock_mode);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return error;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/*
28262306a36Sopenharmony_ci * Calculate how many blocks we need for the new attribute,
28362306a36Sopenharmony_ci */
28462306a36Sopenharmony_ciint
28562306a36Sopenharmony_cixfs_attr_calc_size(
28662306a36Sopenharmony_ci	struct xfs_da_args	*args,
28762306a36Sopenharmony_ci	int			*local)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct xfs_mount	*mp = args->dp->i_mount;
29062306a36Sopenharmony_ci	int			size;
29162306a36Sopenharmony_ci	int			nblks;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/*
29462306a36Sopenharmony_ci	 * Determine space new attribute will use, and if it would be
29562306a36Sopenharmony_ci	 * "local" or "remote" (note: local != inline).
29662306a36Sopenharmony_ci	 */
29762306a36Sopenharmony_ci	size = xfs_attr_leaf_newentsize(args, local);
29862306a36Sopenharmony_ci	nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
29962306a36Sopenharmony_ci	if (*local) {
30062306a36Sopenharmony_ci		if (size > (args->geo->blksize / 2)) {
30162306a36Sopenharmony_ci			/* Double split possible */
30262306a36Sopenharmony_ci			nblks *= 2;
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci	} else {
30562306a36Sopenharmony_ci		/*
30662306a36Sopenharmony_ci		 * Out of line attribute, cannot double split, but
30762306a36Sopenharmony_ci		 * make room for the attribute value itself.
30862306a36Sopenharmony_ci		 */
30962306a36Sopenharmony_ci		uint	dblocks = xfs_attr3_rmt_blocks(mp, args->valuelen);
31062306a36Sopenharmony_ci		nblks += dblocks;
31162306a36Sopenharmony_ci		nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return nblks;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/* Initialize transaction reservation for attr operations */
31862306a36Sopenharmony_civoid
31962306a36Sopenharmony_cixfs_init_attr_trans(
32062306a36Sopenharmony_ci	struct xfs_da_args	*args,
32162306a36Sopenharmony_ci	struct xfs_trans_res	*tres,
32262306a36Sopenharmony_ci	unsigned int		*total)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct xfs_mount	*mp = args->dp->i_mount;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (args->value) {
32762306a36Sopenharmony_ci		tres->tr_logres = M_RES(mp)->tr_attrsetm.tr_logres +
32862306a36Sopenharmony_ci				 M_RES(mp)->tr_attrsetrt.tr_logres *
32962306a36Sopenharmony_ci				 args->total;
33062306a36Sopenharmony_ci		tres->tr_logcount = XFS_ATTRSET_LOG_COUNT;
33162306a36Sopenharmony_ci		tres->tr_logflags = XFS_TRANS_PERM_LOG_RES;
33262306a36Sopenharmony_ci		*total = args->total;
33362306a36Sopenharmony_ci	} else {
33462306a36Sopenharmony_ci		*tres = M_RES(mp)->tr_attrrm;
33562306a36Sopenharmony_ci		*total = XFS_ATTRRM_SPACE_RES(mp);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/*
34062306a36Sopenharmony_ci * Add an attr to a shortform fork. If there is no space,
34162306a36Sopenharmony_ci * xfs_attr_shortform_addname() will convert to leaf format and return -ENOSPC.
34262306a36Sopenharmony_ci * to use.
34362306a36Sopenharmony_ci */
34462306a36Sopenharmony_ciSTATIC int
34562306a36Sopenharmony_cixfs_attr_try_sf_addname(
34662306a36Sopenharmony_ci	struct xfs_inode	*dp,
34762306a36Sopenharmony_ci	struct xfs_da_args	*args)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	int			error;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/*
35362306a36Sopenharmony_ci	 * Build initial attribute list (if required).
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci	if (dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS)
35662306a36Sopenharmony_ci		xfs_attr_shortform_create(args);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	error = xfs_attr_shortform_addname(args);
35962306a36Sopenharmony_ci	if (error == -ENOSPC)
36062306a36Sopenharmony_ci		return error;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/*
36362306a36Sopenharmony_ci	 * Commit the shortform mods, and we're done.
36462306a36Sopenharmony_ci	 * NOTE: this is also the error path (EEXIST, etc).
36562306a36Sopenharmony_ci	 */
36662306a36Sopenharmony_ci	if (!error && !(args->op_flags & XFS_DA_OP_NOTIME))
36762306a36Sopenharmony_ci		xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (xfs_has_wsync(dp->i_mount))
37062306a36Sopenharmony_ci		xfs_trans_set_sync(args->trans);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	return error;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic int
37662306a36Sopenharmony_cixfs_attr_sf_addname(
37762306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct xfs_da_args		*args = attr->xattri_da_args;
38062306a36Sopenharmony_ci	struct xfs_inode		*dp = args->dp;
38162306a36Sopenharmony_ci	int				error = 0;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	error = xfs_attr_try_sf_addname(dp, args);
38462306a36Sopenharmony_ci	if (error != -ENOSPC) {
38562306a36Sopenharmony_ci		ASSERT(!error || error == -EEXIST);
38662306a36Sopenharmony_ci		attr->xattri_dela_state = XFS_DAS_DONE;
38762306a36Sopenharmony_ci		goto out;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/*
39162306a36Sopenharmony_ci	 * It won't fit in the shortform, transform to a leaf block.  GROT:
39262306a36Sopenharmony_ci	 * another possible req'mt for a double-split btree op.
39362306a36Sopenharmony_ci	 */
39462306a36Sopenharmony_ci	error = xfs_attr_shortform_to_leaf(args);
39562306a36Sopenharmony_ci	if (error)
39662306a36Sopenharmony_ci		return error;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	attr->xattri_dela_state = XFS_DAS_LEAF_ADD;
39962306a36Sopenharmony_ciout:
40062306a36Sopenharmony_ci	trace_xfs_attr_sf_addname_return(attr->xattri_dela_state, args->dp);
40162306a36Sopenharmony_ci	return error;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/*
40562306a36Sopenharmony_ci * Handle the state change on completion of a multi-state attr operation.
40662306a36Sopenharmony_ci *
40762306a36Sopenharmony_ci * If the XFS_DA_OP_REPLACE flag is set, this means the operation was the first
40862306a36Sopenharmony_ci * modification in a attr replace operation and we still have to do the second
40962306a36Sopenharmony_ci * state, indicated by @replace_state.
41062306a36Sopenharmony_ci *
41162306a36Sopenharmony_ci * We consume the XFS_DA_OP_REPLACE flag so that when we are called again on
41262306a36Sopenharmony_ci * completion of the second half of the attr replace operation we correctly
41362306a36Sopenharmony_ci * signal that it is done.
41462306a36Sopenharmony_ci */
41562306a36Sopenharmony_cistatic enum xfs_delattr_state
41662306a36Sopenharmony_cixfs_attr_complete_op(
41762306a36Sopenharmony_ci	struct xfs_attr_intent	*attr,
41862306a36Sopenharmony_ci	enum xfs_delattr_state	replace_state)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct xfs_da_args	*args = attr->xattri_da_args;
42162306a36Sopenharmony_ci	bool			do_replace = args->op_flags & XFS_DA_OP_REPLACE;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	args->op_flags &= ~XFS_DA_OP_REPLACE;
42462306a36Sopenharmony_ci	if (do_replace) {
42562306a36Sopenharmony_ci		args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
42662306a36Sopenharmony_ci		return replace_state;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci	return XFS_DAS_DONE;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int
43262306a36Sopenharmony_cixfs_attr_leaf_addname(
43362306a36Sopenharmony_ci	struct xfs_attr_intent	*attr)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct xfs_da_args	*args = attr->xattri_da_args;
43662306a36Sopenharmony_ci	int			error;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	ASSERT(xfs_attr_is_leaf(args->dp));
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * Use the leaf buffer we may already hold locked as a result of
44262306a36Sopenharmony_ci	 * a sf-to-leaf conversion.
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	error = xfs_attr_leaf_try_add(args);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (error == -ENOSPC) {
44762306a36Sopenharmony_ci		error = xfs_attr3_leaf_to_node(args);
44862306a36Sopenharmony_ci		if (error)
44962306a36Sopenharmony_ci			return error;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		/*
45262306a36Sopenharmony_ci		 * We're not in leaf format anymore, so roll the transaction and
45362306a36Sopenharmony_ci		 * retry the add to the newly allocated node block.
45462306a36Sopenharmony_ci		 */
45562306a36Sopenharmony_ci		attr->xattri_dela_state = XFS_DAS_NODE_ADD;
45662306a36Sopenharmony_ci		goto out;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	if (error)
45962306a36Sopenharmony_ci		return error;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/*
46262306a36Sopenharmony_ci	 * We need to commit and roll if we need to allocate remote xattr blocks
46362306a36Sopenharmony_ci	 * or perform more xattr manipulations. Otherwise there is nothing more
46462306a36Sopenharmony_ci	 * to do and we can return success.
46562306a36Sopenharmony_ci	 */
46662306a36Sopenharmony_ci	if (args->rmtblkno)
46762306a36Sopenharmony_ci		attr->xattri_dela_state = XFS_DAS_LEAF_SET_RMT;
46862306a36Sopenharmony_ci	else
46962306a36Sopenharmony_ci		attr->xattri_dela_state = xfs_attr_complete_op(attr,
47062306a36Sopenharmony_ci							XFS_DAS_LEAF_REPLACE);
47162306a36Sopenharmony_ciout:
47262306a36Sopenharmony_ci	trace_xfs_attr_leaf_addname_return(attr->xattri_dela_state, args->dp);
47362306a36Sopenharmony_ci	return error;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci/*
47762306a36Sopenharmony_ci * Add an entry to a node format attr tree.
47862306a36Sopenharmony_ci *
47962306a36Sopenharmony_ci * Note that we might still have a leaf here - xfs_attr_is_leaf() cannot tell
48062306a36Sopenharmony_ci * the difference between leaf + remote attr blocks and a node format tree,
48162306a36Sopenharmony_ci * so we may still end up having to convert from leaf to node format here.
48262306a36Sopenharmony_ci */
48362306a36Sopenharmony_cistatic int
48462306a36Sopenharmony_cixfs_attr_node_addname(
48562306a36Sopenharmony_ci	struct xfs_attr_intent	*attr)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct xfs_da_args	*args = attr->xattri_da_args;
48862306a36Sopenharmony_ci	int			error;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	error = xfs_attr_node_addname_find_attr(attr);
49162306a36Sopenharmony_ci	if (error)
49262306a36Sopenharmony_ci		return error;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	error = xfs_attr_node_try_addname(attr);
49562306a36Sopenharmony_ci	if (error == -ENOSPC) {
49662306a36Sopenharmony_ci		error = xfs_attr3_leaf_to_node(args);
49762306a36Sopenharmony_ci		if (error)
49862306a36Sopenharmony_ci			return error;
49962306a36Sopenharmony_ci		/*
50062306a36Sopenharmony_ci		 * No state change, we really are in node form now
50162306a36Sopenharmony_ci		 * but we need the transaction rolled to continue.
50262306a36Sopenharmony_ci		 */
50362306a36Sopenharmony_ci		goto out;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci	if (error)
50662306a36Sopenharmony_ci		return error;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (args->rmtblkno)
50962306a36Sopenharmony_ci		attr->xattri_dela_state = XFS_DAS_NODE_SET_RMT;
51062306a36Sopenharmony_ci	else
51162306a36Sopenharmony_ci		attr->xattri_dela_state = xfs_attr_complete_op(attr,
51262306a36Sopenharmony_ci							XFS_DAS_NODE_REPLACE);
51362306a36Sopenharmony_ciout:
51462306a36Sopenharmony_ci	trace_xfs_attr_node_addname_return(attr->xattri_dela_state, args->dp);
51562306a36Sopenharmony_ci	return error;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int
51962306a36Sopenharmony_cixfs_attr_rmtval_alloc(
52062306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct xfs_da_args              *args = attr->xattri_da_args;
52362306a36Sopenharmony_ci	int				error = 0;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/*
52662306a36Sopenharmony_ci	 * If there was an out-of-line value, allocate the blocks we
52762306a36Sopenharmony_ci	 * identified for its storage and copy the value.  This is done
52862306a36Sopenharmony_ci	 * after we create the attribute so that we don't overflow the
52962306a36Sopenharmony_ci	 * maximum size of a transaction and/or hit a deadlock.
53062306a36Sopenharmony_ci	 */
53162306a36Sopenharmony_ci	if (attr->xattri_blkcnt > 0) {
53262306a36Sopenharmony_ci		error = xfs_attr_rmtval_set_blk(attr);
53362306a36Sopenharmony_ci		if (error)
53462306a36Sopenharmony_ci			return error;
53562306a36Sopenharmony_ci		/* Roll the transaction only if there is more to allocate. */
53662306a36Sopenharmony_ci		if (attr->xattri_blkcnt > 0)
53762306a36Sopenharmony_ci			goto out;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	error = xfs_attr_rmtval_set_value(args);
54162306a36Sopenharmony_ci	if (error)
54262306a36Sopenharmony_ci		return error;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	attr->xattri_dela_state = xfs_attr_complete_op(attr,
54562306a36Sopenharmony_ci						++attr->xattri_dela_state);
54662306a36Sopenharmony_ci	/*
54762306a36Sopenharmony_ci	 * If we are not doing a rename, we've finished the operation but still
54862306a36Sopenharmony_ci	 * have to clear the incomplete flag protecting the new attr from
54962306a36Sopenharmony_ci	 * exposing partially initialised state if we crash during creation.
55062306a36Sopenharmony_ci	 */
55162306a36Sopenharmony_ci	if (attr->xattri_dela_state == XFS_DAS_DONE)
55262306a36Sopenharmony_ci		error = xfs_attr3_leaf_clearflag(args);
55362306a36Sopenharmony_ciout:
55462306a36Sopenharmony_ci	trace_xfs_attr_rmtval_alloc(attr->xattri_dela_state, args->dp);
55562306a36Sopenharmony_ci	return error;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/*
55962306a36Sopenharmony_ci * Mark an attribute entry INCOMPLETE and save pointers to the relevant buffers
56062306a36Sopenharmony_ci * for later deletion of the entry.
56162306a36Sopenharmony_ci */
56262306a36Sopenharmony_cistatic int
56362306a36Sopenharmony_cixfs_attr_leaf_mark_incomplete(
56462306a36Sopenharmony_ci	struct xfs_da_args	*args,
56562306a36Sopenharmony_ci	struct xfs_da_state	*state)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	int			error;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/*
57062306a36Sopenharmony_ci	 * Fill in disk block numbers in the state structure
57162306a36Sopenharmony_ci	 * so that we can get the buffers back after we commit
57262306a36Sopenharmony_ci	 * several transactions in the following calls.
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	error = xfs_attr_fillstate(state);
57562306a36Sopenharmony_ci	if (error)
57662306a36Sopenharmony_ci		return error;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/*
57962306a36Sopenharmony_ci	 * Mark the attribute as INCOMPLETE
58062306a36Sopenharmony_ci	 */
58162306a36Sopenharmony_ci	return xfs_attr3_leaf_setflag(args);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci/* Ensure the da state of an xattr deferred work item is ready to go. */
58562306a36Sopenharmony_cistatic inline void
58662306a36Sopenharmony_cixfs_attr_item_init_da_state(
58762306a36Sopenharmony_ci	struct xfs_attr_intent	*attr)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct xfs_da_args	*args = attr->xattri_da_args;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (!attr->xattri_da_state)
59262306a36Sopenharmony_ci		attr->xattri_da_state = xfs_da_state_alloc(args);
59362306a36Sopenharmony_ci	else
59462306a36Sopenharmony_ci		xfs_da_state_reset(attr->xattri_da_state, args);
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/*
59862306a36Sopenharmony_ci * Initial setup for xfs_attr_node_removename.  Make sure the attr is there and
59962306a36Sopenharmony_ci * the blocks are valid.  Attr keys with remote blocks will be marked
60062306a36Sopenharmony_ci * incomplete.
60162306a36Sopenharmony_ci */
60262306a36Sopenharmony_cistatic
60362306a36Sopenharmony_ciint xfs_attr_node_removename_setup(
60462306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct xfs_da_args		*args = attr->xattri_da_args;
60762306a36Sopenharmony_ci	struct xfs_da_state		*state;
60862306a36Sopenharmony_ci	int				error;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	xfs_attr_item_init_da_state(attr);
61162306a36Sopenharmony_ci	error = xfs_attr_node_lookup(args, attr->xattri_da_state);
61262306a36Sopenharmony_ci	if (error != -EEXIST)
61362306a36Sopenharmony_ci		goto out;
61462306a36Sopenharmony_ci	error = 0;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	state = attr->xattri_da_state;
61762306a36Sopenharmony_ci	ASSERT(state->path.blk[state->path.active - 1].bp != NULL);
61862306a36Sopenharmony_ci	ASSERT(state->path.blk[state->path.active - 1].magic ==
61962306a36Sopenharmony_ci		XFS_ATTR_LEAF_MAGIC);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	error = xfs_attr_leaf_mark_incomplete(args, state);
62262306a36Sopenharmony_ci	if (error)
62362306a36Sopenharmony_ci		goto out;
62462306a36Sopenharmony_ci	if (args->rmtblkno > 0)
62562306a36Sopenharmony_ci		error = xfs_attr_rmtval_invalidate(args);
62662306a36Sopenharmony_ciout:
62762306a36Sopenharmony_ci	if (error) {
62862306a36Sopenharmony_ci		xfs_da_state_free(attr->xattri_da_state);
62962306a36Sopenharmony_ci		attr->xattri_da_state = NULL;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return error;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci * Remove the original attr we have just replaced. This is dependent on the
63762306a36Sopenharmony_ci * original lookup and insert placing the old attr in args->blkno/args->index
63862306a36Sopenharmony_ci * and the new attr in args->blkno2/args->index2.
63962306a36Sopenharmony_ci */
64062306a36Sopenharmony_cistatic int
64162306a36Sopenharmony_cixfs_attr_leaf_remove_attr(
64262306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct xfs_da_args              *args = attr->xattri_da_args;
64562306a36Sopenharmony_ci	struct xfs_inode		*dp = args->dp;
64662306a36Sopenharmony_ci	struct xfs_buf			*bp = NULL;
64762306a36Sopenharmony_ci	int				forkoff;
64862306a36Sopenharmony_ci	int				error;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	error = xfs_attr3_leaf_read(args->trans, args->dp, args->blkno,
65162306a36Sopenharmony_ci				   &bp);
65262306a36Sopenharmony_ci	if (error)
65362306a36Sopenharmony_ci		return error;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	xfs_attr3_leaf_remove(bp, args);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	forkoff = xfs_attr_shortform_allfit(bp, dp);
65862306a36Sopenharmony_ci	if (forkoff)
65962306a36Sopenharmony_ci		error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
66062306a36Sopenharmony_ci		/* bp is gone due to xfs_da_shrink_inode */
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return error;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci/*
66662306a36Sopenharmony_ci * Shrink an attribute from leaf to shortform. Used by the node format remove
66762306a36Sopenharmony_ci * path when the node format collapses to a single block and so we have to check
66862306a36Sopenharmony_ci * if it can be collapsed further.
66962306a36Sopenharmony_ci */
67062306a36Sopenharmony_cistatic int
67162306a36Sopenharmony_cixfs_attr_leaf_shrink(
67262306a36Sopenharmony_ci	struct xfs_da_args	*args)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct xfs_inode	*dp = args->dp;
67562306a36Sopenharmony_ci	struct xfs_buf		*bp;
67662306a36Sopenharmony_ci	int			forkoff;
67762306a36Sopenharmony_ci	int			error;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (!xfs_attr_is_leaf(dp))
68062306a36Sopenharmony_ci		return 0;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
68362306a36Sopenharmony_ci	if (error)
68462306a36Sopenharmony_ci		return error;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	forkoff = xfs_attr_shortform_allfit(bp, dp);
68762306a36Sopenharmony_ci	if (forkoff) {
68862306a36Sopenharmony_ci		error = xfs_attr3_leaf_to_shortform(bp, args, forkoff);
68962306a36Sopenharmony_ci		/* bp is gone due to xfs_da_shrink_inode */
69062306a36Sopenharmony_ci	} else {
69162306a36Sopenharmony_ci		xfs_trans_brelse(args->trans, bp);
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	return error;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci/*
69862306a36Sopenharmony_ci * Run the attribute operation specified in @attr.
69962306a36Sopenharmony_ci *
70062306a36Sopenharmony_ci * This routine is meant to function as a delayed operation and will set the
70162306a36Sopenharmony_ci * state to XFS_DAS_DONE when the operation is complete.  Calling functions will
70262306a36Sopenharmony_ci * need to handle this, and recall the function until either an error or
70362306a36Sopenharmony_ci * XFS_DAS_DONE is detected.
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_ciint
70662306a36Sopenharmony_cixfs_attr_set_iter(
70762306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct xfs_da_args              *args = attr->xattri_da_args;
71062306a36Sopenharmony_ci	int				error = 0;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* State machine switch */
71362306a36Sopenharmony_cinext_state:
71462306a36Sopenharmony_ci	switch (attr->xattri_dela_state) {
71562306a36Sopenharmony_ci	case XFS_DAS_UNINIT:
71662306a36Sopenharmony_ci		ASSERT(0);
71762306a36Sopenharmony_ci		return -EFSCORRUPTED;
71862306a36Sopenharmony_ci	case XFS_DAS_SF_ADD:
71962306a36Sopenharmony_ci		return xfs_attr_sf_addname(attr);
72062306a36Sopenharmony_ci	case XFS_DAS_LEAF_ADD:
72162306a36Sopenharmony_ci		return xfs_attr_leaf_addname(attr);
72262306a36Sopenharmony_ci	case XFS_DAS_NODE_ADD:
72362306a36Sopenharmony_ci		return xfs_attr_node_addname(attr);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	case XFS_DAS_SF_REMOVE:
72662306a36Sopenharmony_ci		error = xfs_attr_sf_removename(args);
72762306a36Sopenharmony_ci		attr->xattri_dela_state = xfs_attr_complete_op(attr,
72862306a36Sopenharmony_ci						xfs_attr_init_add_state(args));
72962306a36Sopenharmony_ci		break;
73062306a36Sopenharmony_ci	case XFS_DAS_LEAF_REMOVE:
73162306a36Sopenharmony_ci		error = xfs_attr_leaf_removename(args);
73262306a36Sopenharmony_ci		attr->xattri_dela_state = xfs_attr_complete_op(attr,
73362306a36Sopenharmony_ci						xfs_attr_init_add_state(args));
73462306a36Sopenharmony_ci		break;
73562306a36Sopenharmony_ci	case XFS_DAS_NODE_REMOVE:
73662306a36Sopenharmony_ci		error = xfs_attr_node_removename_setup(attr);
73762306a36Sopenharmony_ci		if (error == -ENOATTR &&
73862306a36Sopenharmony_ci		    (args->op_flags & XFS_DA_OP_RECOVERY)) {
73962306a36Sopenharmony_ci			attr->xattri_dela_state = xfs_attr_complete_op(attr,
74062306a36Sopenharmony_ci						xfs_attr_init_add_state(args));
74162306a36Sopenharmony_ci			error = 0;
74262306a36Sopenharmony_ci			break;
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci		if (error)
74562306a36Sopenharmony_ci			return error;
74662306a36Sopenharmony_ci		attr->xattri_dela_state = XFS_DAS_NODE_REMOVE_RMT;
74762306a36Sopenharmony_ci		if (args->rmtblkno == 0)
74862306a36Sopenharmony_ci			attr->xattri_dela_state++;
74962306a36Sopenharmony_ci		break;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	case XFS_DAS_LEAF_SET_RMT:
75262306a36Sopenharmony_ci	case XFS_DAS_NODE_SET_RMT:
75362306a36Sopenharmony_ci		error = xfs_attr_rmtval_find_space(attr);
75462306a36Sopenharmony_ci		if (error)
75562306a36Sopenharmony_ci			return error;
75662306a36Sopenharmony_ci		attr->xattri_dela_state++;
75762306a36Sopenharmony_ci		fallthrough;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	case XFS_DAS_LEAF_ALLOC_RMT:
76062306a36Sopenharmony_ci	case XFS_DAS_NODE_ALLOC_RMT:
76162306a36Sopenharmony_ci		error = xfs_attr_rmtval_alloc(attr);
76262306a36Sopenharmony_ci		if (error)
76362306a36Sopenharmony_ci			return error;
76462306a36Sopenharmony_ci		if (attr->xattri_dela_state == XFS_DAS_DONE)
76562306a36Sopenharmony_ci			break;
76662306a36Sopenharmony_ci		goto next_state;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	case XFS_DAS_LEAF_REPLACE:
76962306a36Sopenharmony_ci	case XFS_DAS_NODE_REPLACE:
77062306a36Sopenharmony_ci		/*
77162306a36Sopenharmony_ci		 * We must "flip" the incomplete flags on the "new" and "old"
77262306a36Sopenharmony_ci		 * attribute/value pairs so that one disappears and one appears
77362306a36Sopenharmony_ci		 * atomically.
77462306a36Sopenharmony_ci		 */
77562306a36Sopenharmony_ci		error = xfs_attr3_leaf_flipflags(args);
77662306a36Sopenharmony_ci		if (error)
77762306a36Sopenharmony_ci			return error;
77862306a36Sopenharmony_ci		/*
77962306a36Sopenharmony_ci		 * We must commit the flag value change now to make it atomic
78062306a36Sopenharmony_ci		 * and then we can start the next trans in series at REMOVE_OLD.
78162306a36Sopenharmony_ci		 */
78262306a36Sopenharmony_ci		attr->xattri_dela_state++;
78362306a36Sopenharmony_ci		break;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	case XFS_DAS_LEAF_REMOVE_OLD:
78662306a36Sopenharmony_ci	case XFS_DAS_NODE_REMOVE_OLD:
78762306a36Sopenharmony_ci		/*
78862306a36Sopenharmony_ci		 * If we have a remote attr, start the process of removing it
78962306a36Sopenharmony_ci		 * by invalidating any cached buffers.
79062306a36Sopenharmony_ci		 *
79162306a36Sopenharmony_ci		 * If we don't have a remote attr, we skip the remote block
79262306a36Sopenharmony_ci		 * removal state altogether with a second state increment.
79362306a36Sopenharmony_ci		 */
79462306a36Sopenharmony_ci		xfs_attr_restore_rmt_blk(args);
79562306a36Sopenharmony_ci		if (args->rmtblkno) {
79662306a36Sopenharmony_ci			error = xfs_attr_rmtval_invalidate(args);
79762306a36Sopenharmony_ci			if (error)
79862306a36Sopenharmony_ci				return error;
79962306a36Sopenharmony_ci		} else {
80062306a36Sopenharmony_ci			attr->xattri_dela_state++;
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		attr->xattri_dela_state++;
80462306a36Sopenharmony_ci		goto next_state;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	case XFS_DAS_LEAF_REMOVE_RMT:
80762306a36Sopenharmony_ci	case XFS_DAS_NODE_REMOVE_RMT:
80862306a36Sopenharmony_ci		error = xfs_attr_rmtval_remove(attr);
80962306a36Sopenharmony_ci		if (error == -EAGAIN) {
81062306a36Sopenharmony_ci			error = 0;
81162306a36Sopenharmony_ci			break;
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci		if (error)
81462306a36Sopenharmony_ci			return error;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		/*
81762306a36Sopenharmony_ci		 * We've finished removing the remote attr blocks, so commit the
81862306a36Sopenharmony_ci		 * transaction and move on to removing the attr name from the
81962306a36Sopenharmony_ci		 * leaf/node block. Removing the attr might require a full
82062306a36Sopenharmony_ci		 * transaction reservation for btree block freeing, so we
82162306a36Sopenharmony_ci		 * can't do that in the same transaction where we removed the
82262306a36Sopenharmony_ci		 * remote attr blocks.
82362306a36Sopenharmony_ci		 */
82462306a36Sopenharmony_ci		attr->xattri_dela_state++;
82562306a36Sopenharmony_ci		break;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	case XFS_DAS_LEAF_REMOVE_ATTR:
82862306a36Sopenharmony_ci		error = xfs_attr_leaf_remove_attr(attr);
82962306a36Sopenharmony_ci		attr->xattri_dela_state = xfs_attr_complete_op(attr,
83062306a36Sopenharmony_ci						xfs_attr_init_add_state(args));
83162306a36Sopenharmony_ci		break;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	case XFS_DAS_NODE_REMOVE_ATTR:
83462306a36Sopenharmony_ci		error = xfs_attr_node_remove_attr(attr);
83562306a36Sopenharmony_ci		if (!error)
83662306a36Sopenharmony_ci			error = xfs_attr_leaf_shrink(args);
83762306a36Sopenharmony_ci		attr->xattri_dela_state = xfs_attr_complete_op(attr,
83862306a36Sopenharmony_ci						xfs_attr_init_add_state(args));
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci	default:
84162306a36Sopenharmony_ci		ASSERT(0);
84262306a36Sopenharmony_ci		break;
84362306a36Sopenharmony_ci	}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	trace_xfs_attr_set_iter_return(attr->xattri_dela_state, args->dp);
84662306a36Sopenharmony_ci	return error;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci/*
85162306a36Sopenharmony_ci * Return EEXIST if attr is found, or ENOATTR if not
85262306a36Sopenharmony_ci */
85362306a36Sopenharmony_cistatic int
85462306a36Sopenharmony_cixfs_attr_lookup(
85562306a36Sopenharmony_ci	struct xfs_da_args	*args)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	struct xfs_inode	*dp = args->dp;
85862306a36Sopenharmony_ci	struct xfs_buf		*bp = NULL;
85962306a36Sopenharmony_ci	struct xfs_da_state	*state;
86062306a36Sopenharmony_ci	int			error;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	if (!xfs_inode_hasattr(dp))
86362306a36Sopenharmony_ci		return -ENOATTR;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	if (dp->i_af.if_format == XFS_DINODE_FMT_LOCAL)
86662306a36Sopenharmony_ci		return xfs_attr_sf_findname(args, NULL, NULL);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	if (xfs_attr_is_leaf(dp)) {
86962306a36Sopenharmony_ci		error = xfs_attr_leaf_hasname(args, &bp);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		if (bp)
87262306a36Sopenharmony_ci			xfs_trans_brelse(args->trans, bp);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci		return error;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	state = xfs_da_state_alloc(args);
87862306a36Sopenharmony_ci	error = xfs_attr_node_lookup(args, state);
87962306a36Sopenharmony_ci	xfs_da_state_free(state);
88062306a36Sopenharmony_ci	return error;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic int
88462306a36Sopenharmony_cixfs_attr_intent_init(
88562306a36Sopenharmony_ci	struct xfs_da_args	*args,
88662306a36Sopenharmony_ci	unsigned int		op_flags,	/* op flag (set or remove) */
88762306a36Sopenharmony_ci	struct xfs_attr_intent	**attr)		/* new xfs_attr_intent */
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	struct xfs_attr_intent	*new;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	new = kmem_cache_zalloc(xfs_attr_intent_cache, GFP_NOFS | __GFP_NOFAIL);
89362306a36Sopenharmony_ci	new->xattri_op_flags = op_flags;
89462306a36Sopenharmony_ci	new->xattri_da_args = args;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	*attr = new;
89762306a36Sopenharmony_ci	return 0;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci/* Sets an attribute for an inode as a deferred operation */
90162306a36Sopenharmony_cistatic int
90262306a36Sopenharmony_cixfs_attr_defer_add(
90362306a36Sopenharmony_ci	struct xfs_da_args	*args)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct xfs_attr_intent	*new;
90662306a36Sopenharmony_ci	int			error = 0;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_SET, &new);
90962306a36Sopenharmony_ci	if (error)
91062306a36Sopenharmony_ci		return error;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	new->xattri_dela_state = xfs_attr_init_add_state(args);
91362306a36Sopenharmony_ci	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
91462306a36Sopenharmony_ci	trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	return 0;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci/* Sets an attribute for an inode as a deferred operation */
92062306a36Sopenharmony_cistatic int
92162306a36Sopenharmony_cixfs_attr_defer_replace(
92262306a36Sopenharmony_ci	struct xfs_da_args	*args)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct xfs_attr_intent	*new;
92562306a36Sopenharmony_ci	int			error = 0;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_REPLACE, &new);
92862306a36Sopenharmony_ci	if (error)
92962306a36Sopenharmony_ci		return error;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	new->xattri_dela_state = xfs_attr_init_replace_state(args);
93262306a36Sopenharmony_ci	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
93362306a36Sopenharmony_ci	trace_xfs_attr_defer_replace(new->xattri_dela_state, args->dp);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	return 0;
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci/* Removes an attribute for an inode as a deferred operation */
93962306a36Sopenharmony_cistatic int
94062306a36Sopenharmony_cixfs_attr_defer_remove(
94162306a36Sopenharmony_ci	struct xfs_da_args	*args)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	struct xfs_attr_intent	*new;
94562306a36Sopenharmony_ci	int			error;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	error  = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_REMOVE, &new);
94862306a36Sopenharmony_ci	if (error)
94962306a36Sopenharmony_ci		return error;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	new->xattri_dela_state = xfs_attr_init_remove_state(args);
95262306a36Sopenharmony_ci	xfs_defer_add(args->trans, XFS_DEFER_OPS_TYPE_ATTR, &new->xattri_list);
95362306a36Sopenharmony_ci	trace_xfs_attr_defer_remove(new->xattri_dela_state, args->dp);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	return 0;
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci/*
95962306a36Sopenharmony_ci * Note: If args->value is NULL the attribute will be removed, just like the
96062306a36Sopenharmony_ci * Linux ->setattr API.
96162306a36Sopenharmony_ci */
96262306a36Sopenharmony_ciint
96362306a36Sopenharmony_cixfs_attr_set(
96462306a36Sopenharmony_ci	struct xfs_da_args	*args)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	struct xfs_inode	*dp = args->dp;
96762306a36Sopenharmony_ci	struct xfs_mount	*mp = dp->i_mount;
96862306a36Sopenharmony_ci	struct xfs_trans_res	tres;
96962306a36Sopenharmony_ci	bool			rsvd = (args->attr_filter & XFS_ATTR_ROOT);
97062306a36Sopenharmony_ci	int			error, local;
97162306a36Sopenharmony_ci	int			rmt_blks = 0;
97262306a36Sopenharmony_ci	unsigned int		total;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (xfs_is_shutdown(dp->i_mount))
97562306a36Sopenharmony_ci		return -EIO;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	error = xfs_qm_dqattach(dp);
97862306a36Sopenharmony_ci	if (error)
97962306a36Sopenharmony_ci		return error;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	args->geo = mp->m_attr_geo;
98262306a36Sopenharmony_ci	args->whichfork = XFS_ATTR_FORK;
98362306a36Sopenharmony_ci	args->hashval = xfs_da_hashname(args->name, args->namelen);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/*
98662306a36Sopenharmony_ci	 * We have no control over the attribute names that userspace passes us
98762306a36Sopenharmony_ci	 * to remove, so we have to allow the name lookup prior to attribute
98862306a36Sopenharmony_ci	 * removal to fail as well.  Preserve the logged flag, since we need
98962306a36Sopenharmony_ci	 * to pass that through to the logging code.
99062306a36Sopenharmony_ci	 */
99162306a36Sopenharmony_ci	args->op_flags = XFS_DA_OP_OKNOENT |
99262306a36Sopenharmony_ci					(args->op_flags & XFS_DA_OP_LOGGED);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (args->value) {
99562306a36Sopenharmony_ci		XFS_STATS_INC(mp, xs_attr_set);
99662306a36Sopenharmony_ci		args->total = xfs_attr_calc_size(args, &local);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		/*
99962306a36Sopenharmony_ci		 * If the inode doesn't have an attribute fork, add one.
100062306a36Sopenharmony_ci		 * (inode must not be locked when we call this routine)
100162306a36Sopenharmony_ci		 */
100262306a36Sopenharmony_ci		if (xfs_inode_has_attr_fork(dp) == 0) {
100362306a36Sopenharmony_ci			int sf_size = sizeof(struct xfs_attr_sf_hdr) +
100462306a36Sopenharmony_ci				xfs_attr_sf_entsize_byname(args->namelen,
100562306a36Sopenharmony_ci						args->valuelen);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci			error = xfs_bmap_add_attrfork(dp, sf_size, rsvd);
100862306a36Sopenharmony_ci			if (error)
100962306a36Sopenharmony_ci				return error;
101062306a36Sopenharmony_ci		}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci		if (!local)
101362306a36Sopenharmony_ci			rmt_blks = xfs_attr3_rmt_blocks(mp, args->valuelen);
101462306a36Sopenharmony_ci	} else {
101562306a36Sopenharmony_ci		XFS_STATS_INC(mp, xs_attr_remove);
101662306a36Sopenharmony_ci		rmt_blks = xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX);
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	/*
102062306a36Sopenharmony_ci	 * Root fork attributes can use reserved data blocks for this
102162306a36Sopenharmony_ci	 * operation if necessary
102262306a36Sopenharmony_ci	 */
102362306a36Sopenharmony_ci	xfs_init_attr_trans(args, &tres, &total);
102462306a36Sopenharmony_ci	error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans);
102562306a36Sopenharmony_ci	if (error)
102662306a36Sopenharmony_ci		return error;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (args->value || xfs_inode_hasattr(dp)) {
102962306a36Sopenharmony_ci		error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
103062306a36Sopenharmony_ci				XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
103162306a36Sopenharmony_ci		if (error == -EFBIG)
103262306a36Sopenharmony_ci			error = xfs_iext_count_upgrade(args->trans, dp,
103362306a36Sopenharmony_ci					XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
103462306a36Sopenharmony_ci		if (error)
103562306a36Sopenharmony_ci			goto out_trans_cancel;
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	error = xfs_attr_lookup(args);
103962306a36Sopenharmony_ci	switch (error) {
104062306a36Sopenharmony_ci	case -EEXIST:
104162306a36Sopenharmony_ci		/* if no value, we are performing a remove operation */
104262306a36Sopenharmony_ci		if (!args->value) {
104362306a36Sopenharmony_ci			error = xfs_attr_defer_remove(args);
104462306a36Sopenharmony_ci			break;
104562306a36Sopenharmony_ci		}
104662306a36Sopenharmony_ci		/* Pure create fails if the attr already exists */
104762306a36Sopenharmony_ci		if (args->attr_flags & XATTR_CREATE)
104862306a36Sopenharmony_ci			goto out_trans_cancel;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		error = xfs_attr_defer_replace(args);
105162306a36Sopenharmony_ci		break;
105262306a36Sopenharmony_ci	case -ENOATTR:
105362306a36Sopenharmony_ci		/* Can't remove what isn't there. */
105462306a36Sopenharmony_ci		if (!args->value)
105562306a36Sopenharmony_ci			goto out_trans_cancel;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci		/* Pure replace fails if no existing attr to replace. */
105862306a36Sopenharmony_ci		if (args->attr_flags & XATTR_REPLACE)
105962306a36Sopenharmony_ci			goto out_trans_cancel;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci		error = xfs_attr_defer_add(args);
106262306a36Sopenharmony_ci		break;
106362306a36Sopenharmony_ci	default:
106462306a36Sopenharmony_ci		goto out_trans_cancel;
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci	if (error)
106762306a36Sopenharmony_ci		goto out_trans_cancel;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/*
107062306a36Sopenharmony_ci	 * If this is a synchronous mount, make sure that the
107162306a36Sopenharmony_ci	 * transaction goes to disk before returning to the user.
107262306a36Sopenharmony_ci	 */
107362306a36Sopenharmony_ci	if (xfs_has_wsync(mp))
107462306a36Sopenharmony_ci		xfs_trans_set_sync(args->trans);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (!(args->op_flags & XFS_DA_OP_NOTIME))
107762306a36Sopenharmony_ci		xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	/*
108062306a36Sopenharmony_ci	 * Commit the last in the sequence of transactions.
108162306a36Sopenharmony_ci	 */
108262306a36Sopenharmony_ci	xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
108362306a36Sopenharmony_ci	error = xfs_trans_commit(args->trans);
108462306a36Sopenharmony_ciout_unlock:
108562306a36Sopenharmony_ci	xfs_iunlock(dp, XFS_ILOCK_EXCL);
108662306a36Sopenharmony_ci	return error;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ciout_trans_cancel:
108962306a36Sopenharmony_ci	if (args->trans)
109062306a36Sopenharmony_ci		xfs_trans_cancel(args->trans);
109162306a36Sopenharmony_ci	goto out_unlock;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci/*========================================================================
109562306a36Sopenharmony_ci * External routines when attribute list is inside the inode
109662306a36Sopenharmony_ci *========================================================================*/
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic inline int xfs_attr_sf_totsize(struct xfs_inode *dp)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	struct xfs_attr_shortform *sf;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data;
110362306a36Sopenharmony_ci	return be16_to_cpu(sf->hdr.totsize);
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci/*
110762306a36Sopenharmony_ci * Add a name to the shortform attribute list structure
110862306a36Sopenharmony_ci * This is the external routine.
110962306a36Sopenharmony_ci */
111062306a36Sopenharmony_cistatic int
111162306a36Sopenharmony_cixfs_attr_shortform_addname(
111262306a36Sopenharmony_ci	struct xfs_da_args	*args)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	int			newsize, forkoff;
111562306a36Sopenharmony_ci	int			error;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	trace_xfs_attr_sf_addname(args);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	error = xfs_attr_shortform_lookup(args);
112062306a36Sopenharmony_ci	switch (error) {
112162306a36Sopenharmony_ci	case -ENOATTR:
112262306a36Sopenharmony_ci		if (args->op_flags & XFS_DA_OP_REPLACE)
112362306a36Sopenharmony_ci			return error;
112462306a36Sopenharmony_ci		break;
112562306a36Sopenharmony_ci	case -EEXIST:
112662306a36Sopenharmony_ci		if (!(args->op_flags & XFS_DA_OP_REPLACE))
112762306a36Sopenharmony_ci			return error;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci		error = xfs_attr_sf_removename(args);
113062306a36Sopenharmony_ci		if (error)
113162306a36Sopenharmony_ci			return error;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		/*
113462306a36Sopenharmony_ci		 * Since we have removed the old attr, clear XFS_DA_OP_REPLACE
113562306a36Sopenharmony_ci		 * so that the new attr doesn't fit in shortform format, the
113662306a36Sopenharmony_ci		 * leaf format add routine won't trip over the attr not being
113762306a36Sopenharmony_ci		 * around.
113862306a36Sopenharmony_ci		 */
113962306a36Sopenharmony_ci		args->op_flags &= ~XFS_DA_OP_REPLACE;
114062306a36Sopenharmony_ci		break;
114162306a36Sopenharmony_ci	case 0:
114262306a36Sopenharmony_ci		break;
114362306a36Sopenharmony_ci	default:
114462306a36Sopenharmony_ci		return error;
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX ||
114862306a36Sopenharmony_ci	    args->valuelen >= XFS_ATTR_SF_ENTSIZE_MAX)
114962306a36Sopenharmony_ci		return -ENOSPC;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	newsize = xfs_attr_sf_totsize(args->dp);
115262306a36Sopenharmony_ci	newsize += xfs_attr_sf_entsize_byname(args->namelen, args->valuelen);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	forkoff = xfs_attr_shortform_bytesfit(args->dp, newsize);
115562306a36Sopenharmony_ci	if (!forkoff)
115662306a36Sopenharmony_ci		return -ENOSPC;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	xfs_attr_shortform_add(args, forkoff);
115962306a36Sopenharmony_ci	return 0;
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/*========================================================================
116462306a36Sopenharmony_ci * External routines when attribute list is one block
116562306a36Sopenharmony_ci *========================================================================*/
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci/* Save the current remote block info and clear the current pointers. */
116862306a36Sopenharmony_cistatic void
116962306a36Sopenharmony_cixfs_attr_save_rmt_blk(
117062306a36Sopenharmony_ci	struct xfs_da_args	*args)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	args->blkno2 = args->blkno;
117362306a36Sopenharmony_ci	args->index2 = args->index;
117462306a36Sopenharmony_ci	args->rmtblkno2 = args->rmtblkno;
117562306a36Sopenharmony_ci	args->rmtblkcnt2 = args->rmtblkcnt;
117662306a36Sopenharmony_ci	args->rmtvaluelen2 = args->rmtvaluelen;
117762306a36Sopenharmony_ci	args->rmtblkno = 0;
117862306a36Sopenharmony_ci	args->rmtblkcnt = 0;
117962306a36Sopenharmony_ci	args->rmtvaluelen = 0;
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci/* Set stored info about a remote block */
118362306a36Sopenharmony_cistatic void
118462306a36Sopenharmony_cixfs_attr_restore_rmt_blk(
118562306a36Sopenharmony_ci	struct xfs_da_args	*args)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	args->blkno = args->blkno2;
118862306a36Sopenharmony_ci	args->index = args->index2;
118962306a36Sopenharmony_ci	args->rmtblkno = args->rmtblkno2;
119062306a36Sopenharmony_ci	args->rmtblkcnt = args->rmtblkcnt2;
119162306a36Sopenharmony_ci	args->rmtvaluelen = args->rmtvaluelen2;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci/*
119562306a36Sopenharmony_ci * Tries to add an attribute to an inode in leaf form
119662306a36Sopenharmony_ci *
119762306a36Sopenharmony_ci * This function is meant to execute as part of a delayed operation and leaves
119862306a36Sopenharmony_ci * the transaction handling to the caller.  On success the attribute is added
119962306a36Sopenharmony_ci * and the inode and transaction are left dirty.  If there is not enough space,
120062306a36Sopenharmony_ci * the attr data is converted to node format and -ENOSPC is returned. Caller is
120162306a36Sopenharmony_ci * responsible for handling the dirty inode and transaction or adding the attr
120262306a36Sopenharmony_ci * in node format.
120362306a36Sopenharmony_ci */
120462306a36Sopenharmony_ciSTATIC int
120562306a36Sopenharmony_cixfs_attr_leaf_try_add(
120662306a36Sopenharmony_ci	struct xfs_da_args	*args)
120762306a36Sopenharmony_ci{
120862306a36Sopenharmony_ci	struct xfs_buf		*bp;
120962306a36Sopenharmony_ci	int			error;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
121262306a36Sopenharmony_ci	if (error)
121362306a36Sopenharmony_ci		return error;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	/*
121662306a36Sopenharmony_ci	 * Look up the xattr name to set the insertion point for the new xattr.
121762306a36Sopenharmony_ci	 */
121862306a36Sopenharmony_ci	error = xfs_attr3_leaf_lookup_int(bp, args);
121962306a36Sopenharmony_ci	switch (error) {
122062306a36Sopenharmony_ci	case -ENOATTR:
122162306a36Sopenharmony_ci		if (args->op_flags & XFS_DA_OP_REPLACE)
122262306a36Sopenharmony_ci			goto out_brelse;
122362306a36Sopenharmony_ci		break;
122462306a36Sopenharmony_ci	case -EEXIST:
122562306a36Sopenharmony_ci		if (!(args->op_flags & XFS_DA_OP_REPLACE))
122662306a36Sopenharmony_ci			goto out_brelse;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci		trace_xfs_attr_leaf_replace(args);
122962306a36Sopenharmony_ci		/*
123062306a36Sopenharmony_ci		 * Save the existing remote attr state so that the current
123162306a36Sopenharmony_ci		 * values reflect the state of the new attribute we are about to
123262306a36Sopenharmony_ci		 * add, not the attribute we just found and will remove later.
123362306a36Sopenharmony_ci		 */
123462306a36Sopenharmony_ci		xfs_attr_save_rmt_blk(args);
123562306a36Sopenharmony_ci		break;
123662306a36Sopenharmony_ci	case 0:
123762306a36Sopenharmony_ci		break;
123862306a36Sopenharmony_ci	default:
123962306a36Sopenharmony_ci		goto out_brelse;
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	return xfs_attr3_leaf_add(bp, args);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ciout_brelse:
124562306a36Sopenharmony_ci	xfs_trans_brelse(args->trans, bp);
124662306a36Sopenharmony_ci	return error;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci/*
125062306a36Sopenharmony_ci * Return EEXIST if attr is found, or ENOATTR if not
125162306a36Sopenharmony_ci */
125262306a36Sopenharmony_ciSTATIC int
125362306a36Sopenharmony_cixfs_attr_leaf_hasname(
125462306a36Sopenharmony_ci	struct xfs_da_args	*args,
125562306a36Sopenharmony_ci	struct xfs_buf		**bp)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	int                     error = 0;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	error = xfs_attr3_leaf_read(args->trans, args->dp, 0, bp);
126062306a36Sopenharmony_ci	if (error)
126162306a36Sopenharmony_ci		return error;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	error = xfs_attr3_leaf_lookup_int(*bp, args);
126462306a36Sopenharmony_ci	if (error != -ENOATTR && error != -EEXIST)
126562306a36Sopenharmony_ci		xfs_trans_brelse(args->trans, *bp);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	return error;
126862306a36Sopenharmony_ci}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci/*
127162306a36Sopenharmony_ci * Remove a name from the leaf attribute list structure
127262306a36Sopenharmony_ci *
127362306a36Sopenharmony_ci * This leaf block cannot have a "remote" value, we only call this routine
127462306a36Sopenharmony_ci * if bmap_one_block() says there is only one block (ie: no remote blks).
127562306a36Sopenharmony_ci */
127662306a36Sopenharmony_ciSTATIC int
127762306a36Sopenharmony_cixfs_attr_leaf_removename(
127862306a36Sopenharmony_ci	struct xfs_da_args	*args)
127962306a36Sopenharmony_ci{
128062306a36Sopenharmony_ci	struct xfs_inode	*dp;
128162306a36Sopenharmony_ci	struct xfs_buf		*bp;
128262306a36Sopenharmony_ci	int			error, forkoff;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	trace_xfs_attr_leaf_removename(args);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/*
128762306a36Sopenharmony_ci	 * Remove the attribute.
128862306a36Sopenharmony_ci	 */
128962306a36Sopenharmony_ci	dp = args->dp;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	error = xfs_attr_leaf_hasname(args, &bp);
129262306a36Sopenharmony_ci	if (error == -ENOATTR) {
129362306a36Sopenharmony_ci		xfs_trans_brelse(args->trans, bp);
129462306a36Sopenharmony_ci		if (args->op_flags & XFS_DA_OP_RECOVERY)
129562306a36Sopenharmony_ci			return 0;
129662306a36Sopenharmony_ci		return error;
129762306a36Sopenharmony_ci	} else if (error != -EEXIST)
129862306a36Sopenharmony_ci		return error;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	xfs_attr3_leaf_remove(bp, args);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	/*
130362306a36Sopenharmony_ci	 * If the result is small enough, shrink it all into the inode.
130462306a36Sopenharmony_ci	 */
130562306a36Sopenharmony_ci	forkoff = xfs_attr_shortform_allfit(bp, dp);
130662306a36Sopenharmony_ci	if (forkoff)
130762306a36Sopenharmony_ci		return xfs_attr3_leaf_to_shortform(bp, args, forkoff);
130862306a36Sopenharmony_ci		/* bp is gone due to xfs_da_shrink_inode */
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	return 0;
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci/*
131462306a36Sopenharmony_ci * Look up a name in a leaf attribute list structure.
131562306a36Sopenharmony_ci *
131662306a36Sopenharmony_ci * This leaf block cannot have a "remote" value, we only call this routine
131762306a36Sopenharmony_ci * if bmap_one_block() says there is only one block (ie: no remote blks).
131862306a36Sopenharmony_ci *
131962306a36Sopenharmony_ci * Returns 0 on successful retrieval, otherwise an error.
132062306a36Sopenharmony_ci */
132162306a36Sopenharmony_ciSTATIC int
132262306a36Sopenharmony_cixfs_attr_leaf_get(xfs_da_args_t *args)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	struct xfs_buf *bp;
132562306a36Sopenharmony_ci	int error;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	trace_xfs_attr_leaf_get(args);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	error = xfs_attr_leaf_hasname(args, &bp);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	if (error == -ENOATTR)  {
133262306a36Sopenharmony_ci		xfs_trans_brelse(args->trans, bp);
133362306a36Sopenharmony_ci		return error;
133462306a36Sopenharmony_ci	} else if (error != -EEXIST)
133562306a36Sopenharmony_ci		return error;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	error = xfs_attr3_leaf_getvalue(bp, args);
133962306a36Sopenharmony_ci	xfs_trans_brelse(args->trans, bp);
134062306a36Sopenharmony_ci	return error;
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci/* Return EEXIST if attr is found, or ENOATTR if not. */
134462306a36Sopenharmony_ciSTATIC int
134562306a36Sopenharmony_cixfs_attr_node_lookup(
134662306a36Sopenharmony_ci	struct xfs_da_args	*args,
134762306a36Sopenharmony_ci	struct xfs_da_state	*state)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	int			retval, error;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/*
135262306a36Sopenharmony_ci	 * Search to see if name exists, and get back a pointer to it.
135362306a36Sopenharmony_ci	 */
135462306a36Sopenharmony_ci	error = xfs_da3_node_lookup_int(state, &retval);
135562306a36Sopenharmony_ci	if (error)
135662306a36Sopenharmony_ci		return error;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	return retval;
135962306a36Sopenharmony_ci}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci/*========================================================================
136262306a36Sopenharmony_ci * External routines when attribute list size > geo->blksize
136362306a36Sopenharmony_ci *========================================================================*/
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ciSTATIC int
136662306a36Sopenharmony_cixfs_attr_node_addname_find_attr(
136762306a36Sopenharmony_ci	 struct xfs_attr_intent	*attr)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct xfs_da_args	*args = attr->xattri_da_args;
137062306a36Sopenharmony_ci	int			error;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	/*
137362306a36Sopenharmony_ci	 * Search to see if name already exists, and get back a pointer
137462306a36Sopenharmony_ci	 * to where it should go.
137562306a36Sopenharmony_ci	 */
137662306a36Sopenharmony_ci	xfs_attr_item_init_da_state(attr);
137762306a36Sopenharmony_ci	error = xfs_attr_node_lookup(args, attr->xattri_da_state);
137862306a36Sopenharmony_ci	switch (error) {
137962306a36Sopenharmony_ci	case -ENOATTR:
138062306a36Sopenharmony_ci		if (args->op_flags & XFS_DA_OP_REPLACE)
138162306a36Sopenharmony_ci			goto error;
138262306a36Sopenharmony_ci		break;
138362306a36Sopenharmony_ci	case -EEXIST:
138462306a36Sopenharmony_ci		if (!(args->op_flags & XFS_DA_OP_REPLACE))
138562306a36Sopenharmony_ci			goto error;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci		trace_xfs_attr_node_replace(args);
138962306a36Sopenharmony_ci		/*
139062306a36Sopenharmony_ci		 * Save the existing remote attr state so that the current
139162306a36Sopenharmony_ci		 * values reflect the state of the new attribute we are about to
139262306a36Sopenharmony_ci		 * add, not the attribute we just found and will remove later.
139362306a36Sopenharmony_ci		 */
139462306a36Sopenharmony_ci		xfs_attr_save_rmt_blk(args);
139562306a36Sopenharmony_ci		break;
139662306a36Sopenharmony_ci	case 0:
139762306a36Sopenharmony_ci		break;
139862306a36Sopenharmony_ci	default:
139962306a36Sopenharmony_ci		goto error;
140062306a36Sopenharmony_ci	}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	return 0;
140362306a36Sopenharmony_cierror:
140462306a36Sopenharmony_ci	if (attr->xattri_da_state) {
140562306a36Sopenharmony_ci		xfs_da_state_free(attr->xattri_da_state);
140662306a36Sopenharmony_ci		attr->xattri_da_state = NULL;
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci	return error;
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci/*
141262306a36Sopenharmony_ci * Add a name to a Btree-format attribute list.
141362306a36Sopenharmony_ci *
141462306a36Sopenharmony_ci * This will involve walking down the Btree, and may involve splitting
141562306a36Sopenharmony_ci * leaf nodes and even splitting intermediate nodes up to and including
141662306a36Sopenharmony_ci * the root node (a special case of an intermediate node).
141762306a36Sopenharmony_ci */
141862306a36Sopenharmony_cistatic int
141962306a36Sopenharmony_cixfs_attr_node_try_addname(
142062306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	struct xfs_da_state		*state = attr->xattri_da_state;
142362306a36Sopenharmony_ci	struct xfs_da_state_blk		*blk;
142462306a36Sopenharmony_ci	int				error;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	trace_xfs_attr_node_addname(state->args);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	blk = &state->path.blk[state->path.active-1];
142962306a36Sopenharmony_ci	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	error = xfs_attr3_leaf_add(blk->bp, state->args);
143262306a36Sopenharmony_ci	if (error == -ENOSPC) {
143362306a36Sopenharmony_ci		if (state->path.active == 1) {
143462306a36Sopenharmony_ci			/*
143562306a36Sopenharmony_ci			 * Its really a single leaf node, but it had
143662306a36Sopenharmony_ci			 * out-of-line values so it looked like it *might*
143762306a36Sopenharmony_ci			 * have been a b-tree. Let the caller deal with this.
143862306a36Sopenharmony_ci			 */
143962306a36Sopenharmony_ci			goto out;
144062306a36Sopenharmony_ci		}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci		/*
144362306a36Sopenharmony_ci		 * Split as many Btree elements as required.
144462306a36Sopenharmony_ci		 * This code tracks the new and old attr's location
144562306a36Sopenharmony_ci		 * in the index/blkno/rmtblkno/rmtblkcnt fields and
144662306a36Sopenharmony_ci		 * in the index2/blkno2/rmtblkno2/rmtblkcnt2 fields.
144762306a36Sopenharmony_ci		 */
144862306a36Sopenharmony_ci		error = xfs_da3_split(state);
144962306a36Sopenharmony_ci		if (error)
145062306a36Sopenharmony_ci			goto out;
145162306a36Sopenharmony_ci	} else {
145262306a36Sopenharmony_ci		/*
145362306a36Sopenharmony_ci		 * Addition succeeded, update Btree hashvals.
145462306a36Sopenharmony_ci		 */
145562306a36Sopenharmony_ci		xfs_da3_fixhashpath(state, &state->path);
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ciout:
145962306a36Sopenharmony_ci	xfs_da_state_free(state);
146062306a36Sopenharmony_ci	attr->xattri_da_state = NULL;
146162306a36Sopenharmony_ci	return error;
146262306a36Sopenharmony_ci}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_cistatic int
146562306a36Sopenharmony_cixfs_attr_node_removename(
146662306a36Sopenharmony_ci	struct xfs_da_args	*args,
146762306a36Sopenharmony_ci	struct xfs_da_state	*state)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct xfs_da_state_blk	*blk;
147062306a36Sopenharmony_ci	int			retval;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	/*
147362306a36Sopenharmony_ci	 * Remove the name and update the hashvals in the tree.
147462306a36Sopenharmony_ci	 */
147562306a36Sopenharmony_ci	blk = &state->path.blk[state->path.active-1];
147662306a36Sopenharmony_ci	ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
147762306a36Sopenharmony_ci	retval = xfs_attr3_leaf_remove(blk->bp, args);
147862306a36Sopenharmony_ci	xfs_da3_fixhashpath(state, &state->path);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	return retval;
148162306a36Sopenharmony_ci}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_cistatic int
148462306a36Sopenharmony_cixfs_attr_node_remove_attr(
148562306a36Sopenharmony_ci	struct xfs_attr_intent		*attr)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	struct xfs_da_args		*args = attr->xattri_da_args;
148862306a36Sopenharmony_ci	struct xfs_da_state		*state = xfs_da_state_alloc(args);
148962306a36Sopenharmony_ci	int				retval = 0;
149062306a36Sopenharmony_ci	int				error = 0;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	/*
149362306a36Sopenharmony_ci	 * The attr we are removing has already been marked incomplete, so
149462306a36Sopenharmony_ci	 * we need to set the filter appropriately to re-find the "old"
149562306a36Sopenharmony_ci	 * attribute entry after any split ops.
149662306a36Sopenharmony_ci	 */
149762306a36Sopenharmony_ci	args->attr_filter |= XFS_ATTR_INCOMPLETE;
149862306a36Sopenharmony_ci	error = xfs_da3_node_lookup_int(state, &retval);
149962306a36Sopenharmony_ci	if (error)
150062306a36Sopenharmony_ci		goto out;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	error = xfs_attr_node_removename(args, state);
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	/*
150562306a36Sopenharmony_ci	 * Check to see if the tree needs to be collapsed.
150662306a36Sopenharmony_ci	 */
150762306a36Sopenharmony_ci	if (retval && (state->path.active > 1)) {
150862306a36Sopenharmony_ci		error = xfs_da3_join(state);
150962306a36Sopenharmony_ci		if (error)
151062306a36Sopenharmony_ci			goto out;
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci	retval = error = 0;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ciout:
151562306a36Sopenharmony_ci	xfs_da_state_free(state);
151662306a36Sopenharmony_ci	if (error)
151762306a36Sopenharmony_ci		return error;
151862306a36Sopenharmony_ci	return retval;
151962306a36Sopenharmony_ci}
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci/*
152262306a36Sopenharmony_ci * Retrieve the attribute data from a node attribute list.
152362306a36Sopenharmony_ci *
152462306a36Sopenharmony_ci * This routine gets called for any attribute fork that has more than one
152562306a36Sopenharmony_ci * block, ie: both true Btree attr lists and for single-leaf-blocks with
152662306a36Sopenharmony_ci * "remote" values taking up more blocks.
152762306a36Sopenharmony_ci *
152862306a36Sopenharmony_ci * Returns 0 on successful retrieval, otherwise an error.
152962306a36Sopenharmony_ci */
153062306a36Sopenharmony_ciSTATIC int
153162306a36Sopenharmony_cixfs_attr_node_get(
153262306a36Sopenharmony_ci	struct xfs_da_args	*args)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	struct xfs_da_state	*state;
153562306a36Sopenharmony_ci	struct xfs_da_state_blk	*blk;
153662306a36Sopenharmony_ci	int			i;
153762306a36Sopenharmony_ci	int			error;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	trace_xfs_attr_node_get(args);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/*
154262306a36Sopenharmony_ci	 * Search to see if name exists, and get back a pointer to it.
154362306a36Sopenharmony_ci	 */
154462306a36Sopenharmony_ci	state = xfs_da_state_alloc(args);
154562306a36Sopenharmony_ci	error = xfs_attr_node_lookup(args, state);
154662306a36Sopenharmony_ci	if (error != -EEXIST)
154762306a36Sopenharmony_ci		goto out_release;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	/*
155062306a36Sopenharmony_ci	 * Get the value, local or "remote"
155162306a36Sopenharmony_ci	 */
155262306a36Sopenharmony_ci	blk = &state->path.blk[state->path.active - 1];
155362306a36Sopenharmony_ci	error = xfs_attr3_leaf_getvalue(blk->bp, args);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	/*
155662306a36Sopenharmony_ci	 * If not in a transaction, we have to release all the buffers.
155762306a36Sopenharmony_ci	 */
155862306a36Sopenharmony_ciout_release:
155962306a36Sopenharmony_ci	for (i = 0; i < state->path.active; i++) {
156062306a36Sopenharmony_ci		xfs_trans_brelse(args->trans, state->path.blk[i].bp);
156162306a36Sopenharmony_ci		state->path.blk[i].bp = NULL;
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	xfs_da_state_free(state);
156562306a36Sopenharmony_ci	return error;
156662306a36Sopenharmony_ci}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci/* Returns true if the attribute entry name is valid. */
156962306a36Sopenharmony_cibool
157062306a36Sopenharmony_cixfs_attr_namecheck(
157162306a36Sopenharmony_ci	const void	*name,
157262306a36Sopenharmony_ci	size_t		length)
157362306a36Sopenharmony_ci{
157462306a36Sopenharmony_ci	/*
157562306a36Sopenharmony_ci	 * MAXNAMELEN includes the trailing null, but (name/length) leave it
157662306a36Sopenharmony_ci	 * out, so use >= for the length check.
157762306a36Sopenharmony_ci	 */
157862306a36Sopenharmony_ci	if (length >= MAXNAMELEN)
157962306a36Sopenharmony_ci		return false;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	/* There shouldn't be any nulls here */
158262306a36Sopenharmony_ci	return !memchr(name, 0, length);
158362306a36Sopenharmony_ci}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ciint __init
158662306a36Sopenharmony_cixfs_attr_intent_init_cache(void)
158762306a36Sopenharmony_ci{
158862306a36Sopenharmony_ci	xfs_attr_intent_cache = kmem_cache_create("xfs_attr_intent",
158962306a36Sopenharmony_ci			sizeof(struct xfs_attr_intent),
159062306a36Sopenharmony_ci			0, 0, NULL);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	return xfs_attr_intent_cache != NULL ? 0 : -ENOMEM;
159362306a36Sopenharmony_ci}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_civoid
159662306a36Sopenharmony_cixfs_attr_intent_destroy_cache(void)
159762306a36Sopenharmony_ci{
159862306a36Sopenharmony_ci	kmem_cache_destroy(xfs_attr_intent_cache);
159962306a36Sopenharmony_ci	xfs_attr_intent_cache = NULL;
160062306a36Sopenharmony_ci}
1601