diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_attr.c')
| -rw-r--r-- | fs/xfs/libxfs/xfs_attr.c | 79 | 
1 files changed, 55 insertions, 24 deletions
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index d48fcf11cc35..510ca6974604 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -97,7 +97,10 @@ xfs_inode_hasattr(   * Overall external interface routines.   *========================================================================*/ -/* Retrieve an extended attribute and its value.  Must have ilock. */ +/* + * 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_inode	*ip, @@ -115,12 +118,28 @@ xfs_attr_get_ilocked(  		return xfs_attr_node_get(args);  } -/* Retrieve an extended attribute by name, and its value. */ +/* + * Retrieve an extended attribute by name, and its value if requested. + * + * If ATTR_KERNOVAL is set in @flags, 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 @valuelenp, + * + * If the attribute is found, but exceeds the size limit set by the caller in + * @valuelenp, return -ERANGE with the size of the attribute that was found in + * @valuelenp. + * + * If ATTR_ALLOC is set in @flags, allocate the buffer for the value after + * existence of the attribute has been determined. On success, return that + * buffer to the caller and leave them to free it. On failure, free any + * allocated buffer and ensure the buffer pointer returned to the caller is + * null. + */  int  xfs_attr_get(  	struct xfs_inode	*ip,  	const unsigned char	*name, -	unsigned char		*value, +	unsigned char		**value,  	int			*valuelenp,  	int			flags)  { @@ -128,6 +147,8 @@ xfs_attr_get(  	uint			lock_mode;  	int			error; +	ASSERT((flags & (ATTR_ALLOC | ATTR_KERNOVAL)) || *value); +  	XFS_STATS_INC(ip->i_mount, xs_attr_get);  	if (XFS_FORCED_SHUTDOWN(ip->i_mount)) @@ -137,17 +158,29 @@ xfs_attr_get(  	if (error)  		return error; -	args.value = value; -	args.valuelen = *valuelenp;  	/* Entirely possible to look up a name which doesn't exist */  	args.op_flags = XFS_DA_OP_OKNOENT; +	if (flags & ATTR_ALLOC) +		args.op_flags |= XFS_DA_OP_ALLOCVAL; +	else +		args.value = *value; +	args.valuelen = *valuelenp;  	lock_mode = xfs_ilock_attr_map_shared(ip);  	error = xfs_attr_get_ilocked(ip, &args);  	xfs_iunlock(ip, lock_mode); -  	*valuelenp = args.valuelen; -	return error == -EEXIST ? 0 : error; + +	/* on error, we have to clean up allocated value buffers */ +	if (error) { +		if (flags & ATTR_ALLOC) { +			kmem_free(args.value); +			*value = NULL; +		} +		return error; +	} +	*value = args.value; +	return 0;  }  /* @@ -768,6 +801,8 @@ xfs_attr_leaf_removename(   *   * 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.   */  STATIC int  xfs_attr_leaf_get(xfs_da_args_t *args) @@ -789,9 +824,6 @@ xfs_attr_leaf_get(xfs_da_args_t *args)  	}  	error = xfs_attr3_leaf_getvalue(bp, args);  	xfs_trans_brelse(args->trans, bp); -	if (!error && (args->rmtblkno > 0) && !(args->flags & ATTR_KERNOVAL)) { -		error = xfs_attr_rmtval_get(args); -	}  	return error;  } @@ -1268,11 +1300,13 @@ xfs_attr_refillstate(xfs_da_state_t *state)  }  /* - * Look up a filename in a node attribute list. + * 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.   */  STATIC int  xfs_attr_node_get(xfs_da_args_t *args) @@ -1294,24 +1328,21 @@ xfs_attr_node_get(xfs_da_args_t *args)  	error = xfs_da3_node_lookup_int(state, &retval);  	if (error) {  		retval = error; -	} else if (retval == -EEXIST) { -		blk = &state->path.blk[ state->path.active-1 ]; -		ASSERT(blk->bp != NULL); -		ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC); - -		/* -		 * Get the value, local or "remote" -		 */ -		retval = xfs_attr3_leaf_getvalue(blk->bp, args); -		if (!retval && (args->rmtblkno > 0) -		    && !(args->flags & ATTR_KERNOVAL)) { -			retval = xfs_attr_rmtval_get(args); -		} +		goto out_release;  	} +	if (retval != -EEXIST) +		goto out_release; + +	/* +	 * Get the value, local or "remote" +	 */ +	blk = &state->path.blk[state->path.active - 1]; +	retval = 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;  |