Commit 20855e4c authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-5.19-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:
 "This fixes some stalling problems and corrects the last of the
  problems (I hope) observed during testing of the new atomic xattr
  update feature.

   - Fix statfs blocking on background inode gc workers

   - Fix some broken inode lock assertion code

   - Fix xattr leaf buffer leaks when cancelling a deferred xattr update
     operation

   - Clean up xattr recovery to make it easier to understand.

   - Fix xattr leaf block verifiers tripping over empty blocks.

   - Remove complicated and error prone xattr leaf block bholding mess.

   - Fix a bug where an rt extent crossing EOF was treated as "posteof"
     blocks and cleaned unnecessarily.

   - Fix a UAF when log shutdown races with unmount"

* tag 'xfs-5.19-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: prevent a UAF when log IO errors race with unmount
  xfs: dont treat rt extents beyond EOF as eofblocks to be cleared
  xfs: don't hold xattr leaf buffers across transaction rolls
  xfs: empty xattr leaf header blocks are not corruption
  xfs: clean up the end of xfs_attri_item_recover
  xfs: always free xattri_leaf_bp when cancelling a deferred op
  xfs: use invalidate_lock to check the state of mmap_lock
  xfs: factor out the common lock flags assert
  xfs: introduce xfs_inodegc_push()
  xfs: bound maximum wait time for inodegc work
parents 69cb6c65 7561cea5
Loading
Loading
Loading
Loading
+9 −29
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
STATIC int xfs_attr_leaf_get(xfs_da_args_t *args);
STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
STATIC int xfs_attr_leaf_hasname(struct xfs_da_args *args, struct xfs_buf **bp);
STATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args, struct xfs_buf *bp);
STATIC int xfs_attr_leaf_try_add(struct xfs_da_args *args);

/*
 * Internal routines when attribute list is more than one block.
@@ -393,16 +393,10 @@ xfs_attr_sf_addname(
	 * 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, &attr->xattri_leaf_bp);
	error = xfs_attr_shortform_to_leaf(args);
	if (error)
		return error;

	/*
	 * Prevent the leaf buffer from being unlocked so that a concurrent AIL
	 * push cannot grab the half-baked leaf buffer and run into problems
	 * with the write verifier.
	 */
	xfs_trans_bhold(args->trans, attr->xattri_leaf_bp);
	attr->xattri_dela_state = XFS_DAS_LEAF_ADD;
out:
	trace_xfs_attr_sf_addname_return(attr->xattri_dela_state, args->dp);
