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