/* * xfs_attr.c * * Provide the external interfaces to manage attribute lists.
*/
/*======================================================================== * Function prototypes for the kernel.
*========================================================================*/
/* * Internal routines when attribute list fits inside the inode.
*/ STATICint xfs_attr_shortform_addname(xfs_da_args_t *args);
/* * Internal routines when attribute list is one block.
*/ STATICint xfs_attr_leaf_get(xfs_da_args_t *args); STATICint xfs_attr_leaf_removename(xfs_da_args_t *args); STATICint xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
/* * Internal routines when attribute list is more than one block.
*/ STATICint xfs_attr_node_get(xfs_da_args_t *args); STATICvoid xfs_attr_restore_rmt_blk(struct xfs_da_args *args); staticint xfs_attr_node_try_addname(struct xfs_attr_intent *attr); STATICint xfs_attr_node_addname_find_attr(struct xfs_attr_intent *attr); STATICint xfs_attr_node_remove_attr(struct xfs_attr_intent *attr); STATICint xfs_attr_node_lookup(struct xfs_da_args *args, struct xfs_da_state *state);
int
xfs_inode_hasattr( struct xfs_inode *ip)
{ if (!xfs_inode_has_attr_fork(ip)) return 0; if (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&
ip->i_af.if_nextents == 0) return 0; return 1;
}
/* * Returns true if the there is exactly only block in the attr fork, in which * case the attribute fork consists of a single leaf block entry.
*/ bool
xfs_attr_is_leaf( struct xfs_inode *ip)
{ struct xfs_ifork *ifp = &ip->i_af; struct xfs_iext_cursor icur; struct xfs_bmbt_irec imap;
ASSERT(!xfs_need_iread_extents(ifp));
if (ifp->if_nextents != 1 || ifp->if_format != XFS_DINODE_FMT_EXTENTS) returnfalse;
/* * XXX (dchinner): name path state saving and refilling is an optimisation to * avoid needing to look up name entries after rolling transactions removing * remote xattr blocks between the name entry lookup and name entry removal. * This optimisation got sidelined when combining the set and remove state * machines, but the code has been left in place because it is worthwhile to * restore the optimisation once the combined state machine paths have settled. * * This comment is a public service announcement to remind Future Dave that he * still needs to restore this code to working order.
*/ #if 0 /* * Fill in the disk block numbers in the state structure for the buffers * that are attached to the state structure. * This is done so that we can quickly reattach ourselves to those buffers * after some set of transaction commits have released these buffers.
*/ staticint
xfs_attr_fillstate(xfs_da_state_t *state)
{
xfs_da_state_path_t *path;
xfs_da_state_blk_t *blk; int level;
trace_xfs_attr_fillstate(state->args);
/* * Roll down the "path" in the state structure, storing the on-disk * block number for those buffers in the "path".
*/
path = &state->path;
ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); for (blk = path->blk, level = 0; level < path->active; blk++, level++) { if (blk->bp) {
blk->disk_blkno = xfs_buf_daddr(blk->bp);
blk->bp = NULL;
} else {
blk->disk_blkno = 0;
}
}
/* * Roll down the "altpath" in the state structure, storing the on-disk * block number for those buffers in the "altpath".
*/
path = &state->altpath;
ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); for (blk = path->blk, level = 0; level < path->active; blk++, level++) { if (blk->bp) {
blk->disk_blkno = xfs_buf_daddr(blk->bp);
blk->bp = NULL;
} else {
blk->disk_blkno = 0;
}
}
return 0;
}
/* * Reattach the buffers to the state structure based on the disk block * numbers stored in the state structure. * This is done after some set of transaction commits have released those * buffers from our grip.
*/ staticint
xfs_attr_refillstate(xfs_da_state_t *state)
{
xfs_da_state_path_t *path;
xfs_da_state_blk_t *blk; int level, error;
trace_xfs_attr_refillstate(state->args);
/* * Roll down the "path" in the state structure, storing the on-disk * block number for those buffers in the "path".
*/
path = &state->path;
ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); for (blk = path->blk, level = 0; level < path->active; blk++, level++) { if (blk->disk_blkno) {
error = xfs_da3_node_read_mapped(state->args->trans,
state->args->dp, blk->disk_blkno,
&blk->bp, XFS_ATTR_FORK); if (error) return error;
} else {
blk->bp = NULL;
}
}
/* * Roll down the "altpath" in the state structure, storing the on-disk * block number for those buffers in the "altpath".
*/
path = &state->altpath;
ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH)); for (blk = path->blk, level = 0; level < path->active; blk++, level++) { if (blk->disk_blkno) {
error = xfs_da3_node_read_mapped(state->args->trans,
state->args->dp, blk->disk_blkno,
&blk->bp, XFS_ATTR_FORK); if (error) return error;
} else {
blk->bp = NULL;
}
}
/* * Retrieve an extended attribute and its value. Must have ilock. * Returns 0 on successful retrieval, otherwise an error.
*/ int
xfs_attr_get_ilocked( struct xfs_da_args *args)
{ int error;
if (!xfs_inode_hasattr(args->dp)) return -ENOATTR;
/* * The incore attr fork iext tree must be loaded for xfs_attr_is_leaf * to work correctly.
*/
error = xfs_iread_extents(args->trans, args->dp, XFS_ATTR_FORK); if (error) return error;
if (args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL) return xfs_attr_shortform_getvalue(args); if (xfs_attr_is_leaf(args->dp)) return xfs_attr_leaf_get(args); return xfs_attr_node_get(args);
}
/* * Retrieve an extended attribute by name, and its value if requested. * * If args->valuelen is zero, then the caller does not want the value, just an * indication whether the attribute exists and the size of the value if it * exists. The size is returned in args.valuelen. * * If args->value is NULL but args->valuelen is non-zero, allocate the buffer * for the value after existence of the attribute has been determined. The * caller always has to free args->value if it is set, no matter if this * function was successful or not. * * If the attribute is found, but exceeds the size limit set by the caller in * args->valuelen, return -ERANGE with the size of the attribute that was found * in args->valuelen.
*/ int
xfs_attr_get( struct xfs_da_args *args)
{
uint lock_mode; int error;
XFS_STATS_INC(args->dp->i_mount, xs_attr_get);
if (xfs_is_shutdown(args->dp->i_mount)) return -EIO;
/* * Calculate how many blocks we need for the new attribute,
*/ int
xfs_attr_calc_size( struct xfs_da_args *args, int *local)
{ struct xfs_mount *mp = args->dp->i_mount; int size; int nblks;
/* * Determine space new attribute will use, and if it would be * "local" or "remote" (note: local != inline).
*/
size = xfs_attr_leaf_newentsize(args, local);
nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK); if (*local) { if (size > (args->geo->blksize / 2)) { /* Double split possible */
nblks *= 2;
}
} else { /* * Out of line attribute, cannot double split, but * make room for the attribute value itself.
*/
uint dblocks = xfs_attr3_rmt_blocks(mp, args->valuelen);
nblks += dblocks;
nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK);
}
/* * Add an attr to a shortform fork. If there is no space, * xfs_attr_shortform_addname() will convert to leaf format and return -ENOSPC. * to use.
*/ STATICint
xfs_attr_try_sf_addname( struct xfs_inode *dp, struct xfs_da_args *args)
{
int error;
/* * Build initial attribute list (if required).
*/ if (dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS)
xfs_attr_shortform_create(args);
error = xfs_attr_shortform_addname(args); if (error == -ENOSPC) return error;
/* * Commit the shortform mods, and we're done. * NOTE: this is also the error path (EEXIST, etc).
*/ if (!error)
xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
if (xfs_has_wsync(dp->i_mount))
xfs_trans_set_sync(args->trans);
/* * It won't fit in the shortform, transform to a leaf block. GROT: * another possible req'mt for a double-split btree op.
*/
error = xfs_attr_shortform_to_leaf(args); if (error) return error;
/* Compute the hash value for a user/root/secure extended attribute */
xfs_dahash_t
xfs_attr_hashname( const uint8_t *name, int namelen)
{ return xfs_da_hashname(name, namelen);
}
/* Compute the hash value for any extended attribute from any namespace. */
xfs_dahash_t
xfs_attr_hashval( struct xfs_mount *mp, unsignedint attr_flags, const uint8_t *name, int namelen, constvoid *value, int valuelen)
{
ASSERT(xfs_attr_check_namespace(attr_flags));
if (attr_flags & XFS_ATTR_PARENT) return xfs_parent_hashattr(mp, name, namelen, value, valuelen);
return xfs_attr_hashname(name, namelen);
}
/* Save the current remote block info and clear the current pointers. */ staticvoid
xfs_attr_save_rmt_blk( struct xfs_da_args *args)
{
args->blkno2 = args->blkno;
args->index2 = args->index;
args->rmtblkno2 = args->rmtblkno;
args->rmtblkcnt2 = args->rmtblkcnt;
args->rmtvaluelen2 = args->rmtvaluelen;
args->rmtblkno = 0;
args->rmtblkcnt = 0;
args->rmtvaluelen = 0;
}
/* Set stored info about a remote block */ staticvoid
xfs_attr_restore_rmt_blk( struct xfs_da_args *args)
{
args->blkno = args->blkno2;
args->index = args->index2;
args->rmtblkno = args->rmtblkno2;
args->rmtblkcnt = args->rmtblkcnt2;
args->rmtvaluelen = args->rmtvaluelen2;
}
/* * PPTR_REPLACE operations require the caller to set the old and new names and * values explicitly. Update the canonical fields to the new name and value * here now that the removal phase has finished.
*/ staticvoid
xfs_attr_update_pptr_replace_args( struct xfs_da_args *args)
{
ASSERT(args->new_namelen > 0);
args->name = args->new_name;
args->namelen = args->new_namelen;
args->value = args->new_value;
args->valuelen = args->new_valuelen;
xfs_attr_sethash(args);
}
/* * Handle the state change on completion of a multi-state attr operation. * * If the XFS_DA_OP_REPLACE flag is set, this means the operation was the first * modification in a attr replace operation and we still have to do the second * state, indicated by @replace_state. * * We consume the XFS_DA_OP_REPLACE flag so that when we are called again on * completion of the second half of the attr replace operation we correctly * signal that it is done.
*/ staticenum xfs_delattr_state
xfs_attr_complete_op( struct xfs_attr_intent *attr, enum xfs_delattr_state replace_state)
{ struct xfs_da_args *args = attr->xattri_da_args;
/* * Try to add an attribute to an inode in leaf form.
*/ staticint
xfs_attr_leaf_addname( struct xfs_attr_intent *attr)
{ struct xfs_da_args *args = attr->xattri_da_args; struct xfs_buf *bp; int error;
/* * Look up the xattr name to set the insertion point for the new xattr.
*/
error = xfs_attr3_leaf_lookup_int(bp, args); switch (error) { case -ENOATTR: if (args->op_flags & XFS_DA_OP_REPLACE) goto out_brelse; break; case -EEXIST: if (!(args->op_flags & XFS_DA_OP_REPLACE)) goto out_brelse;
trace_xfs_attr_leaf_replace(args); /* * Save the existing remote attr state so that the current * values reflect the state of the new attribute we are about to * add, not the attribute we just found and will remove later.
*/
xfs_attr_save_rmt_blk(args); break; case 0: break; default: goto out_brelse;
}
/* * We need to commit and roll if we need to allocate remote xattr blocks * or perform more xattr manipulations. Otherwise there is nothing more * to do and we can return success.
*/ if (!xfs_attr3_leaf_add(bp, args)) {
error = xfs_attr3_leaf_to_node(args); if (error) return error;
/* * Add an entry to a node format attr tree. * * Note that we might still have a leaf here - xfs_attr_is_leaf() cannot tell * the difference between leaf + remote attr blocks and a node format tree, * so we may still end up having to convert from leaf to node format here.
*/ staticint
xfs_attr_node_addname( struct xfs_attr_intent *attr)
{ struct xfs_da_args *args = attr->xattri_da_args; int error;
error = xfs_attr_node_addname_find_attr(attr); if (error) return error;
error = xfs_attr_node_try_addname(attr); if (error == 1) {
error = xfs_attr3_leaf_to_node(args); if (error) return error; /* * No state change, we really are in node form now * but we need the transaction rolled to continue.
*/ goto out;
} if (error) return error;
/* * If there was an out-of-line value, allocate the blocks we * identified for its storage and copy the value. This is done * after we create the attribute so that we don't overflow the * maximum size of a transaction and/or hit a deadlock.
*/ if (attr->xattri_blkcnt > 0) {
error = xfs_attr_rmtval_set_blk(attr); if (error) return error; /* Roll the transaction only if there is more to allocate. */ if (attr->xattri_blkcnt > 0) goto out;
}
error = xfs_attr_rmtval_set_value(args); if (error) return error;
attr->xattri_dela_state = xfs_attr_complete_op(attr,
++attr->xattri_dela_state); /* * If we are not doing a rename, we've finished the operation but still * have to clear the incomplete flag protecting the new attr from * exposing partially initialised state if we crash during creation.
*/ if (attr->xattri_dela_state == XFS_DAS_DONE)
error = xfs_attr3_leaf_clearflag(args);
out:
trace_xfs_attr_rmtval_alloc(attr->xattri_dela_state, args->dp); return error;
}
/* * Mark an attribute entry INCOMPLETE and save pointers to the relevant buffers * for later deletion of the entry.
*/ staticint
xfs_attr_leaf_mark_incomplete( struct xfs_da_args *args, struct xfs_da_state *state)
{ int error;
/* * Fill in disk block numbers in the state structure * so that we can get the buffers back after we commit * several transactions in the following calls.
*/
error = xfs_attr_fillstate(state); if (error) return error;
/* * Mark the attribute as INCOMPLETE
*/ return xfs_attr3_leaf_setflag(args);
}
/* Ensure the da state of an xattr deferred work item is ready to go. */ staticinlinevoid
xfs_attr_item_init_da_state( struct xfs_attr_intent *attr)
{ struct xfs_da_args *args = attr->xattri_da_args;
if (!attr->xattri_da_state)
attr->xattri_da_state = xfs_da_state_alloc(args); else
xfs_da_state_reset(attr->xattri_da_state, args);
}
/* * Initial setup for xfs_attr_node_removename. Make sure the attr is there and * the blocks are valid. Attr keys with remote blocks will be marked * incomplete.
*/ static int xfs_attr_node_removename_setup( struct xfs_attr_intent *attr)
{ struct xfs_da_args *args = attr->xattri_da_args; struct xfs_da_state *state; int error;
error = xfs_attr_leaf_mark_incomplete(args, state); if (error) goto out; if (args->rmtblkno > 0)
error = xfs_attr_rmtval_invalidate(args);
out: if (error) {
xfs_da_state_free(attr->xattri_da_state);
attr->xattri_da_state = NULL;
}
return error;
}
/* * Remove the original attr we have just replaced. This is dependent on the * original lookup and insert placing the old attr in args->blkno/args->index * and the new attr in args->blkno2/args->index2.
*/ staticint
xfs_attr_leaf_remove_attr( struct xfs_attr_intent *attr)
{ struct xfs_da_args *args = attr->xattri_da_args; struct xfs_inode *dp = args->dp; struct xfs_buf *bp = NULL; int forkoff; int error;
forkoff = xfs_attr_shortform_allfit(bp, dp); if (forkoff)
error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); /* bp is gone due to xfs_da_shrink_inode */
return error;
}
/* * Shrink an attribute from leaf to shortform. Used by the node format remove * path when the node format collapses to a single block and so we have to check * if it can be collapsed further.
*/ staticint
xfs_attr_leaf_shrink( struct xfs_da_args *args)
{ struct xfs_inode *dp = args->dp; struct xfs_buf *bp; int forkoff; int error;
forkoff = xfs_attr_shortform_allfit(bp, dp); if (forkoff) {
error = xfs_attr3_leaf_to_shortform(bp, args, forkoff); /* bp is gone due to xfs_da_shrink_inode */
} else {
xfs_trans_brelse(args->trans, bp);
}
return error;
}
/* * Run the attribute operation specified in @attr. * * This routine is meant to function as a delayed operation and will set the * state to XFS_DAS_DONE when the operation is complete. Calling functions will * need to handle this, and recall the function until either an error or * XFS_DAS_DONE is detected.
*/ int
xfs_attr_set_iter( struct xfs_attr_intent *attr)
{ struct xfs_da_args *args = attr->xattri_da_args; int error = 0;
/* State machine switch */
next_state: switch (attr->xattri_dela_state) { case XFS_DAS_UNINIT:
ASSERT(0); return -EFSCORRUPTED; case XFS_DAS_SF_ADD: return xfs_attr_sf_addname(attr); case XFS_DAS_LEAF_ADD: return xfs_attr_leaf_addname(attr); case XFS_DAS_NODE_ADD: return xfs_attr_node_addname(attr);
case XFS_DAS_SF_REMOVE:
error = xfs_attr_sf_removename(args);
attr->xattri_dela_state = xfs_attr_complete_op(attr,
xfs_attr_init_add_state(args)); break; case XFS_DAS_LEAF_REMOVE:
error = xfs_attr_leaf_removename(args);
attr->xattri_dela_state = xfs_attr_complete_op(attr,
xfs_attr_init_add_state(args)); break; case XFS_DAS_NODE_REMOVE:
error = xfs_attr_node_removename_setup(attr); if (error == -ENOATTR &&
(args->op_flags & XFS_DA_OP_RECOVERY)) {
attr->xattri_dela_state = xfs_attr_complete_op(attr,
xfs_attr_init_add_state(args));
error = 0; break;
} if (error) return error;
attr->xattri_dela_state = XFS_DAS_NODE_REMOVE_RMT; if (args->rmtblkno == 0)
attr->xattri_dela_state++; break;
case XFS_DAS_LEAF_SET_RMT: case XFS_DAS_NODE_SET_RMT:
error = xfs_attr_rmtval_find_space(attr); if (error) return error;
attr->xattri_dela_state++;
fallthrough;
case XFS_DAS_LEAF_ALLOC_RMT: case XFS_DAS_NODE_ALLOC_RMT:
error = xfs_attr_rmtval_alloc(attr); if (error) return error; if (attr->xattri_dela_state == XFS_DAS_DONE) break; goto next_state;
case XFS_DAS_LEAF_REPLACE: case XFS_DAS_NODE_REPLACE: /* * We must "flip" the incomplete flags on the "new" and "old" * attribute/value pairs so that one disappears and one appears * atomically.
*/
error = xfs_attr3_leaf_flipflags(args); if (error) return error; /* * We must commit the flag value change now to make it atomic * and then we can start the next trans in series at REMOVE_OLD.
*/
attr->xattri_dela_state++; break;
case XFS_DAS_LEAF_REMOVE_OLD: case XFS_DAS_NODE_REMOVE_OLD: /* * If we have a remote attr, start the process of removing it * by invalidating any cached buffers. * * If we don't have a remote attr, we skip the remote block * removal state altogether with a second state increment.
*/
xfs_attr_restore_rmt_blk(args); if (args->rmtblkno) {
error = xfs_attr_rmtval_invalidate(args); if (error) return error;
} else {
attr->xattri_dela_state++;
}
attr->xattri_dela_state++; goto next_state;
case XFS_DAS_LEAF_REMOVE_RMT: case XFS_DAS_NODE_REMOVE_RMT:
error = xfs_attr_rmtval_remove(attr); if (error == -EAGAIN) {
error = 0; break;
} if (error) return error;
/* * We've finished removing the remote attr blocks, so commit the * transaction and move on to removing the attr name from the * leaf/node block. Removing the attr might require a full * transaction reservation for btree block freeing, so we * can't do that in the same transaction where we removed the * remote attr blocks.
*/
attr->xattri_dela_state++; break;
case XFS_DAS_LEAF_REMOVE_ATTR:
error = xfs_attr_leaf_remove_attr(attr);
attr->xattri_dela_state = xfs_attr_complete_op(attr,
xfs_attr_init_add_state(args)); break;
case XFS_DAS_NODE_REMOVE_ATTR:
error = xfs_attr_node_remove_attr(attr); if (!error)
error = xfs_attr_leaf_shrink(args);
attr->xattri_dela_state = xfs_attr_complete_op(attr,
xfs_attr_init_add_state(args)); break; default:
ASSERT(0); break;
}
/* * Make a change to the xattr structure. * * The caller must have initialized @args, attached dquots, and must not hold * any ILOCKs. Reserved data blocks may be used if @rsvd is set. * * Returns -EEXIST for XFS_ATTRUPDATE_CREATE if the name already exists. * Returns -ENOATTR for XFS_ATTRUPDATE_REMOVE if the name does not exist. * Returns 0 on success, or a negative errno if something else went wrong.
*/ int
xfs_attr_set( struct xfs_da_args *args, enum xfs_attr_update op, bool rsvd)
{ struct xfs_inode *dp = args->dp; struct xfs_mount *mp = dp->i_mount; struct xfs_trans_res tres; int error, local; int rmt_blks = 0; unsignedint total = 0;
ASSERT(!args->trans);
switch (op) { case XFS_ATTRUPDATE_UPSERT: case XFS_ATTRUPDATE_CREATE: case XFS_ATTRUPDATE_REPLACE:
XFS_STATS_INC(mp, xs_attr_set);
args->total = xfs_attr_calc_size(args, &local);
/* * If the inode doesn't have an attribute fork, add one. * (inode must not be locked when we call this routine)
*/ if (xfs_inode_has_attr_fork(dp) == 0) { int sf_size = sizeof(struct xfs_attr_sf_hdr) +
xfs_attr_sf_entsize_byname(args->namelen,
args->valuelen);
error = xfs_attr_add_fork(dp, sf_size, rsvd); if (error) return error;
}
if (!local)
rmt_blks = xfs_attr3_rmt_blocks(mp, args->valuelen);
tres = xfs_attr_set_resv(args);
total = args->total; break; case XFS_ATTRUPDATE_REMOVE:
XFS_STATS_INC(mp, xs_attr_remove);
rmt_blks = xfs_attr3_max_rmt_blocks(mp);
tres = M_RES(mp)->tr_attrrm;
total = XFS_ATTRRM_SPACE_RES(mp); break;
}
/* * Root fork attributes can use reserved data blocks for this * operation if necessary
*/
error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans); if (error) return error;
if (op != XFS_ATTRUPDATE_REMOVE || xfs_inode_hasattr(dp)) {
error = xfs_iext_count_extend(args->trans, dp, XFS_ATTR_FORK,
XFS_IEXT_ATTR_MANIP_CNT(rmt_blks)); if (error) goto out_trans_cancel;
}
error = xfs_attr_lookup(args); switch (error) { case -EEXIST: if (op == XFS_ATTRUPDATE_REMOVE) { /* if no value, we are performing a remove operation */
xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE); break;
}
/* Pure create fails if the attr already exists */ if (op == XFS_ATTRUPDATE_CREATE) goto out_trans_cancel;
xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE); break; case -ENOATTR: /* Can't remove what isn't there. */ if (op == XFS_ATTRUPDATE_REMOVE) goto out_trans_cancel;
/* Pure replace fails if no existing attr to replace. */ if (op == XFS_ATTRUPDATE_REPLACE) goto out_trans_cancel;
xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET); break; default: goto out_trans_cancel;
}
/* * If this is a synchronous mount, make sure that the * transaction goes to disk before returning to the user.
*/ if (xfs_has_wsync(mp))
xfs_trans_set_sync(args->trans);
/* * Commit the last in the sequence of transactions.
*/
xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
error = xfs_trans_commit(args->trans);
out_unlock:
xfs_iunlock(dp, XFS_ILOCK_EXCL);
args->trans = NULL; return error;
out_trans_cancel: if (args->trans)
xfs_trans_cancel(args->trans); goto out_unlock;
}
/*======================================================================== * External routines when attribute list is inside the inode
*========================================================================*/
int xfs_attr_sf_totsize(struct xfs_inode *dp)
{ struct xfs_attr_sf_hdr *sf = dp->i_af.if_data;
return be16_to_cpu(sf->totsize);
}
/* * Add a name to the shortform attribute list structure * This is the external routine.
*/ staticint
xfs_attr_shortform_addname( struct xfs_da_args *args)
{ int newsize, forkoff;
trace_xfs_attr_sf_addname(args);
if (xfs_attr_sf_findname(args)) { int error;
ASSERT(args->op_flags & XFS_DA_OP_REPLACE);
error = xfs_attr_sf_removename(args); if (error) return error;
/* * Since we have removed the old attr, clear XFS_DA_OP_REPLACE * so that the new attr doesn't fit in shortform format, the * leaf format add routine won't trip over the attr not being * around.
*/
args->op_flags &= ~XFS_DA_OP_REPLACE;
} else {
ASSERT(!(args->op_flags & XFS_DA_OP_REPLACE));
}
if (args->namelen >= XFS_ATTR_SF_ENTSIZE_MAX ||
args->valuelen >= XFS_ATTR_SF_ENTSIZE_MAX) return -ENOSPC;
/*======================================================================== * External routines when attribute list is one block
*========================================================================*/
/* * Return EEXIST if attr is found, or ENOATTR if not
*/ STATICint
xfs_attr_leaf_hasname( struct xfs_da_args *args, struct xfs_buf **bp)
{ int error = 0;
/* * Remove a name from the leaf attribute list structure * * This leaf block cannot have a "remote" value, we only call this routine * if bmap_one_block() says there is only one block (ie: no remote blks).
*/ STATICint
xfs_attr_leaf_removename( struct xfs_da_args *args)
{ struct xfs_inode *dp; struct xfs_buf *bp; int error, forkoff;
/* * If the result is small enough, shrink it all into the inode.
*/
forkoff = xfs_attr_shortform_allfit(bp, dp); if (forkoff) return xfs_attr3_leaf_to_shortform(bp, args, forkoff); /* bp is gone due to xfs_da_shrink_inode */
return 0;
}
/* * Look up a name in a leaf attribute list structure. * * This leaf block cannot have a "remote" value, we only call this routine * if bmap_one_block() says there is only one block (ie: no remote blks). * * Returns 0 on successful retrieval, otherwise an error.
*/ STATICint
xfs_attr_leaf_get(xfs_da_args_t *args)
{ struct xfs_buf *bp; int error;
/* Return EEXIST if attr is found, or ENOATTR if not. */ STATICint
xfs_attr_node_lookup( struct xfs_da_args *args, struct xfs_da_state *state)
{ int retval, error;
/* * Search to see if name exists, and get back a pointer to it.
*/
error = xfs_da3_node_lookup_int(state, &retval); if (error) return error;
return retval;
}
/*======================================================================== * External routines when attribute list size > geo->blksize
*========================================================================*/
/* * Search to see if name already exists, and get back a pointer * to where it should go.
*/
xfs_attr_item_init_da_state(attr);
error = xfs_attr_node_lookup(args, attr->xattri_da_state); switch (error) { case -ENOATTR: if (args->op_flags & XFS_DA_OP_REPLACE) goto error; break; case -EEXIST: if (!(args->op_flags & XFS_DA_OP_REPLACE)) goto error;
trace_xfs_attr_node_replace(args); /* * Save the existing remote attr state so that the current * values reflect the state of the new attribute we are about to * add, not the attribute we just found and will remove later.
*/
xfs_attr_save_rmt_blk(args); break; case 0: break; default: goto error;
}
/* * Add a name to a Btree-format attribute list. * * This will involve walking down the Btree, and may involve splitting leaf * nodes and even splitting intermediate nodes up to and including the root * node (a special case of an intermediate node). * * If the tree was still in single leaf format and needs to converted to * real node format return 1 and let the caller handle that.
*/ staticint
xfs_attr_node_try_addname( struct xfs_attr_intent *attr)
{ struct xfs_da_state *state = attr->xattri_da_state; struct xfs_da_state_blk *blk; int error = 0;
if (!xfs_attr3_leaf_add(blk->bp, state->args)) { if (state->path.active == 1) { /* * Its really a single leaf node, but it had * out-of-line values so it looked like it *might* * have been a b-tree. Let the caller deal with this.
*/
error = 1; goto out;
}
/* * Split as many Btree elements as required. * This code tracks the new and old attr's location * in the index/blkno/rmtblkno/rmtblkcnt fields and * in the index2/blkno2/rmtblkno2/rmtblkcnt2 fields.
*/
error = xfs_da3_split(state); if (error) goto out;
} else { /* * Addition succeeded, update Btree hashvals.
*/
xfs_da3_fixhashpath(state, &state->path);
}
/* * Remove the name and update the hashvals in the tree.
*/
blk = &state->path.blk[state->path.active-1];
ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
retval = xfs_attr3_leaf_remove(blk->bp, args);
xfs_da3_fixhashpath(state, &state->path);
/* * The attr we are removing has already been marked incomplete, so * we need to set the filter appropriately to re-find the "old" * attribute entry after any split ops.
*/
args->attr_filter |= XFS_ATTR_INCOMPLETE;
error = xfs_da3_node_lookup_int(state, &retval); if (error) goto out;
error = xfs_attr_node_removename(args, state);
/* * Check to see if the tree needs to be collapsed.
*/ if (retval && (state->path.active > 1)) {
error = xfs_da3_join(state); if (error) goto out;
}
retval = error = 0;
out:
xfs_da_state_free(state); if (error) return error; return retval;
}
/* * Retrieve the attribute data from a node attribute list. * * This routine gets called for any attribute fork that has more than one * block, ie: both true Btree attr lists and for single-leaf-blocks with * "remote" values taking up more blocks. * * Returns 0 on successful retrieval, otherwise an error.
*/ STATICint
xfs_attr_node_get( struct xfs_da_args *args)
{ struct xfs_da_state *state; struct xfs_da_state_blk *blk; int i; int error;
trace_xfs_attr_node_get(args);
/* * Search to see if name exists, and get back a pointer to it.
*/
state = xfs_da_state_alloc(args);
error = xfs_attr_node_lookup(args, state); if (error != -EEXIST) goto out_release;
/* * Get the value, local or "remote"
*/
blk = &state->path.blk[state->path.active - 1];
error = xfs_attr3_leaf_getvalue(blk->bp, args);
/* * If not in a transaction, we have to release all the buffers.
*/
out_release: for (i = 0; i < state->path.active; i++) {
xfs_trans_brelse(args->trans, state->path.blk[i].bp);
state->path.blk[i].bp = NULL;
}
xfs_da_state_free(state); return error;
}
/* Enforce that there is at most one namespace bit per attr. */ inlinebool xfs_attr_check_namespace(unsignedint attr_flags)
{ return hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) < 2;
}
/* Returns true if the attribute entry name is valid. */ bool
xfs_attr_namecheck( unsignedint attr_flags, constvoid *name,
size_t length)
{ /* Only one namespace bit allowed. */ if (!xfs_attr_check_namespace(attr_flags)) returnfalse;
/* * MAXNAMELEN includes the trailing null, but (name/length) leave it * out, so use >= for the length check.
*/ if (length >= MAXNAMELEN) returnfalse;
/* Parent pointers have their own validation. */ if (attr_flags & XFS_ATTR_PARENT) return xfs_parent_namecheck(attr_flags, name, length);
/* There shouldn't be any nulls here */ return !memchr(name, 0, length);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.