@@ -447,11 +441,9 @@ xfs_attr_leaf_addname(

	/*
	 * Use the leaf buffer we may already hold locked as a result of
	 * a sf-to-leaf conversion. The held buffer is no longer valid
	 * after this call, regardless of the result.
	 * a sf-to-leaf conversion.
	 */
	error = xfs_attr_leaf_try_add(args, attr->xattri_leaf_bp);
	attr->xattri_leaf_bp = NULL;
	error = xfs_attr_leaf_try_add(args);

	if (error == -ENOSPC) {
		error = xfs_attr3_leaf_to_node(args);
@@ -497,8 +489,6 @@ xfs_attr_node_addname(
	struct xfs_da_args	*args = attr->xattri_da_args;
	int			error;

	ASSERT(!attr->xattri_leaf_bp);

	error = xfs_attr_node_addname_find_attr(attr);
	if (error)
		return error;
@@ -1215,24 +1205,14 @@ xfs_attr_restore_rmt_blk(
 */
STATIC int
xfs_attr_leaf_try_add(
	struct xfs_da_args	*args,
	struct xfs_buf		*bp)
	struct xfs_da_args	*args)
{
	struct xfs_buf		*bp;
	int			error;

	/*
	 * If the caller provided a buffer to us, it is locked and held in
	 * the transaction because it just did a shortform to leaf conversion.
	 * Hence we don't need to read it again. Otherwise read in the leaf
	 * buffer.
	 */
	if (bp) {
		xfs_trans_bhold_release(args->trans, bp);
	} else {
	error = xfs_attr3_leaf_read(args->trans, args->dp, 0, &bp);
	if (error)
		return error;
	}

	/*
	 * Look up the xattr name to set the insertion point for the new xattr.
+0 −5
Original line number Diff line number Diff line
@@ -515,11 +515,6 @@ struct xfs_attr_intent {
	 */
	struct xfs_attri_log_nameval	*xattri_nameval;

	/*
	 * Used by xfs_attr_set to hold a leaf buffer across a transaction roll
	 */
	struct xfs_buf			*xattri_leaf_bp;

	/* Used to keep track of current state of delayed operation */
	enum xfs_delattr_state		xattri_dela_state;

+19 −16
Original line number Diff line number Diff line
@@ -289,6 +289,23 @@ xfs_attr3_leaf_verify_entry(
	return NULL;
}

/*
 * Validate an attribute leaf block.
 *
 * Empty leaf blocks can occur under the following circumstances:
 *
 * 1. setxattr adds a new extended attribute to a file;
 * 2. The file has zero existing attributes;
 * 3. The attribute is too large to fit in the attribute fork;
 * 4. The attribute is small enough to fit in a leaf block;
 * 5. A log flush occurs after committing the transaction that creates
 *    the (empty) leaf block; and
 * 6. The filesystem goes down after the log flush but before the new
 *    attribute can be committed to the leaf block.
 *
 * Hence we need to ensure that we don't fail the validation purely
 * because the leaf is empty.
 */
static xfs_failaddr_t
xfs_attr3_leaf_verify(
	struct xfs_buf			*bp)
@@ -310,15 +327,6 @@ xfs_attr3_leaf_verify(
	if (fa)
		return fa;

	/*
	 * Empty leaf blocks should never occur;  they imply the existence of a
	 * software bug that needs fixing. xfs_repair also flags them as a
	 * corruption that needs fixing, so we should never let these go to
	 * disk.
	 */
	if (ichdr.count == 0)
		return __this_address;

	/*
	 * firstused is the block offset of the first name info structure.
	 * Make sure it doesn't go off the block or crash into the header.
@@ -922,14 +930,10 @@ xfs_attr_shortform_getvalue(
	return -ENOATTR;
}

/*
 * Convert from using the shortform to the leaf.  On success, return the
 * buffer so that we can keep it locked until we're totally done with it.
 */
/* Convert from using the shortform to the leaf format. */
int
xfs_attr_shortform_to_leaf(
	struct xfs_da_args		*args,
	struct xfs_buf			**leaf_bp)
	struct xfs_da_args		*args)
{
	struct xfs_inode		*dp;
	struct xfs_attr_shortform	*sf;
@@ -991,7 +995,6 @@ xfs_attr_shortform_to_leaf(
		sfe = xfs_attr_sf_nextentry(sfe);
	}
	error = 0;
	*leaf_bp = bp;
out:
	kmem_free(tmpbuffer);
	return error;
+1 −2
Original line number Diff line number Diff line
@@ -49,8 +49,7 @@ void xfs_attr_shortform_create(struct xfs_da_args *args);
void	xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
int	xfs_attr_shortform_lookup(struct xfs_da_args *args);
int	xfs_attr_shortform_getvalue(struct xfs_da_args *args);
int	xfs_attr_shortform_to_leaf(struct xfs_da_args *args,
			struct xfs_buf **leaf_bp);
int	xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
int	xfs_attr_sf_removename(struct xfs_da_args *args);
int	xfs_attr_sf_findname(struct xfs_da_args *args,
			     struct xfs_attr_sf_entry **sfep,
+15 −12
Original line number Diff line number Diff line
@@ -576,7 +576,7 @@ xfs_attri_item_recover(
	struct xfs_trans_res		tres;
	struct xfs_attri_log_format	*attrp;
	struct xfs_attri_log_nameval	*nv = attrip->attri_nameval;
	int				error, ret = 0;
	int				error;
	int				total;
	int				local;
	struct xfs_attrd_log_item	*done_item = NULL;
@@ -655,28 +655,31 @@ xfs_attri_item_recover(
	xfs_ilock(ip, XFS_ILOCK_EXCL);
	xfs_trans_ijoin(tp, ip, 0);

	ret = xfs_xattri_finish_update(attr, done_item);
	if (ret == -EAGAIN) {
		/* There's more work to do, so add it to this transaction */
	error = xfs_xattri_finish_update(attr, done_item);
	if (error == -EAGAIN) {
		/*
		 * There's more work to do, so add the intent item to this
		 * transaction so that we can continue it later.
		 */
		xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_ATTR, &attr->xattri_list);
	} else
		error = ret;
		error = xfs_defer_ops_capture_and_commit(tp, capture_list);
		if (error)
			goto out_unlock;

		xfs_iunlock(ip, XFS_ILOCK_EXCL);
		xfs_irele(ip);
		return 0;
	}
	if (error) {
		xfs_trans_cancel(tp);
		goto out_unlock;
	}

	error = xfs_defer_ops_capture_and_commit(tp, capture_list);

out_unlock:
	if (attr->xattri_leaf_bp)
		xfs_buf_relse(attr->xattri_leaf_bp);

	xfs_iunlock(ip, XFS_ILOCK_EXCL);
	xfs_irele(ip);
out:
	if (ret != -EAGAIN)
	xfs_attr_free_item(attr);
	return error;
}
Loading