All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/9] xfs-4.17: online scrub fixes
@ 2018-03-15  0:29 Darrick J. Wong
  2018-03-15  0:29 ` [PATCH 1/9] xfs: sanity-check the unused space before trying to use it Darrick J. Wong
                   ` (11 more replies)
  0 siblings, 12 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:29 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

Hi all,

This series refactors various libxfs and scrub code in preparation for
landing the online repair feature.

The first patch converts another directory function to return
EFSCORRUPTED to userspace instead of ASSERTing on bad on-disk metadata.
This fixes a crash seen in xfs/391.

The next three patches continue the refactoring of libxfs validator
functions that was started in 4.16.  This time we strengthen the extent
record checks to include out of bounds tests, which scrub/repair will
need to check ifork contents.  Patches 2-3 refactor the inode corruption
logging so that we can capture both the exact code check that triggered
the warning as well as dump the relevant metadata buffer for offline
analysis.

Patch 5 enhances the block mapping scrubber to take a third pass over
the mapping data to make sure that every rmap also has a corresponding
bmap.  This is only done for btree format forks because the complexity
involved in using a btree vastly increases the chances for problems.

Patch 6 removes the raw inode record checking that was introduced in the
original online scrub patches.  Not only was it causing some problems
with the way it was handling the inode cluster buffer, it's also
unnecessary -- the inode repair code knows how to fix (nearly) every
corruption that the iget paths look for, so we should move on to repair
asap.

Patch 7 cleans up helper function parameters that were made unnecessary
by the previous patch.

Patch 8 reclassifies inode cluster buffer read problems in the inode
btree scrubber as a cross referencing error instead of a straight
corruption.

Patch 9 moves the extent size hint validators into libxfs so that scrub
and repair can share the code.

If you're going to start using this mess, you probably ought to just
pull from my git trees.  The kernel patches[1] should apply against
4.16-rc5.  xfsprogs[2] and xfstests[3] can be found in their usual
places.  The git trees contain all four series' worth of changes.

This is an extraordinary way to eat your data.  Enjoy!
Comments and questions are, as always, welcome.

--D

[1] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=djwong-devel
[2] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=djwong-devel
[3] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfstests-dev.git/log/?h=djwong-devel

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
@ 2018-03-15  0:29 ` Darrick J. Wong
  2018-03-21 13:52   ` Brian Foster
                     ` (3 more replies)
  2018-03-15  0:29 ` [PATCH 2/9] xfs: refactor bmap record valiation Darrick J. Wong
                   ` (10 subsequent siblings)
  11 siblings, 4 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:29 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
it doesn't make sense.  Since a carefully crafted fuzzed image can cause
the kernel to crash after blowing a bunch of assertions, let's move
those checks into a validator function and rig everything up to return
EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
xfs/391.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_dir2.h       |    2 +
 fs/xfs/libxfs/xfs_dir2_block.c |   38 ++++++++++++-------
 fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
 fs/xfs/libxfs/xfs_dir2_leaf.c  |    6 +++
 fs/xfs/libxfs/xfs_dir2_node.c  |    4 ++
 5 files changed, 93 insertions(+), 35 deletions(-)


diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index 388d67c..989e95a 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
 extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
 		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
 		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
-extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
+extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
 		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
 		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
 		int *needlogp, int *needscanp);
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 2da86a3..5726402 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -454,12 +454,15 @@ xfs_dir2_block_addname(
 		/*
 		 * Mark the space needed for the new leaf entry, now in use.
 		 */
-		xfs_dir2_data_use_free(args, bp, enddup,
+		error = xfs_dir2_data_use_free(args, bp, enddup,
 			(xfs_dir2_data_aoff_t)
 			((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) -
 			 sizeof(*blp)),
 			(xfs_dir2_data_aoff_t)sizeof(*blp),
 			&needlog, &needscan);
+		if (error)
+			return error;
+
 		/*
 		 * Update the tail (entry count).
 		 */
@@ -541,9 +544,11 @@ xfs_dir2_block_addname(
 	/*
 	 * Mark space for the data entry used.
 	 */
-	xfs_dir2_data_use_free(args, bp, dup,
+	error = xfs_dir2_data_use_free(args, bp, dup,
 		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
 		(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Create the new data entry.
 	 */
@@ -997,8 +1002,10 @@ xfs_dir2_leaf_to_block(
 	/*
 	 * Use up the space at the end of the block (blp/btp).
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup, args->geo->blksize - size, size,
-		&needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, dbp, dup,
+		args->geo->blksize - size, size, &needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Initialize the block tail.
 	 */
@@ -1110,18 +1117,14 @@ xfs_dir2_sf_to_block(
 	 * Add block 0 to the inode.
 	 */
 	error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
-	if (error) {
-		kmem_free(sfp);
-		return error;
-	}
+	if (error)
+		goto out_free;
 	/*
 	 * Initialize the data block, then convert it to block format.
 	 */
 	error = xfs_dir3_data_init(args, blkno, &bp);
-	if (error) {
-		kmem_free(sfp);
-		return error;
-	}
+	if (error)
+		goto out_free;
 	xfs_dir3_block_init(mp, tp, bp, dp);
 	hdr = bp->b_addr;
 
@@ -1136,8 +1139,10 @@ xfs_dir2_sf_to_block(
 	 */
 	dup = dp->d_ops->data_unused_p(hdr);
 	needlog = needscan = 0;
-	xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
+	error = xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
 			       i, &needlog, &needscan);
+	if (error)
+		goto out_free;
 	ASSERT(needscan == 0);
 	/*
 	 * Fill in the tail.
@@ -1150,9 +1155,11 @@ xfs_dir2_sf_to_block(
 	/*
 	 * Remove the freespace, we'll manage it.
 	 */
-	xfs_dir2_data_use_free(args, bp, dup,
+	error = xfs_dir2_data_use_free(args, bp, dup,
 		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
 		be16_to_cpu(dup->length), &needlog, &needscan);
+	if (error)
+		goto out_free;
 	/*
 	 * Create entry for .
 	 */
@@ -1256,4 +1263,7 @@ xfs_dir2_sf_to_block(
 	xfs_dir2_block_log_tail(tp, bp);
 	xfs_dir3_data_check(dp, bp);
 	return 0;
+out_free:
+	kmem_free(sfp);
+	return error;
 }
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index 9202794..167503d 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -932,10 +932,51 @@ xfs_dir2_data_make_free(
 	*needscanp = needscan;
 }
 
+/* Check our free data for obvious signs of corruption. */
+static inline xfs_failaddr_t
+xfs_dir2_data_check_free(
+	struct xfs_dir2_data_hdr	*hdr,
+	struct xfs_dir2_data_unused	*dup,
+	xfs_dir2_data_aoff_t		offset,
+	xfs_dir2_data_aoff_t		len)
+{
+	if (hdr->magic != cpu_to_be32(XFS_DIR2_DATA_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
+		return __this_address;
+	if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG)
+		return __this_address;
+	if (offset < (char *)dup - (char *)hdr)
+		return __this_address;
+	if (offset + len > (char *)dup + be16_to_cpu(dup->length) - (char *)hdr)
+		return __this_address;
+	if ((char *)dup - (char *)hdr !=
+	    be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
+		return __this_address;
+	return NULL;
+}
+
+/* Sanity-check a new bestfree entry. */
+static inline xfs_failaddr_t
+xfs_dir2_data_check_new_free(
+	struct xfs_dir2_data_hdr	*hdr,
+	struct xfs_dir2_data_free	*dfp,
+	struct xfs_dir2_data_unused	*newdup)
+{
+	if (dfp == NULL)
+		return __this_address;
+	if (dfp->length != newdup->length)
+		return __this_address;
+	if (be16_to_cpu(dfp->offset) != (char *)newdup - (char *)hdr)
+		return __this_address;
+	return NULL;
+}
+
 /*
  * Take a byte range out of an existing unused space and make it un-free.
  */
-void
+int
 xfs_dir2_data_use_free(
 	struct xfs_da_args	*args,
 	struct xfs_buf		*bp,
@@ -947,23 +988,19 @@ xfs_dir2_data_use_free(
 {
 	xfs_dir2_data_hdr_t	*hdr;		/* data block header */
 	xfs_dir2_data_free_t	*dfp;		/* bestfree pointer */
+	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
+	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
+	struct xfs_dir2_data_free *bf;
+	xfs_failaddr_t		fa;
 	int			matchback;	/* matches end of freespace */
 	int			matchfront;	/* matches start of freespace */
 	int			needscan;	/* need to regen bestfree */
-	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
-	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
 	int			oldlen;		/* old unused entry's length */
-	struct xfs_dir2_data_free *bf;
 
 	hdr = bp->b_addr;
-	ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
-	ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
-	ASSERT(offset >= (char *)dup - (char *)hdr);
-	ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)hdr);
-	ASSERT((char *)dup - (char *)hdr == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
+	fa = xfs_dir2_data_check_free(hdr, dup, offset, len);
+	if (fa)
+		goto corrupt;
 	/*
 	 * Look up the entry in the bestfree table.
 	 */
@@ -1008,9 +1045,9 @@ xfs_dir2_data_use_free(
 			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
 			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
 						       needlogp);
-			ASSERT(dfp != NULL);
-			ASSERT(dfp->length == newdup->length);
-			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
+			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
+			if (fa)
+				goto corrupt;
 			/*
 			 * If we got inserted at the last slot,
 			 * that means we don't know if there was a better
@@ -1036,9 +1073,9 @@ xfs_dir2_data_use_free(
 			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
 			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
 						       needlogp);
-			ASSERT(dfp != NULL);
-			ASSERT(dfp->length == newdup->length);
-			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
+			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
+			if (fa)
+				goto corrupt;
 			/*
 			 * If we got inserted at the last slot,
 			 * that means we don't know if there was a better
@@ -1084,6 +1121,11 @@ xfs_dir2_data_use_free(
 		}
 	}
 	*needscanp = needscan;
+	return 0;
+corrupt:
+	xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
+			hdr, __FILE__, __LINE__, fa);
+	return -EFSCORRUPTED;
 }
 
 /* Find the end of the entry data in a data/block format dir block. */
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index d61d52d..f746921 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -877,9 +877,13 @@ xfs_dir2_leaf_addname(
 	/*
 	 * Mark the initial part of our freespace in use for the new entry.
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup,
+	error = xfs_dir2_data_use_free(args, dbp, dup,
 		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
 		&needlog, &needscan);
+	if (error) {
+		xfs_trans_brelse(tp, lbp);
+		return error;
+	}
 	/*
 	 * Initialize our new entry (at last).
 	 */
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 0839ffe..641b352 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -2023,9 +2023,11 @@ xfs_dir2_node_addname_int(
 	/*
 	 * Mark the first part of the unused space, inuse for us.
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup,
+	error = xfs_dir2_data_use_free(args, dbp, dup,
 		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
 		&needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Fill in the new entry and log it.
 	 */


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 2/9] xfs: refactor bmap record valiation
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
  2018-03-15  0:29 ` [PATCH 1/9] xfs: sanity-check the unused space before trying to use it Darrick J. Wong
@ 2018-03-15  0:29 ` Darrick J. Wong
  2018-03-21 13:55   ` Brian Foster
  2018-03-22  6:01   ` [PATCH v2 " Darrick J. Wong
  2018-03-15  0:29 ` [PATCH 3/9] xfs: refactor inode verifier error logging Darrick J. Wong
                   ` (9 subsequent siblings)
  11 siblings, 2 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:29 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Refactor the bmap validator into a more complete helper that looks for
extents that run off the end of the device, overflow into the next AG,
or have invalid flag states, leaving a raw helper so that the inode
repair can check things even if iget fails.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_bmap.c       |   55 ++++++++++++++++++++++++++++++++++++++--
 fs/xfs/libxfs/xfs_bmap.h       |    5 ++++
 fs/xfs/libxfs/xfs_bmap_btree.h |   14 ----------
 fs/xfs/libxfs/xfs_inode_fork.c |   12 ++++++---
 4 files changed, 65 insertions(+), 21 deletions(-)


diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index daae00e..5850e76 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1261,11 +1261,15 @@ xfs_iread_extents(
 		 */
 		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
 		for (j = 0; j < num_recs; j++, frp++, i++) {
+			xfs_failaddr_t	fa;
+
 			xfs_bmbt_disk_get_all(frp, &new);
-			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
-				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
-						 XFS_ERRLEVEL_LOW, mp);
+			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
+			if (fa) {
 				error = -EFSCORRUPTED;
+				xfs_inode_verifier_error(ip, error,
+						"xfs_bmap_read_extents(2)",
+						frp, sizeof(*frp), fa);
 				goto out_brelse;
 			}
 			xfs_iext_insert(ip, &icur, &new, state);
@@ -6154,3 +6158,48 @@ xfs_bmap_finish_one(
 
 	return error;
 }
+
+/* Check that an extent does not have invalid flags or bad ranges. */
+xfs_failaddr_t
+xfs_bmbt_validate_extent(
+	struct xfs_mount	*mp,
+	bool			isrt,
+	int			whichfork,
+	struct xfs_bmbt_irec	*irec)
+{
+	xfs_fsblock_t		endfsb;
+
+	endfsb = irec->br_startblock + irec->br_blockcount - 1;
+	if (isrt) {
+		if (!xfs_verify_rtbno(mp, irec->br_startblock))
+			return __this_address;
+		if (!xfs_verify_rtbno(mp, endfsb))
+			return __this_address;
+	} else {
+		if (!xfs_verify_fsbno(mp, irec->br_startblock))
+			return __this_address;
+		if (!xfs_verify_fsbno(mp, endfsb))
+			return __this_address;
+		if (XFS_FSB_TO_AGNO(mp, irec->br_startblock) !=
+		    XFS_FSB_TO_AGNO(mp, endfsb))
+			return __this_address;
+	}
+	if (irec->br_state != XFS_EXT_NORM) {
+		if (whichfork != XFS_DATA_FORK)
+			return __this_address;
+		if (!xfs_sb_version_hasextflgbit(&mp->m_sb))
+			return __this_address;
+	}
+	return NULL;
+}
+
+/* Check that an inode's extent does not have invalid flags or bad ranges. */
+xfs_failaddr_t
+xfs_bmap_validate_extent(
+	struct xfs_inode	*ip,
+	int			whichfork,
+	struct xfs_bmbt_irec	*irec)
+{
+	return xfs_bmbt_validate_extent(ip->i_mount, XFS_IS_REALTIME_INODE(ip),
+			whichfork, irec);
+}
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index e36d757..e0fef89 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -274,4 +274,9 @@ static inline int xfs_bmap_fork_to_state(int whichfork)
 	}
 }
 
+xfs_failaddr_t xfs_bmbt_validate_extent(struct xfs_mount *mp, bool isrt,
+		int whichfork, struct xfs_bmbt_irec *irec);
+xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork,
+		struct xfs_bmbt_irec *irec);
+
 #endif	/* __XFS_BMAP_H__ */
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
index 135b8c5..e450574 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.h
+++ b/fs/xfs/libxfs/xfs_bmap_btree.h
@@ -118,18 +118,4 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
 extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
 		struct xfs_trans *, struct xfs_inode *, int);
 
-/*
- * Check that the extent does not contain an invalid unwritten extent flag.
- */
-static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
-		struct xfs_bmbt_irec *irec)
-{
-	if (irec->br_state == XFS_EXT_NORM)
-		return true;
-	if (whichfork == XFS_DATA_FORK &&
-	    xfs_sb_version_hasextflgbit(&mp->m_sb))
-		return true;
-	return false;
-}
-
 #endif	/* __XFS_BMAP_BTREE_H__ */
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 866d2861..613fba2 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -245,10 +245,14 @@ xfs_iformat_extents(
 
 		xfs_iext_first(ifp, &icur);
 		for (i = 0; i < nex; i++, dp++) {
+			xfs_failaddr_t	fa;
+
 			xfs_bmbt_disk_get_all(dp, &new);
-			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
-				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
-						 XFS_ERRLEVEL_LOW, mp);
+			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
+			if (fa) {
+				xfs_inode_verifier_error(ip, -EFSCORRUPTED,
+						"xfs_iformat_extents(2)",
+						dp, sizeof(*dp), fa);
 				return -EFSCORRUPTED;
 			}
 
@@ -595,7 +599,7 @@ xfs_iextents_copy(
 	for_each_xfs_iext(ifp, &icur, &rec) {
 		if (isnullstartblock(rec.br_startblock))
 			continue;
-		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, &rec));
+		ASSERT(xfs_bmap_validate_extent(ip, whichfork, &rec) == NULL);
 		xfs_bmbt_disk_set_all(dp, &rec);
 		trace_xfs_write_extent(ip, &icur, state, _RET_IP_);
 		copied += sizeof(struct xfs_bmbt_rec);


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 3/9] xfs: refactor inode verifier error logging
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
  2018-03-15  0:29 ` [PATCH 1/9] xfs: sanity-check the unused space before trying to use it Darrick J. Wong
  2018-03-15  0:29 ` [PATCH 2/9] xfs: refactor bmap record valiation Darrick J. Wong
@ 2018-03-15  0:29 ` Darrick J. Wong
  2018-03-21 13:55   ` Brian Foster
  2018-03-15  0:29 ` [PATCH 4/9] xfs: refactor inode buffer " Darrick J. Wong
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:29 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Refactor some of the inode verifier failure logging call sites to use
the new xfs_inode_verifier_error method which dumps the offending buffer
as well as the code location of the failed check.  This trims the
output, makes it clearer to the admin that repair must be run, and gives
the developers more details to work from.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_bmap.c       |    5 +++--
 fs/xfs/libxfs/xfs_inode_fork.c |   15 +++++++++------
 2 files changed, 12 insertions(+), 8 deletions(-)


diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 5850e76..fe7534e 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1244,8 +1244,9 @@ xfs_iread_extents(
 			xfs_warn(ip->i_mount,
 				"corrupt dinode %Lu, (btree extents).",
 				(unsigned long long) ip->i_ino);
-			XFS_CORRUPTION_ERROR(__func__,
-				XFS_ERRLEVEL_LOW, ip->i_mount, block);
+			xfs_inode_verifier_error(ip, -EFSCORRUPTED,
+					__func__, block, sizeof(*block),
+					__this_address);
 			error = -EFSCORRUPTED;
 			goto out_brelse;
 		}
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 613fba2..701c42a 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -195,8 +195,9 @@ xfs_iformat_local(
 	"corrupt inode %Lu (bad size %d for local fork, size = %d).",
 			(unsigned long long) ip->i_ino, size,
 			XFS_DFORK_SIZE(dip, ip->i_mount, whichfork));
-		XFS_CORRUPTION_ERROR("xfs_iformat_local", XFS_ERRLEVEL_LOW,
-				     ip->i_mount, dip);
+		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
+				"xfs_iformat_local", dip, sizeof(*dip),
+				__this_address);
 		return -EFSCORRUPTED;
 	}
 
@@ -231,8 +232,9 @@ xfs_iformat_extents(
 	if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, mp, whichfork))) {
 		xfs_warn(ip->i_mount, "corrupt inode %Lu ((a)extents = %d).",
 			(unsigned long long) ip->i_ino, nex);
-		XFS_CORRUPTION_ERROR("xfs_iformat_extents(1)", XFS_ERRLEVEL_LOW,
-				     mp, dip);
+		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
+				"xfs_iformat_extents(1)", dip, sizeof(*dip),
+				__this_address);
 		return -EFSCORRUPTED;
 	}
 
@@ -309,8 +311,9 @@ xfs_iformat_btree(
 		     level == 0 || level > XFS_BTREE_MAXLEVELS) {
 		xfs_warn(mp, "corrupt inode %Lu (btree).",
 					(unsigned long long) ip->i_ino);
-		XFS_CORRUPTION_ERROR("xfs_iformat_btree", XFS_ERRLEVEL_LOW,
-					 mp, dip);
+		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
+				"xfs_iformat_btree", dfp, size,
+				__this_address);
 		return -EFSCORRUPTED;
 	}
 


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 4/9] xfs: refactor inode buffer verifier error logging
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (2 preceding siblings ...)
  2018-03-15  0:29 ` [PATCH 3/9] xfs: refactor inode verifier error logging Darrick J. Wong
@ 2018-03-15  0:29 ` Darrick J. Wong
  2018-03-21 13:55   ` Brian Foster
  2018-04-24 19:51   ` Eric Sandeen
  2018-03-15  0:30 ` [PATCH 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files Darrick J. Wong
                   ` (7 subsequent siblings)
  11 siblings, 2 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:29 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

When the inode buffer verifier encounters an error, it's much more
helpful to print a buffer from the offending inode instead of just the
start of the inode chunk buffer.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_inode_buf.c |    4 +++-
 fs/xfs/xfs_error.c            |   29 ++++++++++++++++++++++++-----
 fs/xfs/xfs_error.h            |    3 +++
 3 files changed, 30 insertions(+), 6 deletions(-)


diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 4fe17b3..51019e5 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -115,13 +115,15 @@ xfs_inode_buf_verify(
 				return;
 			}
 
-			xfs_verifier_error(bp, -EFSCORRUPTED, __this_address);
 #ifdef DEBUG
 			xfs_alert(mp,
 				"bad inode magic/vsn daddr %lld #%d (magic=%x)",
 				(unsigned long long)bp->b_bn, i,
 				be16_to_cpu(dip->di_magic));
 #endif
+			xfs_buf_verifier_error(bp, -EFSCORRUPTED,
+					__func__, dip, sizeof(*dip),
+					NULL);
 		}
 	}
 	xfs_inobp_check(mp, bp);
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
index ccf520f..a63f508 100644
--- a/fs/xfs/xfs_error.c
+++ b/fs/xfs/xfs_error.c
@@ -347,27 +347,32 @@ xfs_corruption_error(
  * values, and omit the stack trace unless the error level is tuned high.
  */
 void
-xfs_verifier_error(
+xfs_buf_verifier_error(
 	struct xfs_buf		*bp,
 	int			error,
+	const char		*name,
+	void			*buf,
+	size_t			bufsz,
 	xfs_failaddr_t		failaddr)
 {
 	struct xfs_mount	*mp = bp->b_target->bt_mount;
 	xfs_failaddr_t		fa;
+	int			sz;
 
 	fa = failaddr ? failaddr : __return_address;
 	__xfs_buf_ioerror(bp, error, fa);
 
-	xfs_alert(mp, "Metadata %s detected at %pS, %s block 0x%llx",
+	xfs_alert(mp, "Metadata %s detected at %pS, %s block 0x%llx %s",
 		  bp->b_error == -EFSBADCRC ? "CRC error" : "corruption",
-		  fa, bp->b_ops->name, bp->b_bn);
+		  fa, bp->b_ops->name, bp->b_bn, name);
 
 	xfs_alert(mp, "Unmount and run xfs_repair");
 
 	if (xfs_error_level >= XFS_ERRLEVEL_LOW) {
+		sz = min_t(size_t, XFS_CORRUPTION_DUMP_LEN, bufsz);
 		xfs_alert(mp, "First %d bytes of corrupted metadata buffer:",
-				XFS_CORRUPTION_DUMP_LEN);
-		xfs_hex_dump(xfs_buf_offset(bp, 0), XFS_CORRUPTION_DUMP_LEN);
+				sz);
+		xfs_hex_dump(buf, sz);
 	}
 
 	if (xfs_error_level >= XFS_ERRLEVEL_HIGH)
@@ -375,6 +380,20 @@ xfs_verifier_error(
 }
 
 /*
+ * Warnings specifically for verifier errors.  Differentiate CRC vs. invalid
+ * values, and omit the stack trace unless the error level is tuned high.
+ */
+void
+xfs_verifier_error(
+	struct xfs_buf		*bp,
+	int			error,
+	xfs_failaddr_t		failaddr)
+{
+	return xfs_buf_verifier_error(bp, error, "", xfs_buf_offset(bp, 0),
+			XFS_CORRUPTION_DUMP_LEN, failaddr);
+}
+
+/*
  * Warnings for inode corruption problems.  Don't bother with the stack
  * trace unless the error level is turned up high.
  */
diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h
index 7e728c5..ce39134 100644
--- a/fs/xfs/xfs_error.h
+++ b/fs/xfs/xfs_error.h
@@ -26,6 +26,9 @@ extern void xfs_error_report(const char *tag, int level, struct xfs_mount *mp,
 extern void xfs_corruption_error(const char *tag, int level,
 			struct xfs_mount *mp, void *p, const char *filename,
 			int linenum, xfs_failaddr_t failaddr);
+extern void xfs_buf_verifier_error(struct xfs_buf *bp, int error,
+			const char *name, void *buf, size_t bufsz,
+			xfs_failaddr_t failaddr);
 extern void xfs_verifier_error(struct xfs_buf *bp, int error,
 			xfs_failaddr_t failaddr);
 extern void xfs_inode_verifier_error(struct xfs_inode *ip, int error,


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (3 preceding siblings ...)
  2018-03-15  0:29 ` [PATCH 4/9] xfs: refactor inode buffer " Darrick J. Wong
@ 2018-03-15  0:30 ` Darrick J. Wong
  2018-03-21 17:42   ` Brian Foster
  2018-03-22  6:02   ` [PATCH v2 " Darrick J. Wong
  2018-03-15  0:30 ` [PATCH 6/9] xfs: inode scrubber shouldn't bother with raw checks Darrick J. Wong
                   ` (6 subsequent siblings)
  11 siblings, 2 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:30 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

When we're scanning an extent mapping inode fork, ensure that every rmap
record for this ifork has a corresponding bmbt record too.  This
(mostly) provides the ability to cross-reference rmap records with bmap
data.  The rmap scrubber cannot do the xref on its own because that
requires taking an ilock with the agf lock held, which violates our
locking order rules (inode, then agf).

Note that we only do this for forks that are in btree format due to the
increased complexity; or forks that should have data but suspiciously
have zero extents because the inode could have just had its iforks
zapped by the inode repair code and now we need to reclaim the old
extents.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/bmap.c |  164 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 163 insertions(+), 1 deletion(-)


diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index d002821..2f38add 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -37,6 +37,7 @@
 #include "xfs_bmap_util.h"
 #include "xfs_bmap_btree.h"
 #include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
 #include "xfs_refcount.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
@@ -423,6 +424,163 @@ xfs_scrub_bmap_btree(
 	return error;
 }
 
+struct xfs_scrub_bmap_check_rmap_info {
+	struct xfs_scrub_context	*sc;
+	int				whichfork;
+	struct xfs_iext_cursor		icur;
+};
+
+/* Can we find bmaps that fit this rmap? */
+STATIC int
+xfs_scrub_bmap_check_rmap(
+	struct xfs_btree_cur		*cur,
+	struct xfs_rmap_irec		*rec,
+	void				*priv)
+{
+	struct xfs_bmbt_irec		irec;
+	struct xfs_scrub_bmap_check_rmap_info	*sbcri = priv;
+	struct xfs_ifork		*ifp;
+	struct xfs_scrub_context	*sc = sbcri->sc;
+	bool				have_map;
+
+	/* Is this even the right fork? */
+	if (rec->rm_owner != sc->ip->i_ino)
+		return 0;
+	if ((sbcri->whichfork == XFS_ATTR_FORK) ^
+	    !!(rec->rm_flags & XFS_RMAP_ATTR_FORK))
+		return 0;
+	if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
+		return 0;
+
+	/* Now look up the bmbt record. */
+	ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork);
+	if (!ifp) {
+		xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+				rec->rm_offset);
+		goto out;
+	}
+	have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
+			&sbcri->icur, &irec);
+	if (!have_map)
+		xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+				rec->rm_offset);
+
+	while (have_map) {
+		if (irec.br_startoff != rec->rm_offset)
+			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+					rec->rm_offset);
+		if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp,
+				cur->bc_private.a.agno, rec->rm_startblock))
+			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+					rec->rm_offset);
+		if (irec.br_blockcount > rec->rm_blockcount)
+			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+					rec->rm_offset);
+		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+			break;
+		rec->rm_startblock += irec.br_blockcount;
+		rec->rm_offset += irec.br_blockcount;
+		rec->rm_blockcount -= irec.br_blockcount;
+		if (rec->rm_blockcount == 0)
+			break;
+		have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec);
+		if (!have_map)
+			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+					rec->rm_offset);
+	}
+
+out:
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return XFS_BTREE_QUERY_RANGE_ABORT;
+	return 0;
+}
+
+/* Make sure each rmap has a corresponding bmbt entry. */
+STATIC int
+xfs_scrub_bmap_check_ag_rmaps(
+	struct xfs_scrub_context	*sc,
+	int				whichfork,
+	xfs_agnumber_t			agno)
+{
+	struct xfs_scrub_bmap_check_rmap_info	sbcri;
+	struct xfs_btree_cur		*cur;
+	struct xfs_buf			*agf;
+	int				error;
+
+	error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
+	if (error)
+		return error;
+
+	cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
+	if (!cur) {
+		error = -ENOMEM;
+		goto out_agf;
+	}
+
+	sbcri.sc = sc;
+	sbcri.whichfork = whichfork;
+	error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri);
+	if (error == XFS_BTREE_QUERY_RANGE_ABORT)
+		error = 0;
+
+	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+out_agf:
+	xfs_trans_brelse(sc->tp, agf);
+	return error;
+}
+
+/* Make sure each rmap has a corresponding bmbt entry. */
+STATIC int
+xfs_scrub_bmap_check_rmaps(
+	struct xfs_scrub_context	*sc,
+	int				whichfork)
+{
+	loff_t				size;
+	xfs_agnumber_t			agno;
+	int				error;
+
+	if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
+	    whichfork == XFS_COW_FORK ||
+	    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
+		return 0;
+
+	/* Don't support realtime rmap checks yet. */
+	if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
+		return 0;
+
+	/*
+	 * Only do this for complex maps that are in btree format, or for
+	 * situations where we would seem to have a size but zero extents.
+	 * The inode repair code can zap broken iforks, which means we have
+	 * to flag this bmap as corrupt if there are rmaps that need to be
+	 * reattached.
+	 */
+	switch (whichfork) {
+	case XFS_DATA_FORK:
+		size = i_size_read(VFS_I(sc->ip));
+		break;
+	case XFS_ATTR_FORK:
+		size = XFS_IFORK_Q(sc->ip);
+		break;
+	default:
+		size = 0;
+		break;
+	}
+	if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
+	    (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
+		return 0;
+
+	for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
+		error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno);
+		if (error)
+			return error;
+		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+			break;
+	}
+
+	return 0;
+}
+
 /*
  * Scrub an inode fork's block mappings.
  *
@@ -463,7 +621,7 @@ xfs_scrub_bmap(
 		break;
 	case XFS_ATTR_FORK:
 		if (!ifp)
-			goto out;
+			goto out_check_rmap;
 		if (!xfs_sb_version_hasattr(&mp->m_sb) &&
 		    !xfs_sb_version_hasattr2(&mp->m_sb))
 			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
@@ -534,6 +692,10 @@ xfs_scrub_bmap(
 			goto out;
 	}
 
+out_check_rmap:
+	error = xfs_scrub_bmap_check_rmaps(sc, whichfork);
+	if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error))
+		goto out;
 out:
 	return error;
 }


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 6/9] xfs: inode scrubber shouldn't bother with raw checks
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (4 preceding siblings ...)
  2018-03-15  0:30 ` [PATCH 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files Darrick J. Wong
@ 2018-03-15  0:30 ` Darrick J. Wong
  2018-03-21 17:42   ` Brian Foster
  2018-03-15  0:30 ` [PATCH 7/9] xfs: remove xfs_buf parameter from inode scrub methods Darrick J. Wong
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:30 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

The inode scrubber tries to _iget the inode prior to running checks.
If that _iget call fails with corruption errors that's an automatic
fail, regardless of whether it was the inode buffer read verifier,
the ifork verifier, or the ifork formatter that errored out.

Therefore, get rid of the raw mode scrub code because it's not needed.
Found by trying to fix some test failures in xfs/379 and xfs/415.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/inode.c |   98 +++++---------------------------------------------
 1 file changed, 9 insertions(+), 89 deletions(-)


diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 21297be..0332a01 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -515,72 +515,6 @@ xfs_scrub_dinode(
 	}
 }
 
-/* Map and read a raw inode. */
-STATIC int
-xfs_scrub_inode_map_raw(
-	struct xfs_scrub_context	*sc,
-	xfs_ino_t			ino,
-	struct xfs_buf			**bpp,
-	struct xfs_dinode		**dipp)
-{
-	struct xfs_imap			imap;
-	struct xfs_mount		*mp = sc->mp;
-	struct xfs_buf			*bp = NULL;
-	struct xfs_dinode		*dip;
-	int				error;
-
-	error = xfs_imap(mp, sc->tp, ino, &imap, XFS_IGET_UNTRUSTED);
-	if (error == -EINVAL) {
-		/*
-		 * Inode could have gotten deleted out from under us;
-		 * just forget about it.
-		 */
-		error = -ENOENT;
-		goto out;
-	}
-	if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
-			XFS_INO_TO_AGBNO(mp, ino), &error))
-		goto out;
-
-	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
-			imap.im_blkno, imap.im_len, XBF_UNMAPPED, &bp,
-			NULL);
-	if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
-			XFS_INO_TO_AGBNO(mp, ino), &error))
-		goto out;
-
-	/*
-	 * Is this really an inode?  We disabled verifiers in the above
-	 * xfs_trans_read_buf call because the inode buffer verifier
-	 * fails on /any/ inode record in the inode cluster with a bad
-	 * magic or version number, not just the one that we're
-	 * checking.  Therefore, grab the buffer unconditionally, attach
-	 * the inode verifiers by hand, and run the inode verifier only
-	 * on the one inode we want.
-	 */
-	bp->b_ops = &xfs_inode_buf_ops;
-	dip = xfs_buf_offset(bp, imap.im_boffset);
-	if (xfs_dinode_verify(mp, ino, dip) != NULL ||
-	    !xfs_dinode_good_version(mp, dip->di_version)) {
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
-		goto out_buf;
-	}
-
-	/* ...and is it the one we asked for? */
-	if (be32_to_cpu(dip->di_gen) != sc->sm->sm_gen) {
-		error = -ENOENT;
-		goto out_buf;
-	}
-
-	*dipp = dip;
-	*bpp = bp;
-out:
-	return error;
-out_buf:
-	xfs_trans_brelse(sc->tp, bp);
-	return error;
-}
-
 /*
  * Make sure the finobt doesn't think this inode is free.
  * We don't have to check the inobt ourselves because we got the inode via
@@ -727,43 +661,29 @@ xfs_scrub_inode(
 	struct xfs_scrub_context	*sc)
 {
 	struct xfs_dinode		di;
-	struct xfs_buf			*bp = NULL;
-	struct xfs_dinode		*dip;
-	xfs_ino_t			ino;
 	int				error = 0;
 
-	/* Did we get the in-core inode, or are we doing this manually? */
-	if (sc->ip) {
-		ino = sc->ip->i_ino;
-		xfs_inode_to_disk(sc->ip, &di, 0);
-		dip = &di;
-	} else {
-		/* Map & read inode. */
-		ino = sc->sm->sm_ino;
-		error = xfs_scrub_inode_map_raw(sc, ino, &bp, &dip);
-		if (error || !bp)
-			goto out;
+	/* iget failed means automatic fail. */
+	if (!sc->ip) {
+		xfs_scrub_ino_set_corrupt(sc, sc->sm->sm_ino, NULL);
+		return 0;
 	}
 
-	xfs_scrub_dinode(sc, bp, dip, ino);
+	/* Scrub the inode core. */
+	xfs_inode_to_disk(sc->ip, &di, 0);
+	xfs_scrub_dinode(sc, NULL, &di, sc->ip->i_ino);
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		goto out;
 
-	/* Now let's do the things that require a live inode. */
-	if (!sc->ip)
-		goto out;
-
 	/*
 	 * Look for discrepancies between file's data blocks and the reflink
 	 * iflag.  We already checked the iflag against the file mode when
 	 * we scrubbed the dinode.
 	 */
 	if (S_ISREG(VFS_I(sc->ip)->i_mode))
-		xfs_scrub_inode_check_reflink_iflag(sc, ino, bp);
+		xfs_scrub_inode_check_reflink_iflag(sc, sc->ip->i_ino, NULL);
 
-	xfs_scrub_inode_xref(sc, ino, dip);
+	xfs_scrub_inode_xref(sc, sc->ip->i_ino, &di);
 out:
-	if (bp)
-		xfs_trans_brelse(sc->tp, bp);
 	return error;
 }


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 7/9] xfs: remove xfs_buf parameter from inode scrub methods
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (5 preceding siblings ...)
  2018-03-15  0:30 ` [PATCH 6/9] xfs: inode scrubber shouldn't bother with raw checks Darrick J. Wong
@ 2018-03-15  0:30 ` Darrick J. Wong
  2018-03-21 17:42   ` Brian Foster
  2018-03-15  0:30 ` [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber Darrick J. Wong
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:30 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Now that we no longer do raw inode buffer scrubbing, the bp parameter is
no longer used anywhere we're dealing with an inode, so remove it and
all the useless NULL parameters that go with it.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/attr.c     |    2 -
 fs/xfs/scrub/bmap.c     |    4 +-
 fs/xfs/scrub/common.c   |   22 ++++------
 fs/xfs/scrub/common.h   |   13 ++----
 fs/xfs/scrub/dir.c      |    2 -
 fs/xfs/scrub/inode.c    |  106 ++++++++++++++++++++++-------------------------
 fs/xfs/scrub/quota.c    |    2 -
 fs/xfs/scrub/rtbitmap.c |    3 -
 fs/xfs/scrub/trace.h    |   31 ++------------
 9 files changed, 74 insertions(+), 111 deletions(-)


diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c
index 4ed8047..127575f 100644
--- a/fs/xfs/scrub/attr.c
+++ b/fs/xfs/scrub/attr.c
@@ -98,7 +98,7 @@ xfs_scrub_xattr_listent(
 
 	if (flags & XFS_ATTR_INCOMPLETE) {
 		/* Incomplete attr key, just mark the inode for preening. */
-		xfs_scrub_ino_set_preen(sx->sc, context->dp->i_ino, NULL);
+		xfs_scrub_ino_set_preen(sx->sc, context->dp->i_ino);
 		return;
 	}
 
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 2f38add..23556ca 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -615,7 +615,7 @@ xfs_scrub_bmap(
 			goto out;
 		/* No CoW forks on non-reflink inodes/filesystems. */
 		if (!xfs_is_reflink_inode(ip)) {
-			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
+			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
 			goto out;
 		}
 		break;
@@ -624,7 +624,7 @@ xfs_scrub_bmap(
 			goto out_check_rmap;
 		if (!xfs_sb_version_hasattr(&mp->m_sb) &&
 		    !xfs_sb_version_hasattr2(&mp->m_sb))
-			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
+			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
 		break;
 	default:
 		ASSERT(whichfork == XFS_DATA_FORK);
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 8033ab9..fe3a2b1 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -213,12 +213,10 @@ xfs_scrub_block_set_preen(
 void
 xfs_scrub_ino_set_preen(
 	struct xfs_scrub_context	*sc,
-	xfs_ino_t			ino,
-	struct xfs_buf			*bp)
+	xfs_ino_t			ino)
 {
 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
-	trace_xfs_scrub_ino_preen(sc, ino, bp ? bp->b_bn : 0,
-			__return_address);
+	trace_xfs_scrub_ino_preen(sc, ino, __return_address);
 }
 
 /* Record a corrupt block. */
@@ -249,22 +247,20 @@ xfs_scrub_block_xref_set_corrupt(
 void
 xfs_scrub_ino_set_corrupt(
 	struct xfs_scrub_context	*sc,
-	xfs_ino_t			ino,
-	struct xfs_buf			*bp)
+	xfs_ino_t			ino)
 {
 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
-	trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address);
+	trace_xfs_scrub_ino_error(sc, ino, __return_address);
 }
 
 /* Record a corruption while cross-referencing with an inode. */
 void
 xfs_scrub_ino_xref_set_corrupt(
 	struct xfs_scrub_context	*sc,
-	xfs_ino_t			ino,
-	struct xfs_buf			*bp)
+	xfs_ino_t			ino)
 {
 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
-	trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address);
+	trace_xfs_scrub_ino_error(sc, ino, __return_address);
 }
 
 /* Record corruption in a block indexed by a file fork. */
@@ -296,12 +292,10 @@ xfs_scrub_fblock_xref_set_corrupt(
 void
 xfs_scrub_ino_set_warning(
 	struct xfs_scrub_context	*sc,
-	xfs_ino_t			ino,
-	struct xfs_buf			*bp)
+	xfs_ino_t			ino)
 {
 	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
-	trace_xfs_scrub_ino_warning(sc, ino, bp ? bp->b_bn : 0,
-			__return_address);
+	trace_xfs_scrub_ino_warning(sc, ino, __return_address);
 }
 
 /* Warn about a block indexed by a file fork that needs review. */
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
index ddb65d2..deaf604 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -63,25 +63,22 @@ bool xfs_scrub_fblock_xref_process_error(struct xfs_scrub_context *sc,
 
 void xfs_scrub_block_set_preen(struct xfs_scrub_context *sc,
 		struct xfs_buf *bp);
-void xfs_scrub_ino_set_preen(struct xfs_scrub_context *sc, xfs_ino_t ino,
-		struct xfs_buf *bp);
+void xfs_scrub_ino_set_preen(struct xfs_scrub_context *sc, xfs_ino_t ino);
 
 void xfs_scrub_block_set_corrupt(struct xfs_scrub_context *sc,
 		struct xfs_buf *bp);
-void xfs_scrub_ino_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino,
-		struct xfs_buf *bp);
+void xfs_scrub_ino_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino);
 void xfs_scrub_fblock_set_corrupt(struct xfs_scrub_context *sc, int whichfork,
 		xfs_fileoff_t offset);
 
 void xfs_scrub_block_xref_set_corrupt(struct xfs_scrub_context *sc,
 		struct xfs_buf *bp);
-void xfs_scrub_ino_xref_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino,
-		struct xfs_buf *bp);
+void xfs_scrub_ino_xref_set_corrupt(struct xfs_scrub_context *sc,
+		xfs_ino_t ino);
 void xfs_scrub_fblock_xref_set_corrupt(struct xfs_scrub_context *sc,
 		int whichfork, xfs_fileoff_t offset);
 
-void xfs_scrub_ino_set_warning(struct xfs_scrub_context *sc, xfs_ino_t ino,
-		struct xfs_buf *bp);
+void xfs_scrub_ino_set_warning(struct xfs_scrub_context *sc, xfs_ino_t ino);
 void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork,
 		xfs_fileoff_t offset);
 
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 50b6a26..38f2980 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -781,7 +781,7 @@ xfs_scrub_directory(
 
 	/* Plausible size? */
 	if (sc->ip->i_d.di_size < xfs_dir2_sf_hdr_size(0)) {
-		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
+		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
 		goto out;
 	}
 
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 0332a01..9eadca9 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -98,7 +98,6 @@ xfs_scrub_setup_inode(
 STATIC void
 xfs_scrub_inode_extsize(
 	struct xfs_scrub_context	*sc,
-	struct xfs_buf			*bp,
 	struct xfs_dinode		*dip,
 	xfs_ino_t			ino,
 	uint16_t			mode,
@@ -149,7 +148,7 @@ xfs_scrub_inode_extsize(
 
 	return;
 bad:
-	xfs_scrub_ino_set_corrupt(sc, ino, bp);
+	xfs_scrub_ino_set_corrupt(sc, ino);
 }
 
 /*
@@ -161,7 +160,6 @@ xfs_scrub_inode_extsize(
 STATIC void
 xfs_scrub_inode_cowextsize(
 	struct xfs_scrub_context	*sc,
-	struct xfs_buf			*bp,
 	struct xfs_dinode		*dip,
 	xfs_ino_t			ino,
 	uint16_t			mode,
@@ -205,14 +203,13 @@ xfs_scrub_inode_cowextsize(
 
 	return;
 bad:
-	xfs_scrub_ino_set_corrupt(sc, ino, bp);
+	xfs_scrub_ino_set_corrupt(sc, ino);
 }
 
 /* Make sure the di_flags make sense for the inode. */
 STATIC void
 xfs_scrub_inode_flags(
 	struct xfs_scrub_context	*sc,
-	struct xfs_buf			*bp,
 	struct xfs_dinode		*dip,
 	xfs_ino_t			ino,
 	uint16_t			mode,
@@ -251,14 +248,13 @@ xfs_scrub_inode_flags(
 
 	return;
 bad:
-	xfs_scrub_ino_set_corrupt(sc, ino, bp);
+	xfs_scrub_ino_set_corrupt(sc, ino);
 }
 
 /* Make sure the di_flags2 make sense for the inode. */
 STATIC void
 xfs_scrub_inode_flags2(
 	struct xfs_scrub_context	*sc,
-	struct xfs_buf			*bp,
 	struct xfs_dinode		*dip,
 	xfs_ino_t			ino,
 	uint16_t			mode,
@@ -295,14 +291,13 @@ xfs_scrub_inode_flags2(
 
 	return;
 bad:
-	xfs_scrub_ino_set_corrupt(sc, ino, bp);
+	xfs_scrub_ino_set_corrupt(sc, ino);
 }
 
 /* Scrub all the ondisk inode fields. */
 STATIC void
 xfs_scrub_dinode(
 	struct xfs_scrub_context	*sc,
-	struct xfs_buf			*bp,
 	struct xfs_dinode		*dip,
 	xfs_ino_t			ino)
 {
@@ -333,7 +328,7 @@ xfs_scrub_dinode(
 		/* mode is recognized */
 		break;
 	default:
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	}
 
@@ -344,22 +339,22 @@ xfs_scrub_dinode(
 		 * We autoconvert v1 inodes into v2 inodes on writeout,
 		 * so just mark this inode for preening.
 		 */
-		xfs_scrub_ino_set_preen(sc, ino, bp);
+		xfs_scrub_ino_set_preen(sc, ino);
 		break;
 	case 2:
 	case 3:
 		if (dip->di_onlink != 0)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 
 		if (dip->di_mode == 0 && sc->ip)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 
 		if (dip->di_projid_hi != 0 &&
 		    !xfs_sb_version_hasprojid32bit(&mp->m_sb))
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	default:
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 		return;
 	}
 
@@ -369,40 +364,40 @@ xfs_scrub_dinode(
 	 */
 	if (dip->di_uid == cpu_to_be32(-1U) ||
 	    dip->di_gid == cpu_to_be32(-1U))
-		xfs_scrub_ino_set_warning(sc, ino, bp);
+		xfs_scrub_ino_set_warning(sc, ino);
 
 	/* di_format */
 	switch (dip->di_format) {
 	case XFS_DINODE_FMT_DEV:
 		if (!S_ISCHR(mode) && !S_ISBLK(mode) &&
 		    !S_ISFIFO(mode) && !S_ISSOCK(mode))
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	case XFS_DINODE_FMT_LOCAL:
 		if (!S_ISDIR(mode) && !S_ISLNK(mode))
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	case XFS_DINODE_FMT_EXTENTS:
 		if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode))
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	case XFS_DINODE_FMT_BTREE:
 		if (!S_ISREG(mode) && !S_ISDIR(mode))
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	case XFS_DINODE_FMT_UUID:
 	default:
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	}
 
 	/* di_[amc]time.nsec */
 	if (be32_to_cpu(dip->di_atime.t_nsec) >= NSEC_PER_SEC)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 	if (be32_to_cpu(dip->di_mtime.t_nsec) >= NSEC_PER_SEC)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 	if (be32_to_cpu(dip->di_ctime.t_nsec) >= NSEC_PER_SEC)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 
 	/*
 	 * di_size.  xfs_dinode_verify checks for things that screw up
@@ -411,19 +406,19 @@ xfs_scrub_dinode(
 	 */
 	isize = be64_to_cpu(dip->di_size);
 	if (isize & (1ULL << 63))
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 
 	/* Devices, fifos, and sockets must have zero size */
 	if (!S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode) && isize != 0)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 
 	/* Directories can't be larger than the data section size (32G) */
 	if (S_ISDIR(mode) && (isize == 0 || isize >= XFS_DIR2_SPACE_SIZE))
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 
 	/* Symlinks can't be larger than SYMLINK_MAXLEN */
 	if (S_ISLNK(mode) && (isize == 0 || isize >= XFS_SYMLINK_MAXLEN))
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 
 	/*
 	 * Warn if the running kernel can't handle the kinds of offsets
@@ -432,7 +427,7 @@ xfs_scrub_dinode(
 	 * overly large offsets, flag the inode for admin review.
 	 */
 	if (isize >= mp->m_super->s_maxbytes)
-		xfs_scrub_ino_set_warning(sc, ino, bp);
+		xfs_scrub_ino_set_warning(sc, ino);
 
 	/* di_nblocks */
 	if (flags2 & XFS_DIFLAG2_REFLINK) {
@@ -447,15 +442,15 @@ xfs_scrub_dinode(
 		 */
 		if (be64_to_cpu(dip->di_nblocks) >=
 		    mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 	} else {
 		if (be64_to_cpu(dip->di_nblocks) >= mp->m_sb.sb_dblocks)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 	}
 
-	xfs_scrub_inode_flags(sc, bp, dip, ino, mode, flags);
+	xfs_scrub_inode_flags(sc, dip, ino, mode, flags);
 
-	xfs_scrub_inode_extsize(sc, bp, dip, ino, mode, flags);
+	xfs_scrub_inode_extsize(sc, dip, ino, mode, flags);
 
 	/* di_nextents */
 	nextents = be32_to_cpu(dip->di_nextents);
@@ -463,31 +458,31 @@ xfs_scrub_dinode(
 	switch (dip->di_format) {
 	case XFS_DINODE_FMT_EXTENTS:
 		if (nextents > fork_recs)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	case XFS_DINODE_FMT_BTREE:
 		if (nextents <= fork_recs)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	default:
 		if (nextents != 0)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	}
 
 	/* di_forkoff */
 	if (XFS_DFORK_APTR(dip) >= (char *)dip + mp->m_sb.sb_inodesize)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 	if (dip->di_anextents != 0 && dip->di_forkoff == 0)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 	if (dip->di_forkoff == 0 && dip->di_aformat != XFS_DINODE_FMT_EXTENTS)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 
 	/* di_aformat */
 	if (dip->di_aformat != XFS_DINODE_FMT_LOCAL &&
 	    dip->di_aformat != XFS_DINODE_FMT_EXTENTS &&
 	    dip->di_aformat != XFS_DINODE_FMT_BTREE)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 
 	/* di_anextents */
 	nextents = be16_to_cpu(dip->di_anextents);
@@ -495,22 +490,22 @@ xfs_scrub_dinode(
 	switch (dip->di_aformat) {
 	case XFS_DINODE_FMT_EXTENTS:
 		if (nextents > fork_recs)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	case XFS_DINODE_FMT_BTREE:
 		if (nextents <= fork_recs)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 		break;
 	default:
 		if (nextents != 0)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
+			xfs_scrub_ino_set_corrupt(sc, ino);
 	}
 
 	if (dip->di_version >= 3) {
 		if (be32_to_cpu(dip->di_crtime.t_nsec) >= NSEC_PER_SEC)
-			xfs_scrub_ino_set_corrupt(sc, ino, bp);
-		xfs_scrub_inode_flags2(sc, bp, dip, ino, mode, flags, flags2);
-		xfs_scrub_inode_cowextsize(sc, bp, dip, ino, mode, flags,
+			xfs_scrub_ino_set_corrupt(sc, ino);
+		xfs_scrub_inode_flags2(sc, dip, ino, mode, flags, flags2);
+		xfs_scrub_inode_cowextsize(sc, dip, ino, mode, flags,
 				flags2);
 	}
 }
@@ -579,18 +574,18 @@ xfs_scrub_inode_xref_bmap(
 	if (!xfs_scrub_should_check_xref(sc, &error, NULL))
 		return;
 	if (nextents < be32_to_cpu(dip->di_nextents))
-		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
+		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino);
 
 	error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
 			&nextents, &acount);
 	if (!xfs_scrub_should_check_xref(sc, &error, NULL))
 		return;
 	if (nextents != be16_to_cpu(dip->di_anextents))
-		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
+		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino);
 
 	/* Check nblocks against the inode. */
 	if (count + acount != be64_to_cpu(dip->di_nblocks))
-		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
+		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino);
 }
 
 /* Cross-reference with the other btrees. */
@@ -634,8 +629,7 @@ xfs_scrub_inode_xref(
 static void
 xfs_scrub_inode_check_reflink_iflag(
 	struct xfs_scrub_context	*sc,
-	xfs_ino_t			ino,
-	struct xfs_buf			*bp)
+	xfs_ino_t			ino)
 {
 	struct xfs_mount		*mp = sc->mp;
 	bool				has_shared;
@@ -650,9 +644,9 @@ xfs_scrub_inode_check_reflink_iflag(
 			XFS_INO_TO_AGBNO(mp, ino), &error))
 		return;
 	if (xfs_is_reflink_inode(sc->ip) && !has_shared)
-		xfs_scrub_ino_set_preen(sc, ino, bp);
+		xfs_scrub_ino_set_preen(sc, ino);
 	else if (!xfs_is_reflink_inode(sc->ip) && has_shared)
-		xfs_scrub_ino_set_corrupt(sc, ino, bp);
+		xfs_scrub_ino_set_corrupt(sc, ino);
 }
 
 /* Scrub an inode. */
@@ -665,13 +659,13 @@ xfs_scrub_inode(
 
 	/* iget failed means automatic fail. */
 	if (!sc->ip) {
-		xfs_scrub_ino_set_corrupt(sc, sc->sm->sm_ino, NULL);
+		xfs_scrub_ino_set_corrupt(sc, sc->sm->sm_ino);
 		return 0;
 	}
 
 	/* Scrub the inode core. */
 	xfs_inode_to_disk(sc->ip, &di, 0);
-	xfs_scrub_dinode(sc, NULL, &di, sc->ip->i_ino);
+	xfs_scrub_dinode(sc, &di, sc->ip->i_ino);
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		goto out;
 
@@ -681,7 +675,7 @@ xfs_scrub_inode(
 	 * we scrubbed the dinode.
 	 */
 	if (S_ISREG(VFS_I(sc->ip)->i_mode))
-		xfs_scrub_inode_check_reflink_iflag(sc, sc->ip->i_ino, NULL);
+		xfs_scrub_inode_check_reflink_iflag(sc, sc->ip->i_ino);
 
 	xfs_scrub_inode_xref(sc, sc->ip->i_ino, &di);
 out:
diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c
index 51daa4a..6ba465e 100644
--- a/fs/xfs/scrub/quota.c
+++ b/fs/xfs/scrub/quota.c
@@ -219,7 +219,7 @@ xfs_scrub_quota(
 	/* Look for problem extents. */
 	xfs_ilock(ip, XFS_ILOCK_EXCL);
 	if (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) {
-		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
+		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
 		goto out_unlock_inode;
 	}
 	max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
index 2639099..39c41dfe 100644
--- a/fs/xfs/scrub/rtbitmap.c
+++ b/fs/xfs/scrub/rtbitmap.c
@@ -116,8 +116,7 @@ xfs_scrub_xref_is_used_rt_space(
 	if (!xfs_scrub_should_check_xref(sc, &error, NULL))
 		goto out_unlock;
 	if (is_free)
-		xfs_scrub_ino_xref_set_corrupt(sc, sc->mp->m_rbmip->i_ino,
-				NULL);
+		xfs_scrub_ino_xref_set_corrupt(sc, sc->mp->m_rbmip->i_ino);
 out_unlock:
 	xfs_iunlock(sc->mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
 }
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 4dc8968..5d2b1c2 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -174,53 +174,32 @@ DEFINE_SCRUB_BLOCK_ERROR_EVENT(xfs_scrub_block_error);
 DEFINE_SCRUB_BLOCK_ERROR_EVENT(xfs_scrub_block_preen);
 
 DECLARE_EVENT_CLASS(xfs_scrub_ino_error_class,
-	TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, xfs_daddr_t daddr,
-		 void *ret_ip),
-	TP_ARGS(sc, ino, daddr, ret_ip),
+	TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, void *ret_ip),
+	TP_ARGS(sc, ino, ret_ip),
 	TP_STRUCT__entry(
 		__field(dev_t, dev)
 		__field(xfs_ino_t, ino)
 		__field(unsigned int, type)
-		__field(xfs_agnumber_t, agno)
-		__field(xfs_agblock_t, bno)
 		__field(void *, ret_ip)
 	),
 	TP_fast_assign(
-		xfs_fsblock_t	fsbno;
-		xfs_agnumber_t	agno;
-		xfs_agblock_t	bno;
-
-		if (daddr) {
-			fsbno = XFS_DADDR_TO_FSB(sc->mp, daddr);
-			agno = XFS_FSB_TO_AGNO(sc->mp, fsbno);
-			bno = XFS_FSB_TO_AGBNO(sc->mp, fsbno);
-		} else {
-			agno = XFS_INO_TO_AGNO(sc->mp, ino);
-			bno = XFS_AGINO_TO_AGBNO(sc->mp,
-					XFS_INO_TO_AGINO(sc->mp, ino));
-		}
-
 		__entry->dev = sc->mp->m_super->s_dev;
 		__entry->ino = ino;
 		__entry->type = sc->sm->sm_type;
-		__entry->agno = agno;
-		__entry->bno = bno;
 		__entry->ret_ip = ret_ip;
 	),
-	TP_printk("dev %d:%d ino 0x%llx type %u agno %u agbno %u ret_ip %pS",
+	TP_printk("dev %d:%d ino 0x%llx type %u ret_ip %pS",
 		  MAJOR(__entry->dev), MINOR(__entry->dev),
 		  __entry->ino,
 		  __entry->type,
-		  __entry->agno,
-		  __entry->bno,
 		  __entry->ret_ip)
 )
 
 #define DEFINE_SCRUB_INO_ERROR_EVENT(name) \
 DEFINE_EVENT(xfs_scrub_ino_error_class, name, \
 	TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, \
-		 xfs_daddr_t daddr, void *ret_ip), \
-	TP_ARGS(sc, ino, daddr, ret_ip))
+		 void *ret_ip), \
+	TP_ARGS(sc, ino, ret_ip))
 
 DEFINE_SCRUB_INO_ERROR_EVENT(xfs_scrub_ino_error);
 DEFINE_SCRUB_INO_ERROR_EVENT(xfs_scrub_ino_preen);


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (6 preceding siblings ...)
  2018-03-15  0:30 ` [PATCH 7/9] xfs: remove xfs_buf parameter from inode scrub methods Darrick J. Wong
@ 2018-03-15  0:30 ` Darrick J. Wong
  2018-03-21 17:42   ` Brian Foster
  2018-03-22  6:24   ` [PATCH v2 " Darrick J. Wong
  2018-03-15  0:30 ` [PATCH 9/9] xfs: move inode extent size hint validation to libxfs Darrick J. Wong
                   ` (3 subsequent siblings)
  11 siblings, 2 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:30 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

During the inode btree scrubs we try to confirm the freemask bits
against the inode records.  If the inode buffer read fails, this is a
cross-referencing error, not a corruption of the inode btree itself.
Use the xref_process_error call here.  Found via core.version middlebit
fuzz in xfs/415.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/ialloc.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)


diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 63ab3f9..32e0d1a 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -259,7 +259,8 @@ xfs_scrub_iallocbt_check_freemask(
 
 		error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap,
 				&dip, &bp, 0, 0);
-		if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error))
+		if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur, 0,
+				&error))
 			continue;
 
 		/* Which inodes are free? */


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 9/9] xfs: move inode extent size hint validation to libxfs
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (7 preceding siblings ...)
  2018-03-15  0:30 ` [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber Darrick J. Wong
@ 2018-03-15  0:30 ` Darrick J. Wong
  2018-03-21 17:42   ` Brian Foster
  2018-03-21  3:21 ` [PATCH 10/9] xfs: don't accept inode buffers with suspicious unlinked chains Darrick J. Wong
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-15  0:30 UTC (permalink / raw)
  To: darrick.wong; +Cc: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

Extent size hint validation is used by scrub to decide if there's an
error, and it will be used by repair to decide to remove the hint.
Since these use the same validation functions, move them to libxfs.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_inode_buf.c |  105 +++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_inode_buf.h |    5 ++
 fs/xfs/scrub/inode.c          |  100 +++++----------------------------------
 3 files changed, 122 insertions(+), 88 deletions(-)


diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 51019e5..cdd4c1d 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -651,3 +651,108 @@ xfs_iread(
 	xfs_trans_brelse(tp, bp);
 	return error;
 }
+
+/*
+ * Validate di_extsize hint.
+ *
+ * The rules are documented at xfs_ioctl_setattr_check_extsize().
+ * These functions must be kept in sync with each other.
+ */
+xfs_failaddr_t
+xfs_inode_validate_extsize(
+	struct xfs_mount		*mp,
+	uint32_t			extsize,
+	uint16_t			mode,
+	uint16_t			flags)
+{
+	bool				rt_flag;
+	bool				hint_flag;
+	bool				inherit_flag;
+	uint32_t			extsize_bytes;
+	uint32_t			blocksize_bytes;
+
+	rt_flag = (flags & XFS_DIFLAG_REALTIME);
+	hint_flag = (flags & XFS_DIFLAG_EXTSIZE);
+	inherit_flag = (flags & XFS_DIFLAG_EXTSZINHERIT);
+	extsize_bytes = XFS_FSB_TO_B(mp, extsize);
+
+	if (rt_flag)
+		blocksize_bytes = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
+	else
+		blocksize_bytes = mp->m_sb.sb_blocksize;
+
+	if ((hint_flag || inherit_flag) && !(S_ISDIR(mode) || S_ISREG(mode)))
+		return __this_address;
+
+	if (hint_flag && !S_ISREG(mode))
+		return __this_address;
+
+	if (inherit_flag && !S_ISDIR(mode))
+		return __this_address;
+
+	if ((hint_flag || inherit_flag) && extsize == 0)
+		return __this_address;
+
+	if (!(hint_flag || inherit_flag) && extsize != 0)
+		return __this_address;
+
+	if (extsize_bytes % blocksize_bytes)
+		return __this_address;
+
+	if (extsize > MAXEXTLEN)
+		return __this_address;
+
+	if (!rt_flag && extsize > mp->m_sb.sb_agblocks / 2)
+		return __this_address;
+
+	return NULL;
+}
+
+/*
+ * Validate di_cowextsize hint.
+ *
+ * The rules are documented at xfs_ioctl_setattr_check_cowextsize().
+ * These functions must be kept in sync with each other.
+ */
+xfs_failaddr_t
+xfs_inode_validate_cowextsize(
+	struct xfs_mount		*mp,
+	uint32_t			cowextsize,
+	uint16_t			mode,
+	uint16_t			flags,
+	uint64_t			flags2)
+{
+	bool				rt_flag;
+	bool				hint_flag;
+	uint32_t			cowextsize_bytes;
+
+	rt_flag = (flags & XFS_DIFLAG_REALTIME);
+	hint_flag = (flags2 & XFS_DIFLAG2_COWEXTSIZE);
+	cowextsize_bytes = XFS_FSB_TO_B(mp, cowextsize);
+
+	if (hint_flag && !xfs_sb_version_hasreflink(&mp->m_sb))
+		return __this_address;
+
+	if (hint_flag && !(S_ISDIR(mode) || S_ISREG(mode)))
+		return __this_address;
+
+	if (hint_flag && cowextsize == 0)
+		return __this_address;
+
+	if (!hint_flag && cowextsize != 0)
+		return __this_address;
+
+	if (hint_flag && rt_flag)
+		return __this_address;
+
+	if (cowextsize_bytes % mp->m_sb.sb_blocksize)
+		return __this_address;
+
+	if (cowextsize > MAXEXTLEN)
+		return __this_address;
+
+	if (cowextsize > mp->m_sb.sb_agblocks / 2)
+		return __this_address;
+
+	return NULL;
+}
diff --git a/fs/xfs/libxfs/xfs_inode_buf.h b/fs/xfs/libxfs/xfs_inode_buf.h
index 8a5e1da..d9a376a 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.h
+++ b/fs/xfs/libxfs/xfs_inode_buf.h
@@ -84,5 +84,10 @@ void	xfs_inobp_check(struct xfs_mount *, struct xfs_buf *);
 
 xfs_failaddr_t xfs_dinode_verify(struct xfs_mount *mp, xfs_ino_t ino,
 			   struct xfs_dinode *dip);
+xfs_failaddr_t xfs_inode_validate_extsize(struct xfs_mount *mp,
+		uint32_t extsize, uint16_t mode, uint16_t flags);
+xfs_failaddr_t xfs_inode_validate_cowextsize(struct xfs_mount *mp,
+		uint32_t cowextsize, uint16_t mode, uint16_t flags,
+		uint64_t flags2);
 
 #endif	/* __XFS_INODE_BUF_H__ */
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 9eadca9..25bca48 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -89,12 +89,7 @@ xfs_scrub_setup_inode(
 
 /* Inode core */
 
-/*
- * Validate di_extsize hint.
- *
- * The rules are documented at xfs_ioctl_setattr_check_extsize().
- * These functions must be kept in sync with each other.
- */
+/* Validate di_extsize hint. */
 STATIC void
 xfs_scrub_inode_extsize(
 	struct xfs_scrub_context	*sc,
@@ -103,52 +98,12 @@ xfs_scrub_inode_extsize(
 	uint16_t			mode,
 	uint16_t			flags)
 {
-	struct xfs_mount		*mp = sc->mp;
-	bool				rt_flag;
-	bool				hint_flag;
-	bool				inherit_flag;
-	uint32_t			extsize;
-	uint32_t			extsize_bytes;
-	uint32_t			blocksize_bytes;
-
-	rt_flag = (flags & XFS_DIFLAG_REALTIME);
-	hint_flag = (flags & XFS_DIFLAG_EXTSIZE);
-	inherit_flag = (flags & XFS_DIFLAG_EXTSZINHERIT);
-	extsize = be32_to_cpu(dip->di_extsize);
-	extsize_bytes = XFS_FSB_TO_B(sc->mp, extsize);
-
-	if (rt_flag)
-		blocksize_bytes = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
-	else
-		blocksize_bytes = mp->m_sb.sb_blocksize;
-
-	if ((hint_flag || inherit_flag) && !(S_ISDIR(mode) || S_ISREG(mode)))
-		goto bad;
-
-	if (hint_flag && !S_ISREG(mode))
-		goto bad;
-
-	if (inherit_flag && !S_ISDIR(mode))
-		goto bad;
+	xfs_failaddr_t			fa;
 
-	if ((hint_flag || inherit_flag) && extsize == 0)
-		goto bad;
-
-	if (!(hint_flag || inherit_flag) && extsize != 0)
-		goto bad;
-
-	if (extsize_bytes % blocksize_bytes)
-		goto bad;
-
-	if (extsize > MAXEXTLEN)
-		goto bad;
-
-	if (!rt_flag && extsize > mp->m_sb.sb_agblocks / 2)
-		goto bad;
-
-	return;
-bad:
-	xfs_scrub_ino_set_corrupt(sc, ino);
+	fa = xfs_inode_validate_extsize(sc->mp, be32_to_cpu(dip->di_extsize),
+			mode, flags);
+	if (fa)
+		xfs_scrub_ino_set_corrupt(sc, ino);
 }
 
 /*
@@ -166,44 +121,13 @@ xfs_scrub_inode_cowextsize(
 	uint16_t			flags,
 	uint64_t			flags2)
 {
-	struct xfs_mount		*mp = sc->mp;
-	bool				rt_flag;
-	bool				hint_flag;
-	uint32_t			extsize;
-	uint32_t			extsize_bytes;
-
-	rt_flag = (flags & XFS_DIFLAG_REALTIME);
-	hint_flag = (flags2 & XFS_DIFLAG2_COWEXTSIZE);
-	extsize = be32_to_cpu(dip->di_cowextsize);
-	extsize_bytes = XFS_FSB_TO_B(sc->mp, extsize);
-
-	if (hint_flag && !xfs_sb_version_hasreflink(&mp->m_sb))
-		goto bad;
-
-	if (hint_flag && !(S_ISDIR(mode) || S_ISREG(mode)))
-		goto bad;
-
-	if (hint_flag && extsize == 0)
-		goto bad;
-
-	if (!hint_flag && extsize != 0)
-		goto bad;
+	xfs_failaddr_t			fa;
 
-	if (hint_flag && rt_flag)
-		goto bad;
-
-	if (extsize_bytes % mp->m_sb.sb_blocksize)
-		goto bad;
-
-	if (extsize > MAXEXTLEN)
-		goto bad;
-
-	if (extsize > mp->m_sb.sb_agblocks / 2)
-		goto bad;
-
-	return;
-bad:
-	xfs_scrub_ino_set_corrupt(sc, ino);
+	fa = xfs_inode_validate_cowextsize(sc->mp,
+			be32_to_cpu(dip->di_cowextsize), mode, flags,
+			flags2);
+	if (fa)
+		xfs_scrub_ino_set_corrupt(sc, ino);
 }
 
 /* Make sure the di_flags make sense for the inode. */


^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 10/9] xfs: don't accept inode buffers with suspicious unlinked chains
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (8 preceding siblings ...)
  2018-03-15  0:30 ` [PATCH 9/9] xfs: move inode extent size hint validation to libxfs Darrick J. Wong
@ 2018-03-21  3:21 ` Darrick J. Wong
  2018-03-21 17:43   ` Brian Foster
  2018-03-22  6:08   ` [PATCH v2 " Darrick J. Wong
  2018-03-21  3:21 ` [PATCH 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode Darrick J. Wong
  2018-03-22  6:19 ` [PATCH 12/9] xfs: xfs_scrub_iallocbt_xref_rmap_inodes should use xref_set_corrupt Darrick J. Wong
  11 siblings, 2 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21  3:21 UTC (permalink / raw)
  To: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

When we're verifying inode buffers, sanity-check the unlinked pointer.
We don't want to run the risk of trying to purge something that's
obviously broken.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/libxfs/xfs_inode_buf.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 4fe17b3..f216d5b 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -93,20 +93,26 @@ xfs_inode_buf_verify(
 	bool		readahead)
 {
 	struct xfs_mount *mp = bp->b_target->bt_mount;
+	xfs_agnumber_t	agno;
 	int		i;
 	int		ni;
 
 	/*
 	 * Validate the magic number and version of every inode in the buffer
 	 */
+	agno = xfs_daddr_to_agno(mp, XFS_BUF_ADDR(bp));
 	ni = XFS_BB_TO_FSB(mp, bp->b_length) * mp->m_sb.sb_inopblock;
 	for (i = 0; i < ni; i++) {
 		int		di_ok;
 		xfs_dinode_t	*dip;
+		xfs_agino_t	unlinked_ino;
 
 		dip = xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog));
+		unlinked_ino = be32_to_cpu(dip->di_next_unlinked);
 		di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) &&
-			xfs_dinode_good_version(mp, dip->di_version);
+			xfs_dinode_good_version(mp, dip->di_version) &&
+			(unlinked_ino == NULLAGINO ||
+			 xfs_verify_agino(mp, agno, unlinked_ino));
 		if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
 						XFS_ERRTAG_ITOBP_INOTOBP))) {
 			if (readahead) {
@@ -122,9 +128,9 @@ xfs_inode_buf_verify(
 				(unsigned long long)bp->b_bn, i,
 				be16_to_cpu(dip->di_magic));
 #endif
+			return;
 		}
 	}
-	xfs_inobp_check(mp, bp);
 }
 
 

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (9 preceding siblings ...)
  2018-03-21  3:21 ` [PATCH 10/9] xfs: don't accept inode buffers with suspicious unlinked chains Darrick J. Wong
@ 2018-03-21  3:21 ` Darrick J. Wong
  2018-03-22 14:34   ` Brian Foster
  2018-03-22 17:57   ` [PATCH v2 " Darrick J. Wong
  2018-03-22  6:19 ` [PATCH 12/9] xfs: xfs_scrub_iallocbt_xref_rmap_inodes should use xref_set_corrupt Darrick J. Wong
  11 siblings, 2 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21  3:21 UTC (permalink / raw)
  To: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

If a directory's parent inode pointer doesn't point to an inode, flag
the directory as corrupt.  Enable IGET_UNTRUSTED here so that we
can confirm with the inobt that the inode is present and allocated.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/parent.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index 0d38514..d1547ca 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -167,9 +167,14 @@ xfs_scrub_parent_validate(
 	 * if the parent pointer erroneously points to a file, we
 	 * can't use DONTCACHE here because DONTCACHE inodes can trigger
 	 * immediate inactive cleanup of the inode.
+	 *
+	 * If _iget returns -EINVAL then the parent inode number is garbage
+	 * and the directory is corrupt.
 	 */
-	error = xfs_iget(mp, sc->tp, dnum, 0, 0, &dp);
-	if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
+	error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp);
+	if (error == -EINVAL)
+		error = -EFSCORRUPTED;
+	if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
 		goto out;
 	if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) {
 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-15  0:29 ` [PATCH 1/9] xfs: sanity-check the unused space before trying to use it Darrick J. Wong
@ 2018-03-21 13:52   ` Brian Foster
  2018-03-21 17:44     ` Darrick J. Wong
  2018-03-22  5:59   ` [PATCH v2 " Darrick J. Wong
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-21 13:52 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:29:36PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
> it doesn't make sense.  Since a carefully crafted fuzzed image can cause
> the kernel to crash after blowing a bunch of assertions, let's move
> those checks into a validator function and rig everything up to return
> EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
> xfs/391.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_dir2.h       |    2 +
>  fs/xfs/libxfs/xfs_dir2_block.c |   38 ++++++++++++-------
>  fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
>  fs/xfs/libxfs/xfs_dir2_leaf.c  |    6 +++
>  fs/xfs/libxfs/xfs_dir2_node.c  |    4 ++
>  5 files changed, 93 insertions(+), 35 deletions(-)
> 
> 
> diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
> index 388d67c..989e95a 100644
> --- a/fs/xfs/libxfs/xfs_dir2.h
> +++ b/fs/xfs/libxfs/xfs_dir2.h
> @@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
>  extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
>  		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
>  		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
> -extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
> +extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
>  		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
>  		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
>  		int *needlogp, int *needscanp);
> diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
> index 2da86a3..5726402 100644
> --- a/fs/xfs/libxfs/xfs_dir2_block.c
> +++ b/fs/xfs/libxfs/xfs_dir2_block.c
> @@ -454,12 +454,15 @@ xfs_dir2_block_addname(
>  		/*
>  		 * Mark the space needed for the new leaf entry, now in use.
>  		 */
> -		xfs_dir2_data_use_free(args, bp, enddup,
> +		error = xfs_dir2_data_use_free(args, bp, enddup,
>  			(xfs_dir2_data_aoff_t)
>  			((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) -
>  			 sizeof(*blp)),
>  			(xfs_dir2_data_aoff_t)sizeof(*blp),
>  			&needlog, &needscan);

Parameter alignment looks screwed up on many of these updated calls
throughout.

> +		if (error)
> +			return error;
> +
>  		/*
>  		 * Update the tail (entry count).
>  		 */
...
> diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
> index 0839ffe..641b352 100644
> --- a/fs/xfs/libxfs/xfs_dir2_node.c
> +++ b/fs/xfs/libxfs/xfs_dir2_node.c
> @@ -2023,9 +2023,11 @@ xfs_dir2_node_addname_int(
>  	/*
>  	 * Mark the first part of the unused space, inuse for us.
>  	 */
> -	xfs_dir2_data_use_free(args, dbp, dup,
> +	error = xfs_dir2_data_use_free(args, dbp, dup,
>  		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
>  		&needlog, &needscan);
> +	if (error)
> +		return error;

Do we have to deal with dbp here?

Brian

>  	/*
>  	 * Fill in the new entry and log it.
>  	 */
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 2/9] xfs: refactor bmap record valiation
  2018-03-15  0:29 ` [PATCH 2/9] xfs: refactor bmap record valiation Darrick J. Wong
@ 2018-03-21 13:55   ` Brian Foster
  2018-03-21 20:30     ` Darrick J. Wong
  2018-03-22  6:01   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-21 13:55 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:29:42PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Refactor the bmap validator into a more complete helper that looks for
> extents that run off the end of the device, overflow into the next AG,
> or have invalid flag states, leaving a raw helper so that the inode
> repair can check things even if iget fails.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_bmap.c       |   55 ++++++++++++++++++++++++++++++++++++++--
>  fs/xfs/libxfs/xfs_bmap.h       |    5 ++++
>  fs/xfs/libxfs/xfs_bmap_btree.h |   14 ----------
>  fs/xfs/libxfs/xfs_inode_fork.c |   12 ++++++---
>  4 files changed, 65 insertions(+), 21 deletions(-)
> 
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index daae00e..5850e76 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1261,11 +1261,15 @@ xfs_iread_extents(
>  		 */
>  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
>  		for (j = 0; j < num_recs; j++, frp++, i++) {
> +			xfs_failaddr_t	fa;
> +
>  			xfs_bmbt_disk_get_all(frp, &new);
> -			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
> -				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> -						 XFS_ERRLEVEL_LOW, mp);
> +			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
> +			if (fa) {
>  				error = -EFSCORRUPTED;
> +				xfs_inode_verifier_error(ip, error,
> +						"xfs_bmap_read_extents(2)",

Might as well fix up the stale function name string.

> +						frp, sizeof(*frp), fa);
>  				goto out_brelse;
>  			}
>  			xfs_iext_insert(ip, &icur, &new, state);
> @@ -6154,3 +6158,48 @@ xfs_bmap_finish_one(
>  
>  	return error;
>  }
> +
> +/* Check that an extent does not have invalid flags or bad ranges. */
> +xfs_failaddr_t
> +xfs_bmbt_validate_extent(
> +	struct xfs_mount	*mp,
> +	bool			isrt,
> +	int			whichfork,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	xfs_fsblock_t		endfsb;
> +
> +	endfsb = irec->br_startblock + irec->br_blockcount - 1;
> +	if (isrt) {
> +		if (!xfs_verify_rtbno(mp, irec->br_startblock))
> +			return __this_address;
> +		if (!xfs_verify_rtbno(mp, endfsb))
> +			return __this_address;
> +	} else {
> +		if (!xfs_verify_fsbno(mp, irec->br_startblock))
> +			return __this_address;
> +		if (!xfs_verify_fsbno(mp, endfsb))
> +			return __this_address;
> +		if (XFS_FSB_TO_AGNO(mp, irec->br_startblock) !=
> +		    XFS_FSB_TO_AGNO(mp, endfsb))
> +			return __this_address;
> +	}
> +	if (irec->br_state != XFS_EXT_NORM) {
> +		if (whichfork != XFS_DATA_FORK)
> +			return __this_address;
> +		if (!xfs_sb_version_hasextflgbit(&mp->m_sb))
> +			return __this_address;
> +	}
> +	return NULL;
> +}
> +
> +/* Check that an inode's extent does not have invalid flags or bad ranges. */
> +xfs_failaddr_t
> +xfs_bmap_validate_extent(
> +	struct xfs_inode	*ip,
> +	int			whichfork,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	return xfs_bmbt_validate_extent(ip->i_mount, XFS_IS_REALTIME_INODE(ip),
> +			whichfork, irec);
> +}

Do we really need this wrapper just for an rt check? Why not just pass
ip down to xfs_bmbt_validate_extent() and pull isrt from there (if
present)?

The commit log implies the helper could be left around for future repair
code. If that's the case, I think it's more appropriate to refactor it
as such in whatever patch the helper is first used. Otherwise looks
fine.

Brian

> diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> index e36d757..e0fef89 100644
> --- a/fs/xfs/libxfs/xfs_bmap.h
> +++ b/fs/xfs/libxfs/xfs_bmap.h
> @@ -274,4 +274,9 @@ static inline int xfs_bmap_fork_to_state(int whichfork)
>  	}
>  }
>  
> +xfs_failaddr_t xfs_bmbt_validate_extent(struct xfs_mount *mp, bool isrt,
> +		int whichfork, struct xfs_bmbt_irec *irec);
> +xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork,
> +		struct xfs_bmbt_irec *irec);
> +
>  #endif	/* __XFS_BMAP_H__ */
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> index 135b8c5..e450574 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> @@ -118,18 +118,4 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
>  extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
>  		struct xfs_trans *, struct xfs_inode *, int);
>  
> -/*
> - * Check that the extent does not contain an invalid unwritten extent flag.
> - */
> -static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> -		struct xfs_bmbt_irec *irec)
> -{
> -	if (irec->br_state == XFS_EXT_NORM)
> -		return true;
> -	if (whichfork == XFS_DATA_FORK &&
> -	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> -		return true;
> -	return false;
> -}
> -
>  #endif	/* __XFS_BMAP_BTREE_H__ */
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 866d2861..613fba2 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -245,10 +245,14 @@ xfs_iformat_extents(
>  
>  		xfs_iext_first(ifp, &icur);
>  		for (i = 0; i < nex; i++, dp++) {
> +			xfs_failaddr_t	fa;
> +
>  			xfs_bmbt_disk_get_all(dp, &new);
> -			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
> -				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> -						 XFS_ERRLEVEL_LOW, mp);
> +			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
> +			if (fa) {
> +				xfs_inode_verifier_error(ip, -EFSCORRUPTED,
> +						"xfs_iformat_extents(2)",
> +						dp, sizeof(*dp), fa);
>  				return -EFSCORRUPTED;
>  			}
>  
> @@ -595,7 +599,7 @@ xfs_iextents_copy(
>  	for_each_xfs_iext(ifp, &icur, &rec) {
>  		if (isnullstartblock(rec.br_startblock))
>  			continue;
> -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, &rec));
> +		ASSERT(xfs_bmap_validate_extent(ip, whichfork, &rec) == NULL);
>  		xfs_bmbt_disk_set_all(dp, &rec);
>  		trace_xfs_write_extent(ip, &icur, state, _RET_IP_);
>  		copied += sizeof(struct xfs_bmbt_rec);
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 3/9] xfs: refactor inode verifier error logging
  2018-03-15  0:29 ` [PATCH 3/9] xfs: refactor inode verifier error logging Darrick J. Wong
@ 2018-03-21 13:55   ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-21 13:55 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:29:48PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Refactor some of the inode verifier failure logging call sites to use
> the new xfs_inode_verifier_error method which dumps the offending buffer
> as well as the code location of the failed check.  This trims the
> output, makes it clearer to the admin that repair must be run, and gives
> the developers more details to work from.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c       |    5 +++--
>  fs/xfs/libxfs/xfs_inode_fork.c |   15 +++++++++------
>  2 files changed, 12 insertions(+), 8 deletions(-)
> 
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 5850e76..fe7534e 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1244,8 +1244,9 @@ xfs_iread_extents(
>  			xfs_warn(ip->i_mount,
>  				"corrupt dinode %Lu, (btree extents).",
>  				(unsigned long long) ip->i_ino);
> -			XFS_CORRUPTION_ERROR(__func__,
> -				XFS_ERRLEVEL_LOW, ip->i_mount, block);
> +			xfs_inode_verifier_error(ip, -EFSCORRUPTED,
> +					__func__, block, sizeof(*block),
> +					__this_address);
>  			error = -EFSCORRUPTED;
>  			goto out_brelse;
>  		}
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 613fba2..701c42a 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -195,8 +195,9 @@ xfs_iformat_local(
>  	"corrupt inode %Lu (bad size %d for local fork, size = %d).",
>  			(unsigned long long) ip->i_ino, size,
>  			XFS_DFORK_SIZE(dip, ip->i_mount, whichfork));
> -		XFS_CORRUPTION_ERROR("xfs_iformat_local", XFS_ERRLEVEL_LOW,
> -				     ip->i_mount, dip);
> +		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
> +				"xfs_iformat_local", dip, sizeof(*dip),
> +				__this_address);
>  		return -EFSCORRUPTED;
>  	}
>  
> @@ -231,8 +232,9 @@ xfs_iformat_extents(
>  	if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, mp, whichfork))) {
>  		xfs_warn(ip->i_mount, "corrupt inode %Lu ((a)extents = %d).",
>  			(unsigned long long) ip->i_ino, nex);
> -		XFS_CORRUPTION_ERROR("xfs_iformat_extents(1)", XFS_ERRLEVEL_LOW,
> -				     mp, dip);
> +		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
> +				"xfs_iformat_extents(1)", dip, sizeof(*dip),
> +				__this_address);
>  		return -EFSCORRUPTED;
>  	}
>  
> @@ -309,8 +311,9 @@ xfs_iformat_btree(
>  		     level == 0 || level > XFS_BTREE_MAXLEVELS) {
>  		xfs_warn(mp, "corrupt inode %Lu (btree).",
>  					(unsigned long long) ip->i_ino);
> -		XFS_CORRUPTION_ERROR("xfs_iformat_btree", XFS_ERRLEVEL_LOW,
> -					 mp, dip);
> +		xfs_inode_verifier_error(ip, -EFSCORRUPTED,
> +				"xfs_iformat_btree", dfp, size,
> +				__this_address);
>  		return -EFSCORRUPTED;
>  	}
>  
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 4/9] xfs: refactor inode buffer verifier error logging
  2018-03-15  0:29 ` [PATCH 4/9] xfs: refactor inode buffer " Darrick J. Wong
@ 2018-03-21 13:55   ` Brian Foster
  2018-03-21 18:03     ` Darrick J. Wong
  2018-04-24 19:51   ` Eric Sandeen
  1 sibling, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-21 13:55 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:29:54PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> When the inode buffer verifier encounters an error, it's much more
> helpful to print a buffer from the offending inode instead of just the
> start of the inode chunk buffer.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/libxfs/xfs_inode_buf.c |    4 +++-
>  fs/xfs/xfs_error.c            |   29 ++++++++++++++++++++++++-----
>  fs/xfs/xfs_error.h            |    3 +++
>  3 files changed, 30 insertions(+), 6 deletions(-)
> 
> 
> diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> index 4fe17b3..51019e5 100644
> --- a/fs/xfs/libxfs/xfs_inode_buf.c
> +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> @@ -115,13 +115,15 @@ xfs_inode_buf_verify(
>  				return;
>  			}
>  
> -			xfs_verifier_error(bp, -EFSCORRUPTED, __this_address);
>  #ifdef DEBUG
>  			xfs_alert(mp,
>  				"bad inode magic/vsn daddr %lld #%d (magic=%x)",
>  				(unsigned long long)bp->b_bn, i,
>  				be16_to_cpu(dip->di_magic));
>  #endif
> +			xfs_buf_verifier_error(bp, -EFSCORRUPTED,
> +					__func__, dip, sizeof(*dip),
> +					NULL);

Why not continue to pass __this_address? Otherwise looks fine:

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  		}
>  	}
>  	xfs_inobp_check(mp, bp);
> diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
> index ccf520f..a63f508 100644
> --- a/fs/xfs/xfs_error.c
> +++ b/fs/xfs/xfs_error.c
> @@ -347,27 +347,32 @@ xfs_corruption_error(
>   * values, and omit the stack trace unless the error level is tuned high.
>   */
>  void
> -xfs_verifier_error(
> +xfs_buf_verifier_error(
>  	struct xfs_buf		*bp,
>  	int			error,
> +	const char		*name,
> +	void			*buf,
> +	size_t			bufsz,
>  	xfs_failaddr_t		failaddr)
>  {
>  	struct xfs_mount	*mp = bp->b_target->bt_mount;
>  	xfs_failaddr_t		fa;
> +	int			sz;
>  
>  	fa = failaddr ? failaddr : __return_address;
>  	__xfs_buf_ioerror(bp, error, fa);
>  
> -	xfs_alert(mp, "Metadata %s detected at %pS, %s block 0x%llx",
> +	xfs_alert(mp, "Metadata %s detected at %pS, %s block 0x%llx %s",
>  		  bp->b_error == -EFSBADCRC ? "CRC error" : "corruption",
> -		  fa, bp->b_ops->name, bp->b_bn);
> +		  fa, bp->b_ops->name, bp->b_bn, name);
>  
>  	xfs_alert(mp, "Unmount and run xfs_repair");
>  
>  	if (xfs_error_level >= XFS_ERRLEVEL_LOW) {
> +		sz = min_t(size_t, XFS_CORRUPTION_DUMP_LEN, bufsz);
>  		xfs_alert(mp, "First %d bytes of corrupted metadata buffer:",
> -				XFS_CORRUPTION_DUMP_LEN);
> -		xfs_hex_dump(xfs_buf_offset(bp, 0), XFS_CORRUPTION_DUMP_LEN);
> +				sz);
> +		xfs_hex_dump(buf, sz);
>  	}
>  
>  	if (xfs_error_level >= XFS_ERRLEVEL_HIGH)
> @@ -375,6 +380,20 @@ xfs_verifier_error(
>  }
>  
>  /*
> + * Warnings specifically for verifier errors.  Differentiate CRC vs. invalid
> + * values, and omit the stack trace unless the error level is tuned high.
> + */
> +void
> +xfs_verifier_error(
> +	struct xfs_buf		*bp,
> +	int			error,
> +	xfs_failaddr_t		failaddr)
> +{
> +	return xfs_buf_verifier_error(bp, error, "", xfs_buf_offset(bp, 0),
> +			XFS_CORRUPTION_DUMP_LEN, failaddr);
> +}
> +
> +/*
>   * Warnings for inode corruption problems.  Don't bother with the stack
>   * trace unless the error level is turned up high.
>   */
> diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h
> index 7e728c5..ce39134 100644
> --- a/fs/xfs/xfs_error.h
> +++ b/fs/xfs/xfs_error.h
> @@ -26,6 +26,9 @@ extern void xfs_error_report(const char *tag, int level, struct xfs_mount *mp,
>  extern void xfs_corruption_error(const char *tag, int level,
>  			struct xfs_mount *mp, void *p, const char *filename,
>  			int linenum, xfs_failaddr_t failaddr);
> +extern void xfs_buf_verifier_error(struct xfs_buf *bp, int error,
> +			const char *name, void *buf, size_t bufsz,
> +			xfs_failaddr_t failaddr);
>  extern void xfs_verifier_error(struct xfs_buf *bp, int error,
>  			xfs_failaddr_t failaddr);
>  extern void xfs_inode_verifier_error(struct xfs_inode *ip, int error,
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files
  2018-03-15  0:30 ` [PATCH 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files Darrick J. Wong
@ 2018-03-21 17:42   ` Brian Foster
  2018-03-21 18:11     ` Darrick J. Wong
  2018-03-22  6:02   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-21 17:42 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:30:00PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> When we're scanning an extent mapping inode fork, ensure that every rmap
> record for this ifork has a corresponding bmbt record too.  This
> (mostly) provides the ability to cross-reference rmap records with bmap
> data.  The rmap scrubber cannot do the xref on its own because that
> requires taking an ilock with the agf lock held, which violates our
> locking order rules (inode, then agf).
> 
> Note that we only do this for forks that are in btree format due to the
> increased complexity; or forks that should have data but suspiciously
> have zero extents because the inode could have just had its iforks
> zapped by the inode repair code and now we need to reclaim the old
> extents.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/scrub/bmap.c |  164 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 163 insertions(+), 1 deletion(-)
> 
> 
> diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> index d002821..2f38add 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -37,6 +37,7 @@
>  #include "xfs_bmap_util.h"
>  #include "xfs_bmap_btree.h"
>  #include "xfs_rmap.h"
> +#include "xfs_rmap_btree.h"
>  #include "xfs_refcount.h"
>  #include "scrub/xfs_scrub.h"
>  #include "scrub/scrub.h"
> @@ -423,6 +424,163 @@ xfs_scrub_bmap_btree(
>  	return error;
>  }
>  
> +struct xfs_scrub_bmap_check_rmap_info {
> +	struct xfs_scrub_context	*sc;
> +	int				whichfork;
> +	struct xfs_iext_cursor		icur;
> +};
> +
> +/* Can we find bmaps that fit this rmap? */
> +STATIC int
> +xfs_scrub_bmap_check_rmap(
> +	struct xfs_btree_cur		*cur,
> +	struct xfs_rmap_irec		*rec,
> +	void				*priv)
> +{
> +	struct xfs_bmbt_irec		irec;
> +	struct xfs_scrub_bmap_check_rmap_info	*sbcri = priv;
> +	struct xfs_ifork		*ifp;
> +	struct xfs_scrub_context	*sc = sbcri->sc;
> +	bool				have_map;
> +
> +	/* Is this even the right fork? */
> +	if (rec->rm_owner != sc->ip->i_ino)
> +		return 0;
> +	if ((sbcri->whichfork == XFS_ATTR_FORK) ^
> +	    !!(rec->rm_flags & XFS_RMAP_ATTR_FORK))
> +		return 0;
> +	if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
> +		return 0;
> +
> +	/* Now look up the bmbt record. */
> +	ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork);
> +	if (!ifp) {
> +		xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> +				rec->rm_offset);
> +		goto out;
> +	}
> +	have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
> +			&sbcri->icur, &irec);
> +	if (!have_map)
> +		xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> +				rec->rm_offset);
> +

Comment on why/how an rmap record covers multiple bmbt records?

> +	while (have_map) {
> +		if (irec.br_startoff != rec->rm_offset)
> +			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> +					rec->rm_offset);
> +		if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp,
> +				cur->bc_private.a.agno, rec->rm_startblock))
> +			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> +					rec->rm_offset);
> +		if (irec.br_blockcount > rec->rm_blockcount)
> +			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> +					rec->rm_offset);
> +		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> +			break;
> +		rec->rm_startblock += irec.br_blockcount;
> +		rec->rm_offset += irec.br_blockcount;
> +		rec->rm_blockcount -= irec.br_blockcount;
> +		if (rec->rm_blockcount == 0)
> +			break;
> +		have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec);
> +		if (!have_map)
> +			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> +					rec->rm_offset);
> +	}
> +
> +out:
> +	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> +		return XFS_BTREE_QUERY_RANGE_ABORT;
> +	return 0;
> +}
> +
> +/* Make sure each rmap has a corresponding bmbt entry. */
> +STATIC int
> +xfs_scrub_bmap_check_ag_rmaps(
> +	struct xfs_scrub_context	*sc,
> +	int				whichfork,
> +	xfs_agnumber_t			agno)
> +{
> +	struct xfs_scrub_bmap_check_rmap_info	sbcri;
> +	struct xfs_btree_cur		*cur;
> +	struct xfs_buf			*agf;
> +	int				error;
> +
> +	error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
> +	if (error)
> +		return error;
> +
> +	cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
> +	if (!cur) {
> +		error = -ENOMEM;
> +		goto out_agf;
> +	}
> +
> +	sbcri.sc = sc;
> +	sbcri.whichfork = whichfork;
> +	error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri);
> +	if (error == XFS_BTREE_QUERY_RANGE_ABORT)
> +		error = 0;
> +
> +	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);

Why is XFS_BTREE_ERROR needed?

Brian

> +out_agf:
> +	xfs_trans_brelse(sc->tp, agf);
> +	return error;
> +}
> +
> +/* Make sure each rmap has a corresponding bmbt entry. */
> +STATIC int
> +xfs_scrub_bmap_check_rmaps(
> +	struct xfs_scrub_context	*sc,
> +	int				whichfork)
> +{
> +	loff_t				size;
> +	xfs_agnumber_t			agno;
> +	int				error;
> +
> +	if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
> +	    whichfork == XFS_COW_FORK ||
> +	    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
> +		return 0;
> +
> +	/* Don't support realtime rmap checks yet. */
> +	if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
> +		return 0;
> +
> +	/*
> +	 * Only do this for complex maps that are in btree format, or for
> +	 * situations where we would seem to have a size but zero extents.
> +	 * The inode repair code can zap broken iforks, which means we have
> +	 * to flag this bmap as corrupt if there are rmaps that need to be
> +	 * reattached.
> +	 */
> +	switch (whichfork) {
> +	case XFS_DATA_FORK:
> +		size = i_size_read(VFS_I(sc->ip));
> +		break;
> +	case XFS_ATTR_FORK:
> +		size = XFS_IFORK_Q(sc->ip);
> +		break;
> +	default:
> +		size = 0;
> +		break;
> +	}
> +	if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
> +	    (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
> +		return 0;
> +
> +	for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
> +		error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno);
> +		if (error)
> +			return error;
> +		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> +			break;
> +	}
> +
> +	return 0;
> +}
> +
>  /*
>   * Scrub an inode fork's block mappings.
>   *
> @@ -463,7 +621,7 @@ xfs_scrub_bmap(
>  		break;
>  	case XFS_ATTR_FORK:
>  		if (!ifp)
> -			goto out;
> +			goto out_check_rmap;
>  		if (!xfs_sb_version_hasattr(&mp->m_sb) &&
>  		    !xfs_sb_version_hasattr2(&mp->m_sb))
>  			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
> @@ -534,6 +692,10 @@ xfs_scrub_bmap(
>  			goto out;
>  	}
>  
> +out_check_rmap:
> +	error = xfs_scrub_bmap_check_rmaps(sc, whichfork);
> +	if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error))
> +		goto out;
>  out:
>  	return error;
>  }
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/9] xfs: inode scrubber shouldn't bother with raw checks
  2018-03-15  0:30 ` [PATCH 6/9] xfs: inode scrubber shouldn't bother with raw checks Darrick J. Wong
@ 2018-03-21 17:42   ` Brian Foster
  2018-03-21 20:37     ` Darrick J. Wong
  0 siblings, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-21 17:42 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:30:07PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> The inode scrubber tries to _iget the inode prior to running checks.
> If that _iget call fails with corruption errors that's an automatic
> fail, regardless of whether it was the inode buffer read verifier,
> the ifork verifier, or the ifork formatter that errored out.
> 
> Therefore, get rid of the raw mode scrub code because it's not needed.
> Found by trying to fix some test failures in xfs/379 and xfs/415.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/scrub/inode.c |   98 +++++---------------------------------------------
>  1 file changed, 9 insertions(+), 89 deletions(-)
> 
> 
> diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> index 21297be..0332a01 100644
> --- a/fs/xfs/scrub/inode.c
> +++ b/fs/xfs/scrub/inode.c
> @@ -515,72 +515,6 @@ xfs_scrub_dinode(
>  	}
>  }
>  
> -/* Map and read a raw inode. */
> -STATIC int
> -xfs_scrub_inode_map_raw(
> -	struct xfs_scrub_context	*sc,
> -	xfs_ino_t			ino,
> -	struct xfs_buf			**bpp,
> -	struct xfs_dinode		**dipp)
> -{
> -	struct xfs_imap			imap;
> -	struct xfs_mount		*mp = sc->mp;
> -	struct xfs_buf			*bp = NULL;
> -	struct xfs_dinode		*dip;
> -	int				error;
> -
> -	error = xfs_imap(mp, sc->tp, ino, &imap, XFS_IGET_UNTRUSTED);
> -	if (error == -EINVAL) {
> -		/*
> -		 * Inode could have gotten deleted out from under us;
> -		 * just forget about it.
> -		 */
> -		error = -ENOENT;
> -		goto out;
> -	}
> -	if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
> -			XFS_INO_TO_AGBNO(mp, ino), &error))
> -		goto out;
> -
> -	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
> -			imap.im_blkno, imap.im_len, XBF_UNMAPPED, &bp,
> -			NULL);
> -	if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
> -			XFS_INO_TO_AGBNO(mp, ino), &error))
> -		goto out;
> -
> -	/*
> -	 * Is this really an inode?  We disabled verifiers in the above
> -	 * xfs_trans_read_buf call because the inode buffer verifier
> -	 * fails on /any/ inode record in the inode cluster with a bad
> -	 * magic or version number, not just the one that we're
> -	 * checking.  Therefore, grab the buffer unconditionally, attach
> -	 * the inode verifiers by hand, and run the inode verifier only
> -	 * on the one inode we want.
> -	 */
> -	bp->b_ops = &xfs_inode_buf_ops;
> -	dip = xfs_buf_offset(bp, imap.im_boffset);
> -	if (xfs_dinode_verify(mp, ino, dip) != NULL ||
> -	    !xfs_dinode_good_version(mp, dip->di_version)) {
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> -		goto out_buf;
> -	}
> -
> -	/* ...and is it the one we asked for? */
> -	if (be32_to_cpu(dip->di_gen) != sc->sm->sm_gen) {
> -		error = -ENOENT;
> -		goto out_buf;
> -	}
> -
> -	*dipp = dip;
> -	*bpp = bp;
> -out:
> -	return error;
> -out_buf:
> -	xfs_trans_brelse(sc->tp, bp);
> -	return error;
> -}
> -
>  /*
>   * Make sure the finobt doesn't think this inode is free.
>   * We don't have to check the inobt ourselves because we got the inode via
> @@ -727,43 +661,29 @@ xfs_scrub_inode(
>  	struct xfs_scrub_context	*sc)
>  {
>  	struct xfs_dinode		di;
> -	struct xfs_buf			*bp = NULL;
> -	struct xfs_dinode		*dip;
> -	xfs_ino_t			ino;
>  	int				error = 0;
>  
> -	/* Did we get the in-core inode, or are we doing this manually? */
> -	if (sc->ip) {
> -		ino = sc->ip->i_ino;
> -		xfs_inode_to_disk(sc->ip, &di, 0);
> -		dip = &di;
> -	} else {
> -		/* Map & read inode. */
> -		ino = sc->sm->sm_ino;
> -		error = xfs_scrub_inode_map_raw(sc, ino, &bp, &dip);
> -		if (error || !bp)
> -			goto out;
> +	/* iget failed means automatic fail. */
> +	if (!sc->ip) {
> +		xfs_scrub_ino_set_corrupt(sc, sc->sm->sm_ino, NULL);
> +		return 0;
>  	}

Ok, so the setup function will attempt to lookup the inode number that's
passed in. If the lookup fails with a corruption error, we'd have ip ==
NULL but return 0 from that setup call because it's not a scrub
operational error. Hence, we get here without an inode and call it an fs
corruption. Seems fine as long as I'm following this correctly:

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  
> -	xfs_scrub_dinode(sc, bp, dip, ino);
> +	/* Scrub the inode core. */
> +	xfs_inode_to_disk(sc->ip, &di, 0);
> +	xfs_scrub_dinode(sc, NULL, &di, sc->ip->i_ino);
>  	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
>  		goto out;
>  
> -	/* Now let's do the things that require a live inode. */
> -	if (!sc->ip)
> -		goto out;
> -
>  	/*
>  	 * Look for discrepancies between file's data blocks and the reflink
>  	 * iflag.  We already checked the iflag against the file mode when
>  	 * we scrubbed the dinode.
>  	 */
>  	if (S_ISREG(VFS_I(sc->ip)->i_mode))
> -		xfs_scrub_inode_check_reflink_iflag(sc, ino, bp);
> +		xfs_scrub_inode_check_reflink_iflag(sc, sc->ip->i_ino, NULL);
>  
> -	xfs_scrub_inode_xref(sc, ino, dip);
> +	xfs_scrub_inode_xref(sc, sc->ip->i_ino, &di);
>  out:
> -	if (bp)
> -		xfs_trans_brelse(sc->tp, bp);
>  	return error;
>  }
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 7/9] xfs: remove xfs_buf parameter from inode scrub methods
  2018-03-15  0:30 ` [PATCH 7/9] xfs: remove xfs_buf parameter from inode scrub methods Darrick J. Wong
@ 2018-03-21 17:42   ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-21 17:42 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:30:13PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Now that we no longer do raw inode buffer scrubbing, the bp parameter is
> no longer used anywhere we're dealing with an inode, so remove it and
> all the useless NULL parameters that go with it.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/scrub/attr.c     |    2 -
>  fs/xfs/scrub/bmap.c     |    4 +-
>  fs/xfs/scrub/common.c   |   22 ++++------
>  fs/xfs/scrub/common.h   |   13 ++----
>  fs/xfs/scrub/dir.c      |    2 -
>  fs/xfs/scrub/inode.c    |  106 ++++++++++++++++++++++-------------------------
>  fs/xfs/scrub/quota.c    |    2 -
>  fs/xfs/scrub/rtbitmap.c |    3 -
>  fs/xfs/scrub/trace.h    |   31 ++------------
>  9 files changed, 74 insertions(+), 111 deletions(-)
> 
> 
> diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c
> index 4ed8047..127575f 100644
> --- a/fs/xfs/scrub/attr.c
> +++ b/fs/xfs/scrub/attr.c
> @@ -98,7 +98,7 @@ xfs_scrub_xattr_listent(
>  
>  	if (flags & XFS_ATTR_INCOMPLETE) {
>  		/* Incomplete attr key, just mark the inode for preening. */
> -		xfs_scrub_ino_set_preen(sx->sc, context->dp->i_ino, NULL);
> +		xfs_scrub_ino_set_preen(sx->sc, context->dp->i_ino);
>  		return;
>  	}
>  
> diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> index 2f38add..23556ca 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -615,7 +615,7 @@ xfs_scrub_bmap(
>  			goto out;
>  		/* No CoW forks on non-reflink inodes/filesystems. */
>  		if (!xfs_is_reflink_inode(ip)) {
> -			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
> +			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
>  			goto out;
>  		}
>  		break;
> @@ -624,7 +624,7 @@ xfs_scrub_bmap(
>  			goto out_check_rmap;
>  		if (!xfs_sb_version_hasattr(&mp->m_sb) &&
>  		    !xfs_sb_version_hasattr2(&mp->m_sb))
> -			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
> +			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
>  		break;
>  	default:
>  		ASSERT(whichfork == XFS_DATA_FORK);
> diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
> index 8033ab9..fe3a2b1 100644
> --- a/fs/xfs/scrub/common.c
> +++ b/fs/xfs/scrub/common.c
> @@ -213,12 +213,10 @@ xfs_scrub_block_set_preen(
>  void
>  xfs_scrub_ino_set_preen(
>  	struct xfs_scrub_context	*sc,
> -	xfs_ino_t			ino,
> -	struct xfs_buf			*bp)
> +	xfs_ino_t			ino)
>  {
>  	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN;
> -	trace_xfs_scrub_ino_preen(sc, ino, bp ? bp->b_bn : 0,
> -			__return_address);
> +	trace_xfs_scrub_ino_preen(sc, ino, __return_address);
>  }
>  
>  /* Record a corrupt block. */
> @@ -249,22 +247,20 @@ xfs_scrub_block_xref_set_corrupt(
>  void
>  xfs_scrub_ino_set_corrupt(
>  	struct xfs_scrub_context	*sc,
> -	xfs_ino_t			ino,
> -	struct xfs_buf			*bp)
> +	xfs_ino_t			ino)
>  {
>  	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
> -	trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address);
> +	trace_xfs_scrub_ino_error(sc, ino, __return_address);
>  }
>  
>  /* Record a corruption while cross-referencing with an inode. */
>  void
>  xfs_scrub_ino_xref_set_corrupt(
>  	struct xfs_scrub_context	*sc,
> -	xfs_ino_t			ino,
> -	struct xfs_buf			*bp)
> +	xfs_ino_t			ino)
>  {
>  	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT;
> -	trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address);
> +	trace_xfs_scrub_ino_error(sc, ino, __return_address);
>  }
>  
>  /* Record corruption in a block indexed by a file fork. */
> @@ -296,12 +292,10 @@ xfs_scrub_fblock_xref_set_corrupt(
>  void
>  xfs_scrub_ino_set_warning(
>  	struct xfs_scrub_context	*sc,
> -	xfs_ino_t			ino,
> -	struct xfs_buf			*bp)
> +	xfs_ino_t			ino)
>  {
>  	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING;
> -	trace_xfs_scrub_ino_warning(sc, ino, bp ? bp->b_bn : 0,
> -			__return_address);
> +	trace_xfs_scrub_ino_warning(sc, ino, __return_address);
>  }
>  
>  /* Warn about a block indexed by a file fork that needs review. */
> diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
> index ddb65d2..deaf604 100644
> --- a/fs/xfs/scrub/common.h
> +++ b/fs/xfs/scrub/common.h
> @@ -63,25 +63,22 @@ bool xfs_scrub_fblock_xref_process_error(struct xfs_scrub_context *sc,
>  
>  void xfs_scrub_block_set_preen(struct xfs_scrub_context *sc,
>  		struct xfs_buf *bp);
> -void xfs_scrub_ino_set_preen(struct xfs_scrub_context *sc, xfs_ino_t ino,
> -		struct xfs_buf *bp);
> +void xfs_scrub_ino_set_preen(struct xfs_scrub_context *sc, xfs_ino_t ino);
>  
>  void xfs_scrub_block_set_corrupt(struct xfs_scrub_context *sc,
>  		struct xfs_buf *bp);
> -void xfs_scrub_ino_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino,
> -		struct xfs_buf *bp);
> +void xfs_scrub_ino_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino);
>  void xfs_scrub_fblock_set_corrupt(struct xfs_scrub_context *sc, int whichfork,
>  		xfs_fileoff_t offset);
>  
>  void xfs_scrub_block_xref_set_corrupt(struct xfs_scrub_context *sc,
>  		struct xfs_buf *bp);
> -void xfs_scrub_ino_xref_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino,
> -		struct xfs_buf *bp);
> +void xfs_scrub_ino_xref_set_corrupt(struct xfs_scrub_context *sc,
> +		xfs_ino_t ino);
>  void xfs_scrub_fblock_xref_set_corrupt(struct xfs_scrub_context *sc,
>  		int whichfork, xfs_fileoff_t offset);
>  
> -void xfs_scrub_ino_set_warning(struct xfs_scrub_context *sc, xfs_ino_t ino,
> -		struct xfs_buf *bp);
> +void xfs_scrub_ino_set_warning(struct xfs_scrub_context *sc, xfs_ino_t ino);
>  void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork,
>  		xfs_fileoff_t offset);
>  
> diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
> index 50b6a26..38f2980 100644
> --- a/fs/xfs/scrub/dir.c
> +++ b/fs/xfs/scrub/dir.c
> @@ -781,7 +781,7 @@ xfs_scrub_directory(
>  
>  	/* Plausible size? */
>  	if (sc->ip->i_d.di_size < xfs_dir2_sf_hdr_size(0)) {
> -		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
> +		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
>  		goto out;
>  	}
>  
> diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> index 0332a01..9eadca9 100644
> --- a/fs/xfs/scrub/inode.c
> +++ b/fs/xfs/scrub/inode.c
> @@ -98,7 +98,6 @@ xfs_scrub_setup_inode(
>  STATIC void
>  xfs_scrub_inode_extsize(
>  	struct xfs_scrub_context	*sc,
> -	struct xfs_buf			*bp,
>  	struct xfs_dinode		*dip,
>  	xfs_ino_t			ino,
>  	uint16_t			mode,
> @@ -149,7 +148,7 @@ xfs_scrub_inode_extsize(
>  
>  	return;
>  bad:
> -	xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +	xfs_scrub_ino_set_corrupt(sc, ino);
>  }
>  
>  /*
> @@ -161,7 +160,6 @@ xfs_scrub_inode_extsize(
>  STATIC void
>  xfs_scrub_inode_cowextsize(
>  	struct xfs_scrub_context	*sc,
> -	struct xfs_buf			*bp,
>  	struct xfs_dinode		*dip,
>  	xfs_ino_t			ino,
>  	uint16_t			mode,
> @@ -205,14 +203,13 @@ xfs_scrub_inode_cowextsize(
>  
>  	return;
>  bad:
> -	xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +	xfs_scrub_ino_set_corrupt(sc, ino);
>  }
>  
>  /* Make sure the di_flags make sense for the inode. */
>  STATIC void
>  xfs_scrub_inode_flags(
>  	struct xfs_scrub_context	*sc,
> -	struct xfs_buf			*bp,
>  	struct xfs_dinode		*dip,
>  	xfs_ino_t			ino,
>  	uint16_t			mode,
> @@ -251,14 +248,13 @@ xfs_scrub_inode_flags(
>  
>  	return;
>  bad:
> -	xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +	xfs_scrub_ino_set_corrupt(sc, ino);
>  }
>  
>  /* Make sure the di_flags2 make sense for the inode. */
>  STATIC void
>  xfs_scrub_inode_flags2(
>  	struct xfs_scrub_context	*sc,
> -	struct xfs_buf			*bp,
>  	struct xfs_dinode		*dip,
>  	xfs_ino_t			ino,
>  	uint16_t			mode,
> @@ -295,14 +291,13 @@ xfs_scrub_inode_flags2(
>  
>  	return;
>  bad:
> -	xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +	xfs_scrub_ino_set_corrupt(sc, ino);
>  }
>  
>  /* Scrub all the ondisk inode fields. */
>  STATIC void
>  xfs_scrub_dinode(
>  	struct xfs_scrub_context	*sc,
> -	struct xfs_buf			*bp,
>  	struct xfs_dinode		*dip,
>  	xfs_ino_t			ino)
>  {
> @@ -333,7 +328,7 @@ xfs_scrub_dinode(
>  		/* mode is recognized */
>  		break;
>  	default:
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	}
>  
> @@ -344,22 +339,22 @@ xfs_scrub_dinode(
>  		 * We autoconvert v1 inodes into v2 inodes on writeout,
>  		 * so just mark this inode for preening.
>  		 */
> -		xfs_scrub_ino_set_preen(sc, ino, bp);
> +		xfs_scrub_ino_set_preen(sc, ino);
>  		break;
>  	case 2:
>  	case 3:
>  		if (dip->di_onlink != 0)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  		if (dip->di_mode == 0 && sc->ip)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  		if (dip->di_projid_hi != 0 &&
>  		    !xfs_sb_version_hasprojid32bit(&mp->m_sb))
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	default:
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  		return;
>  	}
>  
> @@ -369,40 +364,40 @@ xfs_scrub_dinode(
>  	 */
>  	if (dip->di_uid == cpu_to_be32(-1U) ||
>  	    dip->di_gid == cpu_to_be32(-1U))
> -		xfs_scrub_ino_set_warning(sc, ino, bp);
> +		xfs_scrub_ino_set_warning(sc, ino);
>  
>  	/* di_format */
>  	switch (dip->di_format) {
>  	case XFS_DINODE_FMT_DEV:
>  		if (!S_ISCHR(mode) && !S_ISBLK(mode) &&
>  		    !S_ISFIFO(mode) && !S_ISSOCK(mode))
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	case XFS_DINODE_FMT_LOCAL:
>  		if (!S_ISDIR(mode) && !S_ISLNK(mode))
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	case XFS_DINODE_FMT_EXTENTS:
>  		if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode))
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	case XFS_DINODE_FMT_BTREE:
>  		if (!S_ISREG(mode) && !S_ISDIR(mode))
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	case XFS_DINODE_FMT_UUID:
>  	default:
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	}
>  
>  	/* di_[amc]time.nsec */
>  	if (be32_to_cpu(dip->di_atime.t_nsec) >= NSEC_PER_SEC)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  	if (be32_to_cpu(dip->di_mtime.t_nsec) >= NSEC_PER_SEC)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  	if (be32_to_cpu(dip->di_ctime.t_nsec) >= NSEC_PER_SEC)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  	/*
>  	 * di_size.  xfs_dinode_verify checks for things that screw up
> @@ -411,19 +406,19 @@ xfs_scrub_dinode(
>  	 */
>  	isize = be64_to_cpu(dip->di_size);
>  	if (isize & (1ULL << 63))
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  	/* Devices, fifos, and sockets must have zero size */
>  	if (!S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode) && isize != 0)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  	/* Directories can't be larger than the data section size (32G) */
>  	if (S_ISDIR(mode) && (isize == 0 || isize >= XFS_DIR2_SPACE_SIZE))
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  	/* Symlinks can't be larger than SYMLINK_MAXLEN */
>  	if (S_ISLNK(mode) && (isize == 0 || isize >= XFS_SYMLINK_MAXLEN))
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  	/*
>  	 * Warn if the running kernel can't handle the kinds of offsets
> @@ -432,7 +427,7 @@ xfs_scrub_dinode(
>  	 * overly large offsets, flag the inode for admin review.
>  	 */
>  	if (isize >= mp->m_super->s_maxbytes)
> -		xfs_scrub_ino_set_warning(sc, ino, bp);
> +		xfs_scrub_ino_set_warning(sc, ino);
>  
>  	/* di_nblocks */
>  	if (flags2 & XFS_DIFLAG2_REFLINK) {
> @@ -447,15 +442,15 @@ xfs_scrub_dinode(
>  		 */
>  		if (be64_to_cpu(dip->di_nblocks) >=
>  		    mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  	} else {
>  		if (be64_to_cpu(dip->di_nblocks) >= mp->m_sb.sb_dblocks)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  	}
>  
> -	xfs_scrub_inode_flags(sc, bp, dip, ino, mode, flags);
> +	xfs_scrub_inode_flags(sc, dip, ino, mode, flags);
>  
> -	xfs_scrub_inode_extsize(sc, bp, dip, ino, mode, flags);
> +	xfs_scrub_inode_extsize(sc, dip, ino, mode, flags);
>  
>  	/* di_nextents */
>  	nextents = be32_to_cpu(dip->di_nextents);
> @@ -463,31 +458,31 @@ xfs_scrub_dinode(
>  	switch (dip->di_format) {
>  	case XFS_DINODE_FMT_EXTENTS:
>  		if (nextents > fork_recs)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	case XFS_DINODE_FMT_BTREE:
>  		if (nextents <= fork_recs)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	default:
>  		if (nextents != 0)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	}
>  
>  	/* di_forkoff */
>  	if (XFS_DFORK_APTR(dip) >= (char *)dip + mp->m_sb.sb_inodesize)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  	if (dip->di_anextents != 0 && dip->di_forkoff == 0)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  	if (dip->di_forkoff == 0 && dip->di_aformat != XFS_DINODE_FMT_EXTENTS)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  	/* di_aformat */
>  	if (dip->di_aformat != XFS_DINODE_FMT_LOCAL &&
>  	    dip->di_aformat != XFS_DINODE_FMT_EXTENTS &&
>  	    dip->di_aformat != XFS_DINODE_FMT_BTREE)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  
>  	/* di_anextents */
>  	nextents = be16_to_cpu(dip->di_anextents);
> @@ -495,22 +490,22 @@ xfs_scrub_dinode(
>  	switch (dip->di_aformat) {
>  	case XFS_DINODE_FMT_EXTENTS:
>  		if (nextents > fork_recs)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	case XFS_DINODE_FMT_BTREE:
>  		if (nextents <= fork_recs)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  		break;
>  	default:
>  		if (nextents != 0)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +			xfs_scrub_ino_set_corrupt(sc, ino);
>  	}
>  
>  	if (dip->di_version >= 3) {
>  		if (be32_to_cpu(dip->di_crtime.t_nsec) >= NSEC_PER_SEC)
> -			xfs_scrub_ino_set_corrupt(sc, ino, bp);
> -		xfs_scrub_inode_flags2(sc, bp, dip, ino, mode, flags, flags2);
> -		xfs_scrub_inode_cowextsize(sc, bp, dip, ino, mode, flags,
> +			xfs_scrub_ino_set_corrupt(sc, ino);
> +		xfs_scrub_inode_flags2(sc, dip, ino, mode, flags, flags2);
> +		xfs_scrub_inode_cowextsize(sc, dip, ino, mode, flags,
>  				flags2);
>  	}
>  }
> @@ -579,18 +574,18 @@ xfs_scrub_inode_xref_bmap(
>  	if (!xfs_scrub_should_check_xref(sc, &error, NULL))
>  		return;
>  	if (nextents < be32_to_cpu(dip->di_nextents))
> -		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
> +		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>  
>  	error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
>  			&nextents, &acount);
>  	if (!xfs_scrub_should_check_xref(sc, &error, NULL))
>  		return;
>  	if (nextents != be16_to_cpu(dip->di_anextents))
> -		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
> +		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>  
>  	/* Check nblocks against the inode. */
>  	if (count + acount != be64_to_cpu(dip->di_nblocks))
> -		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL);
> +		xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>  }
>  
>  /* Cross-reference with the other btrees. */
> @@ -634,8 +629,7 @@ xfs_scrub_inode_xref(
>  static void
>  xfs_scrub_inode_check_reflink_iflag(
>  	struct xfs_scrub_context	*sc,
> -	xfs_ino_t			ino,
> -	struct xfs_buf			*bp)
> +	xfs_ino_t			ino)
>  {
>  	struct xfs_mount		*mp = sc->mp;
>  	bool				has_shared;
> @@ -650,9 +644,9 @@ xfs_scrub_inode_check_reflink_iflag(
>  			XFS_INO_TO_AGBNO(mp, ino), &error))
>  		return;
>  	if (xfs_is_reflink_inode(sc->ip) && !has_shared)
> -		xfs_scrub_ino_set_preen(sc, ino, bp);
> +		xfs_scrub_ino_set_preen(sc, ino);
>  	else if (!xfs_is_reflink_inode(sc->ip) && has_shared)
> -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  }
>  
>  /* Scrub an inode. */
> @@ -665,13 +659,13 @@ xfs_scrub_inode(
>  
>  	/* iget failed means automatic fail. */
>  	if (!sc->ip) {
> -		xfs_scrub_ino_set_corrupt(sc, sc->sm->sm_ino, NULL);
> +		xfs_scrub_ino_set_corrupt(sc, sc->sm->sm_ino);
>  		return 0;
>  	}
>  
>  	/* Scrub the inode core. */
>  	xfs_inode_to_disk(sc->ip, &di, 0);
> -	xfs_scrub_dinode(sc, NULL, &di, sc->ip->i_ino);
> +	xfs_scrub_dinode(sc, &di, sc->ip->i_ino);
>  	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
>  		goto out;
>  
> @@ -681,7 +675,7 @@ xfs_scrub_inode(
>  	 * we scrubbed the dinode.
>  	 */
>  	if (S_ISREG(VFS_I(sc->ip)->i_mode))
> -		xfs_scrub_inode_check_reflink_iflag(sc, sc->ip->i_ino, NULL);
> +		xfs_scrub_inode_check_reflink_iflag(sc, sc->ip->i_ino);
>  
>  	xfs_scrub_inode_xref(sc, sc->ip->i_ino, &di);
>  out:
> diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c
> index 51daa4a..6ba465e 100644
> --- a/fs/xfs/scrub/quota.c
> +++ b/fs/xfs/scrub/quota.c
> @@ -219,7 +219,7 @@ xfs_scrub_quota(
>  	/* Look for problem extents. */
>  	xfs_ilock(ip, XFS_ILOCK_EXCL);
>  	if (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) {
> -		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
> +		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
>  		goto out_unlock_inode;
>  	}
>  	max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
> diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c
> index 2639099..39c41dfe 100644
> --- a/fs/xfs/scrub/rtbitmap.c
> +++ b/fs/xfs/scrub/rtbitmap.c
> @@ -116,8 +116,7 @@ xfs_scrub_xref_is_used_rt_space(
>  	if (!xfs_scrub_should_check_xref(sc, &error, NULL))
>  		goto out_unlock;
>  	if (is_free)
> -		xfs_scrub_ino_xref_set_corrupt(sc, sc->mp->m_rbmip->i_ino,
> -				NULL);
> +		xfs_scrub_ino_xref_set_corrupt(sc, sc->mp->m_rbmip->i_ino);
>  out_unlock:
>  	xfs_iunlock(sc->mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP);
>  }
> diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
> index 4dc8968..5d2b1c2 100644
> --- a/fs/xfs/scrub/trace.h
> +++ b/fs/xfs/scrub/trace.h
> @@ -174,53 +174,32 @@ DEFINE_SCRUB_BLOCK_ERROR_EVENT(xfs_scrub_block_error);
>  DEFINE_SCRUB_BLOCK_ERROR_EVENT(xfs_scrub_block_preen);
>  
>  DECLARE_EVENT_CLASS(xfs_scrub_ino_error_class,
> -	TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, xfs_daddr_t daddr,
> -		 void *ret_ip),
> -	TP_ARGS(sc, ino, daddr, ret_ip),
> +	TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, void *ret_ip),
> +	TP_ARGS(sc, ino, ret_ip),
>  	TP_STRUCT__entry(
>  		__field(dev_t, dev)
>  		__field(xfs_ino_t, ino)
>  		__field(unsigned int, type)
> -		__field(xfs_agnumber_t, agno)
> -		__field(xfs_agblock_t, bno)
>  		__field(void *, ret_ip)
>  	),
>  	TP_fast_assign(
> -		xfs_fsblock_t	fsbno;
> -		xfs_agnumber_t	agno;
> -		xfs_agblock_t	bno;
> -
> -		if (daddr) {
> -			fsbno = XFS_DADDR_TO_FSB(sc->mp, daddr);
> -			agno = XFS_FSB_TO_AGNO(sc->mp, fsbno);
> -			bno = XFS_FSB_TO_AGBNO(sc->mp, fsbno);
> -		} else {
> -			agno = XFS_INO_TO_AGNO(sc->mp, ino);
> -			bno = XFS_AGINO_TO_AGBNO(sc->mp,
> -					XFS_INO_TO_AGINO(sc->mp, ino));
> -		}
> -
>  		__entry->dev = sc->mp->m_super->s_dev;
>  		__entry->ino = ino;
>  		__entry->type = sc->sm->sm_type;
> -		__entry->agno = agno;
> -		__entry->bno = bno;
>  		__entry->ret_ip = ret_ip;
>  	),
> -	TP_printk("dev %d:%d ino 0x%llx type %u agno %u agbno %u ret_ip %pS",
> +	TP_printk("dev %d:%d ino 0x%llx type %u ret_ip %pS",
>  		  MAJOR(__entry->dev), MINOR(__entry->dev),
>  		  __entry->ino,
>  		  __entry->type,
> -		  __entry->agno,
> -		  __entry->bno,
>  		  __entry->ret_ip)
>  )
>  
>  #define DEFINE_SCRUB_INO_ERROR_EVENT(name) \
>  DEFINE_EVENT(xfs_scrub_ino_error_class, name, \
>  	TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, \
> -		 xfs_daddr_t daddr, void *ret_ip), \
> -	TP_ARGS(sc, ino, daddr, ret_ip))
> +		 void *ret_ip), \
> +	TP_ARGS(sc, ino, ret_ip))
>  
>  DEFINE_SCRUB_INO_ERROR_EVENT(xfs_scrub_ino_error);
>  DEFINE_SCRUB_INO_ERROR_EVENT(xfs_scrub_ino_preen);
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber
  2018-03-15  0:30 ` [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber Darrick J. Wong
@ 2018-03-21 17:42   ` Brian Foster
  2018-03-21 20:50     ` Darrick J. Wong
  2018-03-22  6:24   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-21 17:42 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:30:20PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> During the inode btree scrubs we try to confirm the freemask bits
> against the inode records.  If the inode buffer read fails, this is a
> cross-referencing error, not a corruption of the inode btree itself.
> Use the xref_process_error call here.  Found via core.version middlebit
> fuzz in xfs/415.
> 

What's the difference/consequence of this change?

Brian

> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/scrub/ialloc.c |    3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> 
> diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> index 63ab3f9..32e0d1a 100644
> --- a/fs/xfs/scrub/ialloc.c
> +++ b/fs/xfs/scrub/ialloc.c
> @@ -259,7 +259,8 @@ xfs_scrub_iallocbt_check_freemask(
>  
>  		error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap,
>  				&dip, &bp, 0, 0);
> -		if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error))
> +		if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur, 0,
> +				&error))
>  			continue;
>  
>  		/* Which inodes are free? */
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 9/9] xfs: move inode extent size hint validation to libxfs
  2018-03-15  0:30 ` [PATCH 9/9] xfs: move inode extent size hint validation to libxfs Darrick J. Wong
@ 2018-03-21 17:42   ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-21 17:42 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 14, 2018 at 05:30:28PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Extent size hint validation is used by scrub to decide if there's an
> error, and it will be used by repair to decide to remove the hint.
> Since these use the same validation functions, move them to libxfs.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_inode_buf.c |  105 +++++++++++++++++++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_inode_buf.h |    5 ++
>  fs/xfs/scrub/inode.c          |  100 +++++----------------------------------
>  3 files changed, 122 insertions(+), 88 deletions(-)
> 
> 
> diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> index 51019e5..cdd4c1d 100644
> --- a/fs/xfs/libxfs/xfs_inode_buf.c
> +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> @@ -651,3 +651,108 @@ xfs_iread(
>  	xfs_trans_brelse(tp, bp);
>  	return error;
>  }
> +
> +/*
> + * Validate di_extsize hint.
> + *
> + * The rules are documented at xfs_ioctl_setattr_check_extsize().
> + * These functions must be kept in sync with each other.
> + */
> +xfs_failaddr_t
> +xfs_inode_validate_extsize(
> +	struct xfs_mount		*mp,
> +	uint32_t			extsize,
> +	uint16_t			mode,
> +	uint16_t			flags)
> +{
> +	bool				rt_flag;
> +	bool				hint_flag;
> +	bool				inherit_flag;
> +	uint32_t			extsize_bytes;
> +	uint32_t			blocksize_bytes;
> +
> +	rt_flag = (flags & XFS_DIFLAG_REALTIME);
> +	hint_flag = (flags & XFS_DIFLAG_EXTSIZE);
> +	inherit_flag = (flags & XFS_DIFLAG_EXTSZINHERIT);
> +	extsize_bytes = XFS_FSB_TO_B(mp, extsize);
> +
> +	if (rt_flag)
> +		blocksize_bytes = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
> +	else
> +		blocksize_bytes = mp->m_sb.sb_blocksize;
> +
> +	if ((hint_flag || inherit_flag) && !(S_ISDIR(mode) || S_ISREG(mode)))
> +		return __this_address;
> +
> +	if (hint_flag && !S_ISREG(mode))
> +		return __this_address;
> +
> +	if (inherit_flag && !S_ISDIR(mode))
> +		return __this_address;
> +
> +	if ((hint_flag || inherit_flag) && extsize == 0)
> +		return __this_address;
> +
> +	if (!(hint_flag || inherit_flag) && extsize != 0)
> +		return __this_address;
> +
> +	if (extsize_bytes % blocksize_bytes)
> +		return __this_address;
> +
> +	if (extsize > MAXEXTLEN)
> +		return __this_address;
> +
> +	if (!rt_flag && extsize > mp->m_sb.sb_agblocks / 2)
> +		return __this_address;
> +
> +	return NULL;
> +}
> +
> +/*
> + * Validate di_cowextsize hint.
> + *
> + * The rules are documented at xfs_ioctl_setattr_check_cowextsize().
> + * These functions must be kept in sync with each other.
> + */
> +xfs_failaddr_t
> +xfs_inode_validate_cowextsize(
> +	struct xfs_mount		*mp,
> +	uint32_t			cowextsize,
> +	uint16_t			mode,
> +	uint16_t			flags,
> +	uint64_t			flags2)
> +{
> +	bool				rt_flag;
> +	bool				hint_flag;
> +	uint32_t			cowextsize_bytes;
> +
> +	rt_flag = (flags & XFS_DIFLAG_REALTIME);
> +	hint_flag = (flags2 & XFS_DIFLAG2_COWEXTSIZE);
> +	cowextsize_bytes = XFS_FSB_TO_B(mp, cowextsize);
> +
> +	if (hint_flag && !xfs_sb_version_hasreflink(&mp->m_sb))
> +		return __this_address;
> +
> +	if (hint_flag && !(S_ISDIR(mode) || S_ISREG(mode)))
> +		return __this_address;
> +
> +	if (hint_flag && cowextsize == 0)
> +		return __this_address;
> +
> +	if (!hint_flag && cowextsize != 0)
> +		return __this_address;
> +
> +	if (hint_flag && rt_flag)
> +		return __this_address;
> +
> +	if (cowextsize_bytes % mp->m_sb.sb_blocksize)
> +		return __this_address;
> +
> +	if (cowextsize > MAXEXTLEN)
> +		return __this_address;
> +
> +	if (cowextsize > mp->m_sb.sb_agblocks / 2)
> +		return __this_address;
> +
> +	return NULL;
> +}
> diff --git a/fs/xfs/libxfs/xfs_inode_buf.h b/fs/xfs/libxfs/xfs_inode_buf.h
> index 8a5e1da..d9a376a 100644
> --- a/fs/xfs/libxfs/xfs_inode_buf.h
> +++ b/fs/xfs/libxfs/xfs_inode_buf.h
> @@ -84,5 +84,10 @@ void	xfs_inobp_check(struct xfs_mount *, struct xfs_buf *);
>  
>  xfs_failaddr_t xfs_dinode_verify(struct xfs_mount *mp, xfs_ino_t ino,
>  			   struct xfs_dinode *dip);
> +xfs_failaddr_t xfs_inode_validate_extsize(struct xfs_mount *mp,
> +		uint32_t extsize, uint16_t mode, uint16_t flags);
> +xfs_failaddr_t xfs_inode_validate_cowextsize(struct xfs_mount *mp,
> +		uint32_t cowextsize, uint16_t mode, uint16_t flags,
> +		uint64_t flags2);
>  
>  #endif	/* __XFS_INODE_BUF_H__ */
> diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> index 9eadca9..25bca48 100644
> --- a/fs/xfs/scrub/inode.c
> +++ b/fs/xfs/scrub/inode.c
> @@ -89,12 +89,7 @@ xfs_scrub_setup_inode(
>  
>  /* Inode core */
>  
> -/*
> - * Validate di_extsize hint.
> - *
> - * The rules are documented at xfs_ioctl_setattr_check_extsize().
> - * These functions must be kept in sync with each other.
> - */
> +/* Validate di_extsize hint. */
>  STATIC void
>  xfs_scrub_inode_extsize(
>  	struct xfs_scrub_context	*sc,
> @@ -103,52 +98,12 @@ xfs_scrub_inode_extsize(
>  	uint16_t			mode,
>  	uint16_t			flags)
>  {
> -	struct xfs_mount		*mp = sc->mp;
> -	bool				rt_flag;
> -	bool				hint_flag;
> -	bool				inherit_flag;
> -	uint32_t			extsize;
> -	uint32_t			extsize_bytes;
> -	uint32_t			blocksize_bytes;
> -
> -	rt_flag = (flags & XFS_DIFLAG_REALTIME);
> -	hint_flag = (flags & XFS_DIFLAG_EXTSIZE);
> -	inherit_flag = (flags & XFS_DIFLAG_EXTSZINHERIT);
> -	extsize = be32_to_cpu(dip->di_extsize);
> -	extsize_bytes = XFS_FSB_TO_B(sc->mp, extsize);
> -
> -	if (rt_flag)
> -		blocksize_bytes = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
> -	else
> -		blocksize_bytes = mp->m_sb.sb_blocksize;
> -
> -	if ((hint_flag || inherit_flag) && !(S_ISDIR(mode) || S_ISREG(mode)))
> -		goto bad;
> -
> -	if (hint_flag && !S_ISREG(mode))
> -		goto bad;
> -
> -	if (inherit_flag && !S_ISDIR(mode))
> -		goto bad;
> +	xfs_failaddr_t			fa;
>  
> -	if ((hint_flag || inherit_flag) && extsize == 0)
> -		goto bad;
> -
> -	if (!(hint_flag || inherit_flag) && extsize != 0)
> -		goto bad;
> -
> -	if (extsize_bytes % blocksize_bytes)
> -		goto bad;
> -
> -	if (extsize > MAXEXTLEN)
> -		goto bad;
> -
> -	if (!rt_flag && extsize > mp->m_sb.sb_agblocks / 2)
> -		goto bad;
> -
> -	return;
> -bad:
> -	xfs_scrub_ino_set_corrupt(sc, ino);
> +	fa = xfs_inode_validate_extsize(sc->mp, be32_to_cpu(dip->di_extsize),
> +			mode, flags);
> +	if (fa)
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  }
>  
>  /*
> @@ -166,44 +121,13 @@ xfs_scrub_inode_cowextsize(
>  	uint16_t			flags,
>  	uint64_t			flags2)
>  {
> -	struct xfs_mount		*mp = sc->mp;
> -	bool				rt_flag;
> -	bool				hint_flag;
> -	uint32_t			extsize;
> -	uint32_t			extsize_bytes;
> -
> -	rt_flag = (flags & XFS_DIFLAG_REALTIME);
> -	hint_flag = (flags2 & XFS_DIFLAG2_COWEXTSIZE);
> -	extsize = be32_to_cpu(dip->di_cowextsize);
> -	extsize_bytes = XFS_FSB_TO_B(sc->mp, extsize);
> -
> -	if (hint_flag && !xfs_sb_version_hasreflink(&mp->m_sb))
> -		goto bad;
> -
> -	if (hint_flag && !(S_ISDIR(mode) || S_ISREG(mode)))
> -		goto bad;
> -
> -	if (hint_flag && extsize == 0)
> -		goto bad;
> -
> -	if (!hint_flag && extsize != 0)
> -		goto bad;
> +	xfs_failaddr_t			fa;
>  
> -	if (hint_flag && rt_flag)
> -		goto bad;
> -
> -	if (extsize_bytes % mp->m_sb.sb_blocksize)
> -		goto bad;
> -
> -	if (extsize > MAXEXTLEN)
> -		goto bad;
> -
> -	if (extsize > mp->m_sb.sb_agblocks / 2)
> -		goto bad;
> -
> -	return;
> -bad:
> -	xfs_scrub_ino_set_corrupt(sc, ino);
> +	fa = xfs_inode_validate_cowextsize(sc->mp,
> +			be32_to_cpu(dip->di_cowextsize), mode, flags,
> +			flags2);
> +	if (fa)
> +		xfs_scrub_ino_set_corrupt(sc, ino);
>  }
>  
>  /* Make sure the di_flags make sense for the inode. */
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 10/9] xfs: don't accept inode buffers with suspicious unlinked chains
  2018-03-21  3:21 ` [PATCH 10/9] xfs: don't accept inode buffers with suspicious unlinked chains Darrick J. Wong
@ 2018-03-21 17:43   ` Brian Foster
  2018-03-21 20:52     ` Darrick J. Wong
  2018-03-22  6:08   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-21 17:43 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Mar 20, 2018 at 08:21:36PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> When we're verifying inode buffers, sanity-check the unlinked pointer.
> We don't want to run the risk of trying to purge something that's
> obviously broken.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---

FYI this doesn't seem to apply to for-next (HEAD is commit
df79b81b2e)...

Brian

>  fs/xfs/libxfs/xfs_inode_buf.c |   10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> index 4fe17b3..f216d5b 100644
> --- a/fs/xfs/libxfs/xfs_inode_buf.c
> +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> @@ -93,20 +93,26 @@ xfs_inode_buf_verify(
>  	bool		readahead)
>  {
>  	struct xfs_mount *mp = bp->b_target->bt_mount;
> +	xfs_agnumber_t	agno;
>  	int		i;
>  	int		ni;
>  
>  	/*
>  	 * Validate the magic number and version of every inode in the buffer
>  	 */
> +	agno = xfs_daddr_to_agno(mp, XFS_BUF_ADDR(bp));
>  	ni = XFS_BB_TO_FSB(mp, bp->b_length) * mp->m_sb.sb_inopblock;
>  	for (i = 0; i < ni; i++) {
>  		int		di_ok;
>  		xfs_dinode_t	*dip;
> +		xfs_agino_t	unlinked_ino;
>  
>  		dip = xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog));
> +		unlinked_ino = be32_to_cpu(dip->di_next_unlinked);
>  		di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) &&
> -			xfs_dinode_good_version(mp, dip->di_version);
> +			xfs_dinode_good_version(mp, dip->di_version) &&
> +			(unlinked_ino == NULLAGINO ||
> +			 xfs_verify_agino(mp, agno, unlinked_ino));
>  		if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
>  						XFS_ERRTAG_ITOBP_INOTOBP))) {
>  			if (readahead) {
> @@ -122,9 +128,9 @@ xfs_inode_buf_verify(
>  				(unsigned long long)bp->b_bn, i,
>  				be16_to_cpu(dip->di_magic));
>  #endif
> +			return;
>  		}
>  	}
> -	xfs_inobp_check(mp, bp);
>  }
>  
>  
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-21 13:52   ` Brian Foster
@ 2018-03-21 17:44     ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21 17:44 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 09:52:33AM -0400, Brian Foster wrote:
> On Wed, Mar 14, 2018 at 05:29:36PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
> > it doesn't make sense.  Since a carefully crafted fuzzed image can cause
> > the kernel to crash after blowing a bunch of assertions, let's move
> > those checks into a validator function and rig everything up to return
> > EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
> > xfs/391.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_dir2.h       |    2 +
> >  fs/xfs/libxfs/xfs_dir2_block.c |   38 ++++++++++++-------
> >  fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
> >  fs/xfs/libxfs/xfs_dir2_leaf.c  |    6 +++
> >  fs/xfs/libxfs/xfs_dir2_node.c  |    4 ++
> >  5 files changed, 93 insertions(+), 35 deletions(-)
> > 
> > 
> > diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
> > index 388d67c..989e95a 100644
> > --- a/fs/xfs/libxfs/xfs_dir2.h
> > +++ b/fs/xfs/libxfs/xfs_dir2.h
> > @@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
> >  extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
> >  		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
> >  		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
> > -extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
> > +extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
> >  		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
> >  		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
> >  		int *needlogp, int *needscanp);
> > diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
> > index 2da86a3..5726402 100644
> > --- a/fs/xfs/libxfs/xfs_dir2_block.c
> > +++ b/fs/xfs/libxfs/xfs_dir2_block.c
> > @@ -454,12 +454,15 @@ xfs_dir2_block_addname(
> >  		/*
> >  		 * Mark the space needed for the new leaf entry, now in use.
> >  		 */
> > -		xfs_dir2_data_use_free(args, bp, enddup,
> > +		error = xfs_dir2_data_use_free(args, bp, enddup,
> >  			(xfs_dir2_data_aoff_t)
> >  			((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) -
> >  			 sizeof(*blp)),
> >  			(xfs_dir2_data_aoff_t)sizeof(*blp),
> >  			&needlog, &needscan);
> 
> Parameter alignment looks screwed up on many of these updated calls
> throughout.

Yeah, they're a mess.  As usual I tried to start with the smallest
possible fix, but ugh this does scream for some cleaning.

> > +		if (error)
> > +			return error;
> > +
> >  		/*
> >  		 * Update the tail (entry count).
> >  		 */
> ...
> > diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
> > index 0839ffe..641b352 100644
> > --- a/fs/xfs/libxfs/xfs_dir2_node.c
> > +++ b/fs/xfs/libxfs/xfs_dir2_node.c
> > @@ -2023,9 +2023,11 @@ xfs_dir2_node_addname_int(
> >  	/*
> >  	 * Mark the first part of the unused space, inuse for us.
> >  	 */
> > -	xfs_dir2_data_use_free(args, dbp, dup,
> > +	error = xfs_dir2_data_use_free(args, dbp, dup,
> >  		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
> >  		&needlog, &needscan);
> > +	if (error)
> > +		return error;
> 
> Do we have to deal with dbp here?

It's bjoined to the transaction, so dbp should be freed when the caller
cancels the transaction after we error out.  OTOH it doesn't hurt to
try to release the buffer early.

--D

> Brian
> 
> >  	/*
> >  	 * Fill in the new entry and log it.
> >  	 */
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 4/9] xfs: refactor inode buffer verifier error logging
  2018-03-21 13:55   ` Brian Foster
@ 2018-03-21 18:03     ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21 18:03 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 09:55:30AM -0400, Brian Foster wrote:
> On Wed, Mar 14, 2018 at 05:29:54PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > When the inode buffer verifier encounters an error, it's much more
> > helpful to print a buffer from the offending inode instead of just the
> > start of the inode chunk buffer.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_inode_buf.c |    4 +++-
> >  fs/xfs/xfs_error.c            |   29 ++++++++++++++++++++++++-----
> >  fs/xfs/xfs_error.h            |    3 +++
> >  3 files changed, 30 insertions(+), 6 deletions(-)
> > 
> > 
> > diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> > index 4fe17b3..51019e5 100644
> > --- a/fs/xfs/libxfs/xfs_inode_buf.c
> > +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> > @@ -115,13 +115,15 @@ xfs_inode_buf_verify(
> >  				return;
> >  			}
> >  
> > -			xfs_verifier_error(bp, -EFSCORRUPTED, __this_address);
> >  #ifdef DEBUG
> >  			xfs_alert(mp,
> >  				"bad inode magic/vsn daddr %lld #%d (magic=%x)",
> >  				(unsigned long long)bp->b_bn, i,
> >  				be16_to_cpu(dip->di_magic));
> >  #endif
> > +			xfs_buf_verifier_error(bp, -EFSCORRUPTED,
> > +					__func__, dip, sizeof(*dip),
> > +					NULL);
> 
> Why not continue to pass __this_address? Otherwise looks fine:

On the xfsprogs side it causes weird compilation problems on old gcc,
and since a NULL failaddr maps to __return_address inside
xfs_buf_verifier_error we'll get the desired effect anyway.

--D

> Reviewed-by: Brian Foster <bfoster@redhat.com>
> 
> >  		}
> >  	}
> >  	xfs_inobp_check(mp, bp);
> > diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
> > index ccf520f..a63f508 100644
> > --- a/fs/xfs/xfs_error.c
> > +++ b/fs/xfs/xfs_error.c
> > @@ -347,27 +347,32 @@ xfs_corruption_error(
> >   * values, and omit the stack trace unless the error level is tuned high.
> >   */
> >  void
> > -xfs_verifier_error(
> > +xfs_buf_verifier_error(
> >  	struct xfs_buf		*bp,
> >  	int			error,
> > +	const char		*name,
> > +	void			*buf,
> > +	size_t			bufsz,
> >  	xfs_failaddr_t		failaddr)
> >  {
> >  	struct xfs_mount	*mp = bp->b_target->bt_mount;
> >  	xfs_failaddr_t		fa;
> > +	int			sz;
> >  
> >  	fa = failaddr ? failaddr : __return_address;
> >  	__xfs_buf_ioerror(bp, error, fa);
> >  
> > -	xfs_alert(mp, "Metadata %s detected at %pS, %s block 0x%llx",
> > +	xfs_alert(mp, "Metadata %s detected at %pS, %s block 0x%llx %s",
> >  		  bp->b_error == -EFSBADCRC ? "CRC error" : "corruption",
> > -		  fa, bp->b_ops->name, bp->b_bn);
> > +		  fa, bp->b_ops->name, bp->b_bn, name);
> >  
> >  	xfs_alert(mp, "Unmount and run xfs_repair");
> >  
> >  	if (xfs_error_level >= XFS_ERRLEVEL_LOW) {
> > +		sz = min_t(size_t, XFS_CORRUPTION_DUMP_LEN, bufsz);
> >  		xfs_alert(mp, "First %d bytes of corrupted metadata buffer:",
> > -				XFS_CORRUPTION_DUMP_LEN);
> > -		xfs_hex_dump(xfs_buf_offset(bp, 0), XFS_CORRUPTION_DUMP_LEN);
> > +				sz);
> > +		xfs_hex_dump(buf, sz);
> >  	}
> >  
> >  	if (xfs_error_level >= XFS_ERRLEVEL_HIGH)
> > @@ -375,6 +380,20 @@ xfs_verifier_error(
> >  }
> >  
> >  /*
> > + * Warnings specifically for verifier errors.  Differentiate CRC vs. invalid
> > + * values, and omit the stack trace unless the error level is tuned high.
> > + */
> > +void
> > +xfs_verifier_error(
> > +	struct xfs_buf		*bp,
> > +	int			error,
> > +	xfs_failaddr_t		failaddr)
> > +{
> > +	return xfs_buf_verifier_error(bp, error, "", xfs_buf_offset(bp, 0),
> > +			XFS_CORRUPTION_DUMP_LEN, failaddr);
> > +}
> > +
> > +/*
> >   * Warnings for inode corruption problems.  Don't bother with the stack
> >   * trace unless the error level is turned up high.
> >   */
> > diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h
> > index 7e728c5..ce39134 100644
> > --- a/fs/xfs/xfs_error.h
> > +++ b/fs/xfs/xfs_error.h
> > @@ -26,6 +26,9 @@ extern void xfs_error_report(const char *tag, int level, struct xfs_mount *mp,
> >  extern void xfs_corruption_error(const char *tag, int level,
> >  			struct xfs_mount *mp, void *p, const char *filename,
> >  			int linenum, xfs_failaddr_t failaddr);
> > +extern void xfs_buf_verifier_error(struct xfs_buf *bp, int error,
> > +			const char *name, void *buf, size_t bufsz,
> > +			xfs_failaddr_t failaddr);
> >  extern void xfs_verifier_error(struct xfs_buf *bp, int error,
> >  			xfs_failaddr_t failaddr);
> >  extern void xfs_inode_verifier_error(struct xfs_inode *ip, int error,
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files
  2018-03-21 17:42   ` Brian Foster
@ 2018-03-21 18:11     ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21 18:11 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 01:42:32PM -0400, Brian Foster wrote:
> On Wed, Mar 14, 2018 at 05:30:00PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > When we're scanning an extent mapping inode fork, ensure that every rmap
> > record for this ifork has a corresponding bmbt record too.  This
> > (mostly) provides the ability to cross-reference rmap records with bmap
> > data.  The rmap scrubber cannot do the xref on its own because that
> > requires taking an ilock with the agf lock held, which violates our
> > locking order rules (inode, then agf).
> > 
> > Note that we only do this for forks that are in btree format due to the
> > increased complexity; or forks that should have data but suspiciously
> > have zero extents because the inode could have just had its iforks
> > zapped by the inode repair code and now we need to reclaim the old
> > extents.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/scrub/bmap.c |  164 +++++++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 163 insertions(+), 1 deletion(-)
> > 
> > 
> > diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> > index d002821..2f38add 100644
> > --- a/fs/xfs/scrub/bmap.c
> > +++ b/fs/xfs/scrub/bmap.c
> > @@ -37,6 +37,7 @@
> >  #include "xfs_bmap_util.h"
> >  #include "xfs_bmap_btree.h"
> >  #include "xfs_rmap.h"
> > +#include "xfs_rmap_btree.h"
> >  #include "xfs_refcount.h"
> >  #include "scrub/xfs_scrub.h"
> >  #include "scrub/scrub.h"
> > @@ -423,6 +424,163 @@ xfs_scrub_bmap_btree(
> >  	return error;
> >  }
> >  
> > +struct xfs_scrub_bmap_check_rmap_info {
> > +	struct xfs_scrub_context	*sc;
> > +	int				whichfork;
> > +	struct xfs_iext_cursor		icur;
> > +};
> > +
> > +/* Can we find bmaps that fit this rmap? */
> > +STATIC int
> > +xfs_scrub_bmap_check_rmap(
> > +	struct xfs_btree_cur		*cur,
> > +	struct xfs_rmap_irec		*rec,
> > +	void				*priv)
> > +{
> > +	struct xfs_bmbt_irec		irec;
> > +	struct xfs_scrub_bmap_check_rmap_info	*sbcri = priv;
> > +	struct xfs_ifork		*ifp;
> > +	struct xfs_scrub_context	*sc = sbcri->sc;
> > +	bool				have_map;
> > +
> > +	/* Is this even the right fork? */
> > +	if (rec->rm_owner != sc->ip->i_ino)
> > +		return 0;
> > +	if ((sbcri->whichfork == XFS_ATTR_FORK) ^
> > +	    !!(rec->rm_flags & XFS_RMAP_ATTR_FORK))
> > +		return 0;
> > +	if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
> > +		return 0;
> > +
> > +	/* Now look up the bmbt record. */
> > +	ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork);
> > +	if (!ifp) {
> > +		xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> > +				rec->rm_offset);
> > +		goto out;
> > +	}
> > +	have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
> > +			&sbcri->icur, &irec);
> > +	if (!have_map)
> > +		xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> > +				rec->rm_offset);
> > +
> 
> Comment on why/how an rmap record covers multiple bmbt records?

/*
 * bmap extent record lengths are constrained to 2^21 blocks in length
 * because of space constraints in the on-disk metadata structure.
 * However, rmap extent record lengths are constrained only by AG
 * length, so we have to loop through the bmbt to make sure that the
 * entire rmap is covered by bmbt records.
 */

> 
> > +	while (have_map) {
> > +		if (irec.br_startoff != rec->rm_offset)
> > +			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> > +					rec->rm_offset);
> > +		if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp,
> > +				cur->bc_private.a.agno, rec->rm_startblock))
> > +			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> > +					rec->rm_offset);
> > +		if (irec.br_blockcount > rec->rm_blockcount)
> > +			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> > +					rec->rm_offset);
> > +		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> > +			break;
> > +		rec->rm_startblock += irec.br_blockcount;
> > +		rec->rm_offset += irec.br_blockcount;
> > +		rec->rm_blockcount -= irec.br_blockcount;
> > +		if (rec->rm_blockcount == 0)
> > +			break;
> > +		have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec);
> > +		if (!have_map)
> > +			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
> > +					rec->rm_offset);
> > +	}
> > +
> > +out:
> > +	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> > +		return XFS_BTREE_QUERY_RANGE_ABORT;
> > +	return 0;
> > +}
> > +
> > +/* Make sure each rmap has a corresponding bmbt entry. */
> > +STATIC int
> > +xfs_scrub_bmap_check_ag_rmaps(
> > +	struct xfs_scrub_context	*sc,
> > +	int				whichfork,
> > +	xfs_agnumber_t			agno)
> > +{
> > +	struct xfs_scrub_bmap_check_rmap_info	sbcri;
> > +	struct xfs_btree_cur		*cur;
> > +	struct xfs_buf			*agf;
> > +	int				error;
> > +
> > +	error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
> > +	if (error)
> > +		return error;
> > +
> > +	cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
> > +	if (!cur) {
> > +		error = -ENOMEM;
> > +		goto out_agf;
> > +	}
> > +
> > +	sbcri.sc = sc;
> > +	sbcri.whichfork = whichfork;
> > +	error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri);
> > +	if (error == XFS_BTREE_QUERY_RANGE_ABORT)
> > +		error = 0;
> > +
> > +	xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
> 
> Why is XFS_BTREE_ERROR needed?

I think the usual (error ? ERROR : NOERROR) applies here, will fix.  Good
catch!

--D

> Brian
> 
> > +out_agf:
> > +	xfs_trans_brelse(sc->tp, agf);
> > +	return error;
> > +}
> > +
> > +/* Make sure each rmap has a corresponding bmbt entry. */
> > +STATIC int
> > +xfs_scrub_bmap_check_rmaps(
> > +	struct xfs_scrub_context	*sc,
> > +	int				whichfork)
> > +{
> > +	loff_t				size;
> > +	xfs_agnumber_t			agno;
> > +	int				error;
> > +
> > +	if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
> > +	    whichfork == XFS_COW_FORK ||
> > +	    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
> > +		return 0;
> > +
> > +	/* Don't support realtime rmap checks yet. */
> > +	if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
> > +		return 0;
> > +
> > +	/*
> > +	 * Only do this for complex maps that are in btree format, or for
> > +	 * situations where we would seem to have a size but zero extents.
> > +	 * The inode repair code can zap broken iforks, which means we have
> > +	 * to flag this bmap as corrupt if there are rmaps that need to be
> > +	 * reattached.
> > +	 */
> > +	switch (whichfork) {
> > +	case XFS_DATA_FORK:
> > +		size = i_size_read(VFS_I(sc->ip));
> > +		break;
> > +	case XFS_ATTR_FORK:
> > +		size = XFS_IFORK_Q(sc->ip);
> > +		break;
> > +	default:
> > +		size = 0;
> > +		break;
> > +	}
> > +	if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
> > +	    (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
> > +		return 0;
> > +
> > +	for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
> > +		error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno);
> > +		if (error)
> > +			return error;
> > +		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> > +			break;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  /*
> >   * Scrub an inode fork's block mappings.
> >   *
> > @@ -463,7 +621,7 @@ xfs_scrub_bmap(
> >  		break;
> >  	case XFS_ATTR_FORK:
> >  		if (!ifp)
> > -			goto out;
> > +			goto out_check_rmap;
> >  		if (!xfs_sb_version_hasattr(&mp->m_sb) &&
> >  		    !xfs_sb_version_hasattr2(&mp->m_sb))
> >  			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
> > @@ -534,6 +692,10 @@ xfs_scrub_bmap(
> >  			goto out;
> >  	}
> >  
> > +out_check_rmap:
> > +	error = xfs_scrub_bmap_check_rmaps(sc, whichfork);
> > +	if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error))
> > +		goto out;
> >  out:
> >  	return error;
> >  }
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 2/9] xfs: refactor bmap record valiation
  2018-03-21 13:55   ` Brian Foster
@ 2018-03-21 20:30     ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21 20:30 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 09:55:20AM -0400, Brian Foster wrote:
> On Wed, Mar 14, 2018 at 05:29:42PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Refactor the bmap validator into a more complete helper that looks for
> > extents that run off the end of the device, overflow into the next AG,
> > or have invalid flag states, leaving a raw helper so that the inode
> > repair can check things even if iget fails.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/libxfs/xfs_bmap.c       |   55 ++++++++++++++++++++++++++++++++++++++--
> >  fs/xfs/libxfs/xfs_bmap.h       |    5 ++++
> >  fs/xfs/libxfs/xfs_bmap_btree.h |   14 ----------
> >  fs/xfs/libxfs/xfs_inode_fork.c |   12 ++++++---
> >  4 files changed, 65 insertions(+), 21 deletions(-)
> > 
> > 
> > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > index daae00e..5850e76 100644
> > --- a/fs/xfs/libxfs/xfs_bmap.c
> > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > @@ -1261,11 +1261,15 @@ xfs_iread_extents(
> >  		 */
> >  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> >  		for (j = 0; j < num_recs; j++, frp++, i++) {
> > +			xfs_failaddr_t	fa;
> > +
> >  			xfs_bmbt_disk_get_all(frp, &new);
> > -			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
> > -				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> > -						 XFS_ERRLEVEL_LOW, mp);
> > +			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
> > +			if (fa) {
> >  				error = -EFSCORRUPTED;
> > +				xfs_inode_verifier_error(ip, error,
> > +						"xfs_bmap_read_extents(2)",
> 
> Might as well fix up the stale function name string.

Ok.

> > +						frp, sizeof(*frp), fa);
> >  				goto out_brelse;
> >  			}
> >  			xfs_iext_insert(ip, &icur, &new, state);
> > @@ -6154,3 +6158,48 @@ xfs_bmap_finish_one(
> >  
> >  	return error;
> >  }
> > +
> > +/* Check that an extent does not have invalid flags or bad ranges. */
> > +xfs_failaddr_t
> > +xfs_bmbt_validate_extent(
> > +	struct xfs_mount	*mp,
> > +	bool			isrt,
> > +	int			whichfork,
> > +	struct xfs_bmbt_irec	*irec)
> > +{
> > +	xfs_fsblock_t		endfsb;
> > +
> > +	endfsb = irec->br_startblock + irec->br_blockcount - 1;
> > +	if (isrt) {
> > +		if (!xfs_verify_rtbno(mp, irec->br_startblock))
> > +			return __this_address;
> > +		if (!xfs_verify_rtbno(mp, endfsb))
> > +			return __this_address;
> > +	} else {
> > +		if (!xfs_verify_fsbno(mp, irec->br_startblock))
> > +			return __this_address;
> > +		if (!xfs_verify_fsbno(mp, endfsb))
> > +			return __this_address;
> > +		if (XFS_FSB_TO_AGNO(mp, irec->br_startblock) !=
> > +		    XFS_FSB_TO_AGNO(mp, endfsb))
> > +			return __this_address;
> > +	}
> > +	if (irec->br_state != XFS_EXT_NORM) {
> > +		if (whichfork != XFS_DATA_FORK)
> > +			return __this_address;
> > +		if (!xfs_sb_version_hasextflgbit(&mp->m_sb))
> > +			return __this_address;
> > +	}
> > +	return NULL;
> > +}
> > +
> > +/* Check that an inode's extent does not have invalid flags or bad ranges. */
> > +xfs_failaddr_t
> > +xfs_bmap_validate_extent(
> > +	struct xfs_inode	*ip,
> > +	int			whichfork,
> > +	struct xfs_bmbt_irec	*irec)
> > +{
> > +	return xfs_bmbt_validate_extent(ip->i_mount, XFS_IS_REALTIME_INODE(ip),
> > +			whichfork, irec);
> > +}
> 
> Do we really need this wrapper just for an rt check? Why not just pass
> ip down to xfs_bmbt_validate_extent() and pull isrt from there (if
> present)?
> 
> The commit log implies the helper could be left around for future repair
> code. If that's the case, I think it's more appropriate to refactor it
> as such in whatever patch the helper is first used. Otherwise looks
> fine.

Ok.  FWIW the intended usage for xfs_bmbt_validate_extent is that if the
user asks us to repair an inode for which _iget returns EFSCORRUPTED
we'll walk through all the inode fork sanity checks; if an
extents-format fork has bad bmbt records, we reset the inode fork and
indicate that a bmbt rebuild (via the rmapbt) is necessary.  In that
case there is no xfs_inode hence the need for a raw function.

However... I'll move the creation of the wrapper to that patch and we
can argue about it there. :)

--D

> Brian
> 
> > diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> > index e36d757..e0fef89 100644
> > --- a/fs/xfs/libxfs/xfs_bmap.h
> > +++ b/fs/xfs/libxfs/xfs_bmap.h
> > @@ -274,4 +274,9 @@ static inline int xfs_bmap_fork_to_state(int whichfork)
> >  	}
> >  }
> >  
> > +xfs_failaddr_t xfs_bmbt_validate_extent(struct xfs_mount *mp, bool isrt,
> > +		int whichfork, struct xfs_bmbt_irec *irec);
> > +xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork,
> > +		struct xfs_bmbt_irec *irec);
> > +
> >  #endif	/* __XFS_BMAP_H__ */
> > diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> > index 135b8c5..e450574 100644
> > --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> > +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> > @@ -118,18 +118,4 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
> >  extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
> >  		struct xfs_trans *, struct xfs_inode *, int);
> >  
> > -/*
> > - * Check that the extent does not contain an invalid unwritten extent flag.
> > - */
> > -static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> > -		struct xfs_bmbt_irec *irec)
> > -{
> > -	if (irec->br_state == XFS_EXT_NORM)
> > -		return true;
> > -	if (whichfork == XFS_DATA_FORK &&
> > -	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> > -		return true;
> > -	return false;
> > -}
> > -
> >  #endif	/* __XFS_BMAP_BTREE_H__ */
> > diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> > index 866d2861..613fba2 100644
> > --- a/fs/xfs/libxfs/xfs_inode_fork.c
> > +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> > @@ -245,10 +245,14 @@ xfs_iformat_extents(
> >  
> >  		xfs_iext_first(ifp, &icur);
> >  		for (i = 0; i < nex; i++, dp++) {
> > +			xfs_failaddr_t	fa;
> > +
> >  			xfs_bmbt_disk_get_all(dp, &new);
> > -			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
> > -				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> > -						 XFS_ERRLEVEL_LOW, mp);
> > +			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
> > +			if (fa) {
> > +				xfs_inode_verifier_error(ip, -EFSCORRUPTED,
> > +						"xfs_iformat_extents(2)",
> > +						dp, sizeof(*dp), fa);
> >  				return -EFSCORRUPTED;
> >  			}
> >  
> > @@ -595,7 +599,7 @@ xfs_iextents_copy(
> >  	for_each_xfs_iext(ifp, &icur, &rec) {
> >  		if (isnullstartblock(rec.br_startblock))
> >  			continue;
> > -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, &rec));
> > +		ASSERT(xfs_bmap_validate_extent(ip, whichfork, &rec) == NULL);
> >  		xfs_bmbt_disk_set_all(dp, &rec);
> >  		trace_xfs_write_extent(ip, &icur, state, _RET_IP_);
> >  		copied += sizeof(struct xfs_bmbt_rec);
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 6/9] xfs: inode scrubber shouldn't bother with raw checks
  2018-03-21 17:42   ` Brian Foster
@ 2018-03-21 20:37     ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21 20:37 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 01:42:39PM -0400, Brian Foster wrote:
> On Wed, Mar 14, 2018 at 05:30:07PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > The inode scrubber tries to _iget the inode prior to running checks.
> > If that _iget call fails with corruption errors that's an automatic
> > fail, regardless of whether it was the inode buffer read verifier,
> > the ifork verifier, or the ifork formatter that errored out.
> > 
> > Therefore, get rid of the raw mode scrub code because it's not needed.
> > Found by trying to fix some test failures in xfs/379 and xfs/415.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/scrub/inode.c |   98 +++++---------------------------------------------
> >  1 file changed, 9 insertions(+), 89 deletions(-)
> > 
> > 
> > diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> > index 21297be..0332a01 100644
> > --- a/fs/xfs/scrub/inode.c
> > +++ b/fs/xfs/scrub/inode.c
> > @@ -515,72 +515,6 @@ xfs_scrub_dinode(
> >  	}
> >  }
> >  
> > -/* Map and read a raw inode. */
> > -STATIC int
> > -xfs_scrub_inode_map_raw(
> > -	struct xfs_scrub_context	*sc,
> > -	xfs_ino_t			ino,
> > -	struct xfs_buf			**bpp,
> > -	struct xfs_dinode		**dipp)
> > -{
> > -	struct xfs_imap			imap;
> > -	struct xfs_mount		*mp = sc->mp;
> > -	struct xfs_buf			*bp = NULL;
> > -	struct xfs_dinode		*dip;
> > -	int				error;
> > -
> > -	error = xfs_imap(mp, sc->tp, ino, &imap, XFS_IGET_UNTRUSTED);
> > -	if (error == -EINVAL) {
> > -		/*
> > -		 * Inode could have gotten deleted out from under us;
> > -		 * just forget about it.
> > -		 */
> > -		error = -ENOENT;
> > -		goto out;
> > -	}
> > -	if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
> > -			XFS_INO_TO_AGBNO(mp, ino), &error))
> > -		goto out;
> > -
> > -	error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
> > -			imap.im_blkno, imap.im_len, XBF_UNMAPPED, &bp,
> > -			NULL);
> > -	if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
> > -			XFS_INO_TO_AGBNO(mp, ino), &error))
> > -		goto out;
> > -
> > -	/*
> > -	 * Is this really an inode?  We disabled verifiers in the above
> > -	 * xfs_trans_read_buf call because the inode buffer verifier
> > -	 * fails on /any/ inode record in the inode cluster with a bad
> > -	 * magic or version number, not just the one that we're
> > -	 * checking.  Therefore, grab the buffer unconditionally, attach
> > -	 * the inode verifiers by hand, and run the inode verifier only
> > -	 * on the one inode we want.
> > -	 */
> > -	bp->b_ops = &xfs_inode_buf_ops;
> > -	dip = xfs_buf_offset(bp, imap.im_boffset);
> > -	if (xfs_dinode_verify(mp, ino, dip) != NULL ||
> > -	    !xfs_dinode_good_version(mp, dip->di_version)) {
> > -		xfs_scrub_ino_set_corrupt(sc, ino, bp);
> > -		goto out_buf;
> > -	}
> > -
> > -	/* ...and is it the one we asked for? */
> > -	if (be32_to_cpu(dip->di_gen) != sc->sm->sm_gen) {
> > -		error = -ENOENT;
> > -		goto out_buf;
> > -	}
> > -
> > -	*dipp = dip;
> > -	*bpp = bp;
> > -out:
> > -	return error;
> > -out_buf:
> > -	xfs_trans_brelse(sc->tp, bp);
> > -	return error;
> > -}
> > -
> >  /*
> >   * Make sure the finobt doesn't think this inode is free.
> >   * We don't have to check the inobt ourselves because we got the inode via
> > @@ -727,43 +661,29 @@ xfs_scrub_inode(
> >  	struct xfs_scrub_context	*sc)
> >  {
> >  	struct xfs_dinode		di;
> > -	struct xfs_buf			*bp = NULL;
> > -	struct xfs_dinode		*dip;
> > -	xfs_ino_t			ino;
> >  	int				error = 0;
> >  
> > -	/* Did we get the in-core inode, or are we doing this manually? */
> > -	if (sc->ip) {
> > -		ino = sc->ip->i_ino;
> > -		xfs_inode_to_disk(sc->ip, &di, 0);
> > -		dip = &di;
> > -	} else {
> > -		/* Map & read inode. */
> > -		ino = sc->sm->sm_ino;
> > -		error = xfs_scrub_inode_map_raw(sc, ino, &bp, &dip);
> > -		if (error || !bp)
> > -			goto out;
> > +	/* iget failed means automatic fail. */
> > +	if (!sc->ip) {
> > +		xfs_scrub_ino_set_corrupt(sc, sc->sm->sm_ino, NULL);
> > +		return 0;
> >  	}
> 
> Ok, so the setup function will attempt to lookup the inode number that's
> passed in. If the lookup fails with a corruption error, we'd have ip ==
> NULL but return 0 from that setup call because it's not a scrub
> operational error. Hence, we get here without an inode and call it an fs
> corruption. Seems fine as long as I'm following this correctly:

Correct.  I'll change the comment to read:

/*
 * If sc->ip is NULL, that means that the setup function called xfs_iget
 * to look up the inode.  xfs_iget returned a EFSCORRUPTED and a NULL
 * inode, so flag the corruption error and return.
 */

--D

> Reviewed-by: Brian Foster <bfoster@redhat.com>
> 
> >  
> > -	xfs_scrub_dinode(sc, bp, dip, ino);
> > +	/* Scrub the inode core. */
> > +	xfs_inode_to_disk(sc->ip, &di, 0);
> > +	xfs_scrub_dinode(sc, NULL, &di, sc->ip->i_ino);
> >  	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> >  		goto out;
> >  
> > -	/* Now let's do the things that require a live inode. */
> > -	if (!sc->ip)
> > -		goto out;
> > -
> >  	/*
> >  	 * Look for discrepancies between file's data blocks and the reflink
> >  	 * iflag.  We already checked the iflag against the file mode when
> >  	 * we scrubbed the dinode.
> >  	 */
> >  	if (S_ISREG(VFS_I(sc->ip)->i_mode))
> > -		xfs_scrub_inode_check_reflink_iflag(sc, ino, bp);
> > +		xfs_scrub_inode_check_reflink_iflag(sc, sc->ip->i_ino, NULL);
> >  
> > -	xfs_scrub_inode_xref(sc, ino, dip);
> > +	xfs_scrub_inode_xref(sc, sc->ip->i_ino, &di);
> >  out:
> > -	if (bp)
> > -		xfs_trans_brelse(sc->tp, bp);
> >  	return error;
> >  }
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber
  2018-03-21 17:42   ` Brian Foster
@ 2018-03-21 20:50     ` Darrick J. Wong
  2018-03-22 14:34       ` Brian Foster
  0 siblings, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21 20:50 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 01:42:49PM -0400, Brian Foster wrote:
> On Wed, Mar 14, 2018 at 05:30:20PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > During the inode btree scrubs we try to confirm the freemask bits
> > against the inode records.  If the inode buffer read fails, this is a
> > cross-referencing error, not a corruption of the inode btree itself.
> > Use the xref_process_error call here.  Found via core.version middlebit
> > fuzz in xfs/415.
> > 
> 
> What's the difference/consequence of this change?

For now, more accurate reporting of the outcome of scrubbing the inobt.
If xfs_imap_to_bp returns EFSCORRUPTED that means that it found the
inode buffer, loaded it, and the verifier for the inode records failed.
Therefore, we need to communicate to userspace that the inobt looks ok
but we were unable to confirm that something the inobt points to (the
inode chunk) agrees with the the pointer.  That's an xref error, not a
corruption error.

Once we start on the xfs_scrub repair support, you'll see in the patches
that prior to scanning the inodes in the filesystem, we need to know
whether or not the agi and the inobt are ok.  If the agi or inobt scan
shows corruption, we need to rebuild them (or abort).  If however, the
scan only says cross-referencing error, then we conclude that the inobt
is ok but the inodes are not, and proceed with the inode scan to find
the bad inode and repair it.

--D

> Brian
> 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/scrub/ialloc.c |    3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> > 
> > 
> > diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> > index 63ab3f9..32e0d1a 100644
> > --- a/fs/xfs/scrub/ialloc.c
> > +++ b/fs/xfs/scrub/ialloc.c
> > @@ -259,7 +259,8 @@ xfs_scrub_iallocbt_check_freemask(
> >  
> >  		error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap,
> >  				&dip, &bp, 0, 0);
> > -		if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error))
> > +		if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur, 0,
> > +				&error))
> >  			continue;
> >  
> >  		/* Which inodes are free? */
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 10/9] xfs: don't accept inode buffers with suspicious unlinked chains
  2018-03-21 17:43   ` Brian Foster
@ 2018-03-21 20:52     ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-21 20:52 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 01:43:24PM -0400, Brian Foster wrote:
> On Tue, Mar 20, 2018 at 08:21:36PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > When we're verifying inode buffers, sanity-check the unlinked pointer.
> > We don't want to run the risk of trying to purge something that's
> > obviously broken.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> 
> FYI this doesn't seem to apply to for-next (HEAD is commit
> df79b81b2e)...

Yeah, I think I have some cleaning to do, and then I'll resend this
series with all the fixed up stuff.

--D

> Brian
> 
> >  fs/xfs/libxfs/xfs_inode_buf.c |   10 ++++++++--
> >  1 file changed, 8 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> > index 4fe17b3..f216d5b 100644
> > --- a/fs/xfs/libxfs/xfs_inode_buf.c
> > +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> > @@ -93,20 +93,26 @@ xfs_inode_buf_verify(
> >  	bool		readahead)
> >  {
> >  	struct xfs_mount *mp = bp->b_target->bt_mount;
> > +	xfs_agnumber_t	agno;
> >  	int		i;
> >  	int		ni;
> >  
> >  	/*
> >  	 * Validate the magic number and version of every inode in the buffer
> >  	 */
> > +	agno = xfs_daddr_to_agno(mp, XFS_BUF_ADDR(bp));
> >  	ni = XFS_BB_TO_FSB(mp, bp->b_length) * mp->m_sb.sb_inopblock;
> >  	for (i = 0; i < ni; i++) {
> >  		int		di_ok;
> >  		xfs_dinode_t	*dip;
> > +		xfs_agino_t	unlinked_ino;
> >  
> >  		dip = xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog));
> > +		unlinked_ino = be32_to_cpu(dip->di_next_unlinked);
> >  		di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) &&
> > -			xfs_dinode_good_version(mp, dip->di_version);
> > +			xfs_dinode_good_version(mp, dip->di_version) &&
> > +			(unlinked_ino == NULLAGINO ||
> > +			 xfs_verify_agino(mp, agno, unlinked_ino));
> >  		if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
> >  						XFS_ERRTAG_ITOBP_INOTOBP))) {
> >  			if (readahead) {
> > @@ -122,9 +128,9 @@ xfs_inode_buf_verify(
> >  				(unsigned long long)bp->b_bn, i,
> >  				be16_to_cpu(dip->di_magic));
> >  #endif
> > +			return;
> >  		}
> >  	}
> > -	xfs_inobp_check(mp, bp);
> >  }
> >  
> >  
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v2 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-15  0:29 ` [PATCH 1/9] xfs: sanity-check the unused space before trying to use it Darrick J. Wong
  2018-03-21 13:52   ` Brian Foster
@ 2018-03-22  5:59   ` Darrick J. Wong
  2018-03-22 14:33     ` Brian Foster
  2018-03-22 22:04     ` Dave Chinner
  2018-03-22 17:53   ` [PATCH v3 " Darrick J. Wong
  2018-03-22 22:21   ` [PATCH v4 " Darrick J. Wong
  3 siblings, 2 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22  5:59 UTC (permalink / raw)
  To: linux-xfs; +Cc: Brian Foster

From: Darrick J. Wong <darrick.wong@oracle.com>

In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
it doesn't make sense.  Since a carefully crafted fuzzed image can cause
the kernel to crash after blowing a bunch of assertions, let's move
those checks into a validator function and rig everything up to return
EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
xfs/391.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: release buffers defensively, fix some formatting weirdness
---
 fs/xfs/libxfs/xfs_dir2.h       |    2 +
 fs/xfs/libxfs/xfs_dir2_block.c |   49 +++++++++++++++----------
 fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
 fs/xfs/libxfs/xfs_dir2_leaf.c  |    6 +++
 fs/xfs/libxfs/xfs_dir2_node.c  |    9 ++++-
 5 files changed, 103 insertions(+), 41 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index 388d67c..989e95a 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
 extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
 		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
 		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
-extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
+extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
 		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
 		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
 		int *needlogp, int *needscanp);
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 2da86a3..e8256e2 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -451,15 +451,19 @@ xfs_dir2_block_addname(
 	 * No stale entries, will use enddup space to hold new leaf.
 	 */
 	if (!btp->stale) {
+		xfs_dir2_data_aoff_t	aoff;
+
 		/*
 		 * Mark the space needed for the new leaf entry, now in use.
 		 */
-		xfs_dir2_data_use_free(args, bp, enddup,
-			(xfs_dir2_data_aoff_t)
-			((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) -
-			 sizeof(*blp)),
-			(xfs_dir2_data_aoff_t)sizeof(*blp),
-			&needlog, &needscan);
+		aoff = (xfs_dir2_data_aoff_t)((char *)enddup - (char *)hdr +
+				be16_to_cpu(enddup->length) - sizeof(*blp));
+		error = xfs_dir2_data_use_free(args, bp, enddup, aoff,
+				(xfs_dir2_data_aoff_t)sizeof(*blp), &needlog,
+				&needscan);
+		if (error)
+			return error;
+
 		/*
 		 * Update the tail (entry count).
 		 */
@@ -541,9 +545,11 @@ xfs_dir2_block_addname(
 	/*
 	 * Mark space for the data entry used.
 	 */
-	xfs_dir2_data_use_free(args, bp, dup,
+	error = xfs_dir2_data_use_free(args, bp, dup,
 		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
 		(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Create the new data entry.
 	 */
@@ -997,8 +1003,10 @@ xfs_dir2_leaf_to_block(
 	/*
 	 * Use up the space at the end of the block (blp/btp).
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup, args->geo->blksize - size, size,
-		&needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, dbp, dup,
+		args->geo->blksize - size, size, &needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Initialize the block tail.
 	 */
@@ -1110,18 +1118,14 @@ xfs_dir2_sf_to_block(
 	 * Add block 0 to the inode.
 	 */
 	error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
-	if (error) {
-		kmem_free(sfp);
-		return error;
-	}
+	if (error)
+		goto out_free;
 	/*
 	 * Initialize the data block, then convert it to block format.
 	 */
 	error = xfs_dir3_data_init(args, blkno, &bp);
-	if (error) {
-		kmem_free(sfp);
-		return error;
-	}
+	if (error)
+		goto out_free;
 	xfs_dir3_block_init(mp, tp, bp, dp);
 	hdr = bp->b_addr;
 
@@ -1136,8 +1140,10 @@ xfs_dir2_sf_to_block(
 	 */
 	dup = dp->d_ops->data_unused_p(hdr);
 	needlog = needscan = 0;
-	xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
+	error = xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
 			       i, &needlog, &needscan);
+	if (error)
+		goto out_free;
 	ASSERT(needscan == 0);
 	/*
 	 * Fill in the tail.
@@ -1150,9 +1156,11 @@ xfs_dir2_sf_to_block(
 	/*
 	 * Remove the freespace, we'll manage it.
 	 */
-	xfs_dir2_data_use_free(args, bp, dup,
+	error = xfs_dir2_data_use_free(args, bp, dup,
 		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
 		be16_to_cpu(dup->length), &needlog, &needscan);
+	if (error)
+		goto out_free;
 	/*
 	 * Create entry for .
 	 */
@@ -1256,4 +1264,7 @@ xfs_dir2_sf_to_block(
 	xfs_dir2_block_log_tail(tp, bp);
 	xfs_dir3_data_check(dp, bp);
 	return 0;
+out_free:
+	kmem_free(sfp);
+	return error;
 }
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index 9202794..167503d 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -932,10 +932,51 @@ xfs_dir2_data_make_free(
 	*needscanp = needscan;
 }
 
+/* Check our free data for obvious signs of corruption. */
+static inline xfs_failaddr_t
+xfs_dir2_data_check_free(
+	struct xfs_dir2_data_hdr	*hdr,
+	struct xfs_dir2_data_unused	*dup,
+	xfs_dir2_data_aoff_t		offset,
+	xfs_dir2_data_aoff_t		len)
+{
+	if (hdr->magic != cpu_to_be32(XFS_DIR2_DATA_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
+		return __this_address;
+	if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG)
+		return __this_address;
+	if (offset < (char *)dup - (char *)hdr)
+		return __this_address;
+	if (offset + len > (char *)dup + be16_to_cpu(dup->length) - (char *)hdr)
+		return __this_address;
+	if ((char *)dup - (char *)hdr !=
+	    be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
+		return __this_address;
+	return NULL;
+}
+
+/* Sanity-check a new bestfree entry. */
+static inline xfs_failaddr_t
+xfs_dir2_data_check_new_free(
+	struct xfs_dir2_data_hdr	*hdr,
+	struct xfs_dir2_data_free	*dfp,
+	struct xfs_dir2_data_unused	*newdup)
+{
+	if (dfp == NULL)
+		return __this_address;
+	if (dfp->length != newdup->length)
+		return __this_address;
+	if (be16_to_cpu(dfp->offset) != (char *)newdup - (char *)hdr)
+		return __this_address;
+	return NULL;
+}
+
 /*
  * Take a byte range out of an existing unused space and make it un-free.
  */
-void
+int
 xfs_dir2_data_use_free(
 	struct xfs_da_args	*args,
 	struct xfs_buf		*bp,
@@ -947,23 +988,19 @@ xfs_dir2_data_use_free(
 {
 	xfs_dir2_data_hdr_t	*hdr;		/* data block header */
 	xfs_dir2_data_free_t	*dfp;		/* bestfree pointer */
+	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
+	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
+	struct xfs_dir2_data_free *bf;
+	xfs_failaddr_t		fa;
 	int			matchback;	/* matches end of freespace */
 	int			matchfront;	/* matches start of freespace */
 	int			needscan;	/* need to regen bestfree */
-	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
-	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
 	int			oldlen;		/* old unused entry's length */
-	struct xfs_dir2_data_free *bf;
 
 	hdr = bp->b_addr;
-	ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
-	ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
-	ASSERT(offset >= (char *)dup - (char *)hdr);
-	ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)hdr);
-	ASSERT((char *)dup - (char *)hdr == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
+	fa = xfs_dir2_data_check_free(hdr, dup, offset, len);
+	if (fa)
+		goto corrupt;
 	/*
 	 * Look up the entry in the bestfree table.
 	 */
@@ -1008,9 +1045,9 @@ xfs_dir2_data_use_free(
 			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
 			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
 						       needlogp);
-			ASSERT(dfp != NULL);
-			ASSERT(dfp->length == newdup->length);
-			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
+			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
+			if (fa)
+				goto corrupt;
 			/*
 			 * If we got inserted at the last slot,
 			 * that means we don't know if there was a better
@@ -1036,9 +1073,9 @@ xfs_dir2_data_use_free(
 			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
 			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
 						       needlogp);
-			ASSERT(dfp != NULL);
-			ASSERT(dfp->length == newdup->length);
-			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
+			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
+			if (fa)
+				goto corrupt;
 			/*
 			 * If we got inserted at the last slot,
 			 * that means we don't know if there was a better
@@ -1084,6 +1121,11 @@ xfs_dir2_data_use_free(
 		}
 	}
 	*needscanp = needscan;
+	return 0;
+corrupt:
+	xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
+			hdr, __FILE__, __LINE__, fa);
+	return -EFSCORRUPTED;
 }
 
 /* Find the end of the entry data in a data/block format dir block. */
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index d61d52d..f746921 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -877,9 +877,13 @@ xfs_dir2_leaf_addname(
 	/*
 	 * Mark the initial part of our freespace in use for the new entry.
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup,
+	error = xfs_dir2_data_use_free(args, dbp, dup,
 		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
 		&needlog, &needscan);
+	if (error) {
+		xfs_trans_brelse(tp, lbp);
+		return error;
+	}
 	/*
 	 * Initialize our new entry (at last).
 	 */
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 0839ffe..57b6c4fa 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -1729,6 +1729,7 @@ xfs_dir2_node_addname_int(
 	__be16			*bests;
 	struct xfs_dir3_icfree_hdr freehdr;
 	struct xfs_dir2_data_free *bf;
+	xfs_dir2_data_aoff_t	aoff;
 
 	dp = args->dp;
 	mp = dp->i_mount;
@@ -2023,9 +2024,13 @@ xfs_dir2_node_addname_int(
 	/*
 	 * Mark the first part of the unused space, inuse for us.
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
+	aoff = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
+	error = xfs_dir2_data_use_free(args, dbp, dup, aoff, length,
 		&needlog, &needscan);
+	if (error) {
+		xfs_trans_brelse(tp, dbp);
+		return error;
+	}
 	/*
 	 * Fill in the new entry and log it.
 	 */

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v2 2/9] xfs: refactor bmap record valiation
  2018-03-15  0:29 ` [PATCH 2/9] xfs: refactor bmap record valiation Darrick J. Wong
  2018-03-21 13:55   ` Brian Foster
@ 2018-03-22  6:01   ` Darrick J. Wong
  2018-03-22 14:33     ` Brian Foster
  1 sibling, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22  6:01 UTC (permalink / raw)
  To: linux-xfs; +Cc: Brian Foster

From: Darrick J. Wong <darrick.wong@oracle.com>

Refactor the bmap validator into a more complete helper that looks for
extents that run off the end of the device, overflow into the next AG,
or have invalid flag states.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: defer repair-oriented wrappers until they're needed
---
 fs/xfs/libxfs/xfs_bmap.c       |   46 +++++++++++++++++++++++++++++++++++++---
 fs/xfs/libxfs/xfs_bmap.h       |    3 +++
 fs/xfs/libxfs/xfs_bmap_btree.h |   14 ------------
 fs/xfs/libxfs/xfs_inode_fork.c |   12 +++++++---
 4 files changed, 54 insertions(+), 21 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index daae00e..91214f2 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1261,11 +1261,15 @@ xfs_iread_extents(
 		 */
 		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
 		for (j = 0; j < num_recs; j++, frp++, i++) {
+			xfs_failaddr_t	fa;
+
 			xfs_bmbt_disk_get_all(frp, &new);
-			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
-				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
-						 XFS_ERRLEVEL_LOW, mp);
+			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
+			if (fa) {
 				error = -EFSCORRUPTED;
+				xfs_inode_verifier_error(ip, error,
+						"xfs_iread_extents(2)",
+						frp, sizeof(*frp), fa);
 				goto out_brelse;
 			}
 			xfs_iext_insert(ip, &icur, &new, state);
@@ -6154,3 +6158,39 @@ xfs_bmap_finish_one(
 
 	return error;
 }
+
+/* Check that an inode's extent does not have invalid flags or bad ranges. */
+xfs_failaddr_t
+xfs_bmap_validate_extent(
+	struct xfs_inode	*ip,
+	int			whichfork,
+	struct xfs_bmbt_irec	*irec)
+{
+	struct xfs_mount	*mp = ip->i_mount;
+	xfs_fsblock_t		endfsb;
+	bool			isrt;
+
+	isrt = XFS_IS_REALTIME_INODE(ip);
+	endfsb = irec->br_startblock + irec->br_blockcount - 1;
+	if (isrt) {
+		if (!xfs_verify_rtbno(mp, irec->br_startblock))
+			return __this_address;
+		if (!xfs_verify_rtbno(mp, endfsb))
+			return __this_address;
+	} else {
+		if (!xfs_verify_fsbno(mp, irec->br_startblock))
+			return __this_address;
+		if (!xfs_verify_fsbno(mp, endfsb))
+			return __this_address;
+		if (XFS_FSB_TO_AGNO(mp, irec->br_startblock) !=
+		    XFS_FSB_TO_AGNO(mp, endfsb))
+			return __this_address;
+	}
+	if (irec->br_state != XFS_EXT_NORM) {
+		if (whichfork != XFS_DATA_FORK)
+			return __this_address;
+		if (!xfs_sb_version_hasextflgbit(&mp->m_sb))
+			return __this_address;
+	}
+	return NULL;
+}
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index e36d757..f3be641 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -274,4 +274,7 @@ static inline int xfs_bmap_fork_to_state(int whichfork)
 	}
 }
 
+xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork,
+		struct xfs_bmbt_irec *irec);
+
 #endif	/* __XFS_BMAP_H__ */
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
index 135b8c5..e450574 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.h
+++ b/fs/xfs/libxfs/xfs_bmap_btree.h
@@ -118,18 +118,4 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
 extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
 		struct xfs_trans *, struct xfs_inode *, int);
 
-/*
- * Check that the extent does not contain an invalid unwritten extent flag.
- */
-static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
-		struct xfs_bmbt_irec *irec)
-{
-	if (irec->br_state == XFS_EXT_NORM)
-		return true;
-	if (whichfork == XFS_DATA_FORK &&
-	    xfs_sb_version_hasextflgbit(&mp->m_sb))
-		return true;
-	return false;
-}
-
 #endif	/* __XFS_BMAP_BTREE_H__ */
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 866d2861..613fba2 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -245,10 +245,14 @@ xfs_iformat_extents(
 
 		xfs_iext_first(ifp, &icur);
 		for (i = 0; i < nex; i++, dp++) {
+			xfs_failaddr_t	fa;
+
 			xfs_bmbt_disk_get_all(dp, &new);
-			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
-				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
-						 XFS_ERRLEVEL_LOW, mp);
+			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
+			if (fa) {
+				xfs_inode_verifier_error(ip, -EFSCORRUPTED,
+						"xfs_iformat_extents(2)",
+						dp, sizeof(*dp), fa);
 				return -EFSCORRUPTED;
 			}
 
@@ -595,7 +599,7 @@ xfs_iextents_copy(
 	for_each_xfs_iext(ifp, &icur, &rec) {
 		if (isnullstartblock(rec.br_startblock))
 			continue;
-		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, &rec));
+		ASSERT(xfs_bmap_validate_extent(ip, whichfork, &rec) == NULL);
 		xfs_bmbt_disk_set_all(dp, &rec);
 		trace_xfs_write_extent(ip, &icur, state, _RET_IP_);
 		copied += sizeof(struct xfs_bmbt_rec);

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v2 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files
  2018-03-15  0:30 ` [PATCH 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files Darrick J. Wong
  2018-03-21 17:42   ` Brian Foster
@ 2018-03-22  6:02   ` Darrick J. Wong
  2018-03-22 14:33     ` Brian Foster
  1 sibling, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22  6:02 UTC (permalink / raw)
  To: linux-xfs; +Cc: Brian Foster

From: Darrick J. Wong <darrick.wong@oracle.com>

When we're scanning an extent mapping inode fork, ensure that every rmap
record for this ifork has a corresponding bmbt record too.  This
(mostly) provides the ability to cross-reference rmap records with bmap
data.  The rmap scrubber cannot do the xref on its own because that
requires taking an ilock with the agf lock held, which violates our
locking order rules (inode, then agf).

Note that we only do this for forks that are in btree format due to the
increased complexity; or forks that should have data but suspiciously
have zero extents because the inode could have just had its iforks
zapped by the inode repair code and now we need to reclaim the old
extents.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: enhance comments, fix XFS_BTREE_ERROR usage
---
 fs/xfs/scrub/bmap.c |  170 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 169 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index d002821..75ea2d6 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -37,6 +37,7 @@
 #include "xfs_bmap_util.h"
 #include "xfs_bmap_btree.h"
 #include "xfs_rmap.h"
+#include "xfs_rmap_btree.h"
 #include "xfs_refcount.h"
 #include "scrub/xfs_scrub.h"
 #include "scrub/scrub.h"
@@ -423,6 +424,169 @@ xfs_scrub_bmap_btree(
 	return error;
 }
 
+struct xfs_scrub_bmap_check_rmap_info {
+	struct xfs_scrub_context	*sc;
+	int				whichfork;
+	struct xfs_iext_cursor		icur;
+};
+
+/* Can we find bmaps that fit this rmap? */
+STATIC int
+xfs_scrub_bmap_check_rmap(
+	struct xfs_btree_cur		*cur,
+	struct xfs_rmap_irec		*rec,
+	void				*priv)
+{
+	struct xfs_bmbt_irec		irec;
+	struct xfs_scrub_bmap_check_rmap_info	*sbcri = priv;
+	struct xfs_ifork		*ifp;
+	struct xfs_scrub_context	*sc = sbcri->sc;
+	bool				have_map;
+
+	/* Is this even the right fork? */
+	if (rec->rm_owner != sc->ip->i_ino)
+		return 0;
+	if ((sbcri->whichfork == XFS_ATTR_FORK) ^
+	    !!(rec->rm_flags & XFS_RMAP_ATTR_FORK))
+		return 0;
+	if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
+		return 0;
+
+	/* Now look up the bmbt record. */
+	ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork);
+	if (!ifp) {
+		xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+				rec->rm_offset);
+		goto out;
+	}
+	have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
+			&sbcri->icur, &irec);
+	if (!have_map)
+		xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+				rec->rm_offset);
+	/*
+	 * bmap extent record lengths are constrained to 2^21 blocks in length
+	 * because of space constraints in the on-disk metadata structure.
+	 * However, rmap extent record lengths are constrained only by AG
+	 * length, so we have to loop through the bmbt to make sure that the
+	 * entire rmap is covered by bmbt records.
+	 */
+	while (have_map) {
+		if (irec.br_startoff != rec->rm_offset)
+			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+					rec->rm_offset);
+		if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp,
+				cur->bc_private.a.agno, rec->rm_startblock))
+			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+					rec->rm_offset);
+		if (irec.br_blockcount > rec->rm_blockcount)
+			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+					rec->rm_offset);
+		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+			break;
+		rec->rm_startblock += irec.br_blockcount;
+		rec->rm_offset += irec.br_blockcount;
+		rec->rm_blockcount -= irec.br_blockcount;
+		if (rec->rm_blockcount == 0)
+			break;
+		have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec);
+		if (!have_map)
+			xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork,
+					rec->rm_offset);
+	}
+
+out:
+	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+		return XFS_BTREE_QUERY_RANGE_ABORT;
+	return 0;
+}
+
+/* Make sure each rmap has a corresponding bmbt entry. */
+STATIC int
+xfs_scrub_bmap_check_ag_rmaps(
+	struct xfs_scrub_context	*sc,
+	int				whichfork,
+	xfs_agnumber_t			agno)
+{
+	struct xfs_scrub_bmap_check_rmap_info	sbcri;
+	struct xfs_btree_cur		*cur;
+	struct xfs_buf			*agf;
+	int				error;
+
+	error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
+	if (error)
+		return error;
+
+	cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
+	if (!cur) {
+		error = -ENOMEM;
+		goto out_agf;
+	}
+
+	sbcri.sc = sc;
+	sbcri.whichfork = whichfork;
+	error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri);
+	if (error == XFS_BTREE_QUERY_RANGE_ABORT)
+		error = 0;
+
+	xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+out_agf:
+	xfs_trans_brelse(sc->tp, agf);
+	return error;
+}
+
+/* Make sure each rmap has a corresponding bmbt entry. */
+STATIC int
+xfs_scrub_bmap_check_rmaps(
+	struct xfs_scrub_context	*sc,
+	int				whichfork)
+{
+	loff_t				size;
+	xfs_agnumber_t			agno;
+	int				error;
+
+	if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
+	    whichfork == XFS_COW_FORK ||
+	    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
+		return 0;
+
+	/* Don't support realtime rmap checks yet. */
+	if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
+		return 0;
+
+	/*
+	 * Only do this for complex maps that are in btree format, or for
+	 * situations where we would seem to have a size but zero extents.
+	 * The inode repair code can zap broken iforks, which means we have
+	 * to flag this bmap as corrupt if there are rmaps that need to be
+	 * reattached.
+	 */
+	switch (whichfork) {
+	case XFS_DATA_FORK:
+		size = i_size_read(VFS_I(sc->ip));
+		break;
+	case XFS_ATTR_FORK:
+		size = XFS_IFORK_Q(sc->ip);
+		break;
+	default:
+		size = 0;
+		break;
+	}
+	if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
+	    (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
+		return 0;
+
+	for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
+		error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno);
+		if (error)
+			return error;
+		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+			break;
+	}
+
+	return 0;
+}
+
 /*
  * Scrub an inode fork's block mappings.
  *
@@ -463,7 +627,7 @@ xfs_scrub_bmap(
 		break;
 	case XFS_ATTR_FORK:
 		if (!ifp)
-			goto out;
+			goto out_check_rmap;
 		if (!xfs_sb_version_hasattr(&mp->m_sb) &&
 		    !xfs_sb_version_hasattr2(&mp->m_sb))
 			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
@@ -534,6 +698,10 @@ xfs_scrub_bmap(
 			goto out;
 	}
 
+out_check_rmap:
+	error = xfs_scrub_bmap_check_rmaps(sc, whichfork);
+	if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error))
+		goto out;
 out:
 	return error;
 }

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v2 10/9] xfs: don't accept inode buffers with suspicious unlinked chains
  2018-03-21  3:21 ` [PATCH 10/9] xfs: don't accept inode buffers with suspicious unlinked chains Darrick J. Wong
  2018-03-21 17:43   ` Brian Foster
@ 2018-03-22  6:08   ` Darrick J. Wong
  2018-03-22 14:34     ` Brian Foster
  1 sibling, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22  6:08 UTC (permalink / raw)
  To: linux-xfs; +Cc: Brian Foster

From: Darrick J. Wong <darrick.wong@oracle.com>

When we're verifying inode buffers, sanity-check the unlinked pointer.
We don't want to run the risk of trying to purge something that's
obviously broken.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: fix merge problems, hopefully; this accidentally landed in the wrong
place in my patch stack
---
 fs/xfs/libxfs/xfs_inode_buf.c |   10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index cdd4c1d..f0cfcd79 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -93,20 +93,26 @@ xfs_inode_buf_verify(
 	bool		readahead)
 {
 	struct xfs_mount *mp = bp->b_target->bt_mount;
+	xfs_agnumber_t	agno;
 	int		i;
 	int		ni;
 
 	/*
 	 * Validate the magic number and version of every inode in the buffer
 	 */
+	agno = xfs_daddr_to_agno(mp, XFS_BUF_ADDR(bp));
 	ni = XFS_BB_TO_FSB(mp, bp->b_length) * mp->m_sb.sb_inopblock;
 	for (i = 0; i < ni; i++) {
 		int		di_ok;
 		xfs_dinode_t	*dip;
+		xfs_agino_t	unlinked_ino;
 
 		dip = xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog));
+		unlinked_ino = be32_to_cpu(dip->di_next_unlinked);
 		di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) &&
-			xfs_dinode_good_version(mp, dip->di_version);
+			xfs_dinode_good_version(mp, dip->di_version) &&
+			(unlinked_ino == NULLAGINO ||
+			 xfs_verify_agino(mp, agno, unlinked_ino));
 		if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
 						XFS_ERRTAG_ITOBP_INOTOBP))) {
 			if (readahead) {
@@ -124,9 +130,9 @@ xfs_inode_buf_verify(
 			xfs_buf_verifier_error(bp, -EFSCORRUPTED,
 					__func__, dip, sizeof(*dip),
 					NULL);
+			return;
 		}
 	}
-	xfs_inobp_check(mp, bp);
 }
 
 

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH 12/9] xfs: xfs_scrub_iallocbt_xref_rmap_inodes should use xref_set_corrupt
  2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
                   ` (10 preceding siblings ...)
  2018-03-21  3:21 ` [PATCH 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode Darrick J. Wong
@ 2018-03-22  6:19 ` Darrick J. Wong
  2018-03-22 14:34   ` Brian Foster
  11 siblings, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22  6:19 UTC (permalink / raw)
  To: linux-xfs

From: Darrick J. Wong <darrick.wong@oracle.com>

In xfs_scrub_iallocbt_xref_rmap_inodes we're checking inodes against
rmap records, so we should use xfs_scrub_btree_xref_set_corrupt if we
encounter discrepancies here so that we know that it's a cross
referencing error, not necessarily a corruption in the inobt itself.

The userspace xfs_scrub program will try to repair outright corruptions
in the agi/inobt prior to phase 3 so that the inode scan will proceed.
If only a cross-referencing error is noted, the repair program defers
the repair attempt until it can check the other space metadata at least
once.

It is therefore essential that the inobt scrubber can correctly
distinguish between corruptions and "unable to cross-reference something
else with this inobt".  The same reasoning applies to "xfs: record inode
buf errors as a xref error in inobt scrubber".

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/scrub/ialloc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 32e0d1a..106ca4b 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -434,7 +434,7 @@ xfs_scrub_iallocbt_xref_rmap_inodes(
 	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
 		return;
 	if (blocks != inode_blocks)
-		xfs_scrub_btree_set_corrupt(sc, sc->sa.ino_cur, 0);
+		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
 }
 
 /* Scrub the inode btrees for some AG. */

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v2 8/9] xfs: record inode buf errors as a xref error in inode scrubber
  2018-03-15  0:30 ` [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber Darrick J. Wong
  2018-03-21 17:42   ` Brian Foster
@ 2018-03-22  6:24   ` Darrick J. Wong
  2018-03-22 14:34     ` Brian Foster
  1 sibling, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22  6:24 UTC (permalink / raw)
  To: linux-xfs; +Cc: Brian Foster

From: Darrick J. Wong <darrick.wong@oracle.com>

During the inode btree scrubs we try to confirm the freemask bits
against the inode records.  If the inode buffer read fails, this is a
cross-referencing error, not a corruption of the inode btree itself.
Use the xref_process_error call here.  Found via core.version middlebit
fuzz in xfs/415.

The userspace xfs_scrub program will try to repair outright corruptions
in the agi/inobt prior to phase 3 so that the inode scan will proceed.
If only a cross-referencing error is noted, the repair program defers
the repair attempt until it can check the other space metadata at least
once.

It is therefore essential that the inobt scrubber can correctly
distinguish between corruptions and "unable to cross-reference something
else with this inobt".

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: clarify why we're doing this in the commit message
---
 fs/xfs/scrub/ialloc.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
index 63ab3f9..32e0d1a 100644
--- a/fs/xfs/scrub/ialloc.c
+++ b/fs/xfs/scrub/ialloc.c
@@ -259,7 +259,8 @@ xfs_scrub_iallocbt_check_freemask(
 
 		error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap,
 				&dip, &bp, 0, 0);
-		if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error))
+		if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur, 0,
+				&error))
 			continue;
 
 		/* Which inodes are free? */

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-22  5:59   ` [PATCH v2 " Darrick J. Wong
@ 2018-03-22 14:33     ` Brian Foster
  2018-03-22 17:23       ` Darrick J. Wong
  2018-03-22 22:04     ` Dave Chinner
  1 sibling, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-22 14:33 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 10:59:12PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
> it doesn't make sense.  Since a carefully crafted fuzzed image can cause
> the kernel to crash after blowing a bunch of assertions, let's move
> those checks into a validator function and rig everything up to return
> EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
> xfs/391.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v2: release buffers defensively, fix some formatting weirdness
> ---
>  fs/xfs/libxfs/xfs_dir2.h       |    2 +
>  fs/xfs/libxfs/xfs_dir2_block.c |   49 +++++++++++++++----------
>  fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
>  fs/xfs/libxfs/xfs_dir2_leaf.c  |    6 +++
>  fs/xfs/libxfs/xfs_dir2_node.c  |    9 ++++-
>  5 files changed, 103 insertions(+), 41 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
> index 388d67c..989e95a 100644
> --- a/fs/xfs/libxfs/xfs_dir2.h
> +++ b/fs/xfs/libxfs/xfs_dir2.h
> @@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
>  extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
>  		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
>  		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
> -extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
> +extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
>  		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
>  		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
>  		int *needlogp, int *needscanp);
> diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
> index 2da86a3..e8256e2 100644
> --- a/fs/xfs/libxfs/xfs_dir2_block.c
> +++ b/fs/xfs/libxfs/xfs_dir2_block.c
...
> @@ -541,9 +545,11 @@ xfs_dir2_block_addname(
>  	/*
>  	 * Mark space for the data entry used.
>  	 */
> -	xfs_dir2_data_use_free(args, bp, dup,
> +	error = xfs_dir2_data_use_free(args, bp, dup,
>  		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
>  		(xfs_dir2_data_aoff_t)len, &needlog, &needscan);

Still several of these broken alignments (i.e., the ones where the
'error = ...' indents the first line).

Brian

> +	if (error)
> +		return error;
>  	/*
>  	 * Create the new data entry.
>  	 */
> @@ -997,8 +1003,10 @@ xfs_dir2_leaf_to_block(
>  	/*
>  	 * Use up the space at the end of the block (blp/btp).
>  	 */
> -	xfs_dir2_data_use_free(args, dbp, dup, args->geo->blksize - size, size,
> -		&needlog, &needscan);
> +	error = xfs_dir2_data_use_free(args, dbp, dup,
> +		args->geo->blksize - size, size, &needlog, &needscan);
> +	if (error)
> +		return error;
>  	/*
>  	 * Initialize the block tail.
>  	 */
> @@ -1110,18 +1118,14 @@ xfs_dir2_sf_to_block(
>  	 * Add block 0 to the inode.
>  	 */
>  	error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
> -	if (error) {
> -		kmem_free(sfp);
> -		return error;
> -	}
> +	if (error)
> +		goto out_free;
>  	/*
>  	 * Initialize the data block, then convert it to block format.
>  	 */
>  	error = xfs_dir3_data_init(args, blkno, &bp);
> -	if (error) {
> -		kmem_free(sfp);
> -		return error;
> -	}
> +	if (error)
> +		goto out_free;
>  	xfs_dir3_block_init(mp, tp, bp, dp);
>  	hdr = bp->b_addr;
>  
> @@ -1136,8 +1140,10 @@ xfs_dir2_sf_to_block(
>  	 */
>  	dup = dp->d_ops->data_unused_p(hdr);
>  	needlog = needscan = 0;
> -	xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
> +	error = xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
>  			       i, &needlog, &needscan);
> +	if (error)
> +		goto out_free;
>  	ASSERT(needscan == 0);
>  	/*
>  	 * Fill in the tail.
> @@ -1150,9 +1156,11 @@ xfs_dir2_sf_to_block(
>  	/*
>  	 * Remove the freespace, we'll manage it.
>  	 */
> -	xfs_dir2_data_use_free(args, bp, dup,
> +	error = xfs_dir2_data_use_free(args, bp, dup,
>  		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
>  		be16_to_cpu(dup->length), &needlog, &needscan);
> +	if (error)
> +		goto out_free;
>  	/*
>  	 * Create entry for .
>  	 */
> @@ -1256,4 +1264,7 @@ xfs_dir2_sf_to_block(
>  	xfs_dir2_block_log_tail(tp, bp);
>  	xfs_dir3_data_check(dp, bp);
>  	return 0;
> +out_free:
> +	kmem_free(sfp);
> +	return error;
>  }
> diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
> index 9202794..167503d 100644
> --- a/fs/xfs/libxfs/xfs_dir2_data.c
> +++ b/fs/xfs/libxfs/xfs_dir2_data.c
> @@ -932,10 +932,51 @@ xfs_dir2_data_make_free(
>  	*needscanp = needscan;
>  }
>  
> +/* Check our free data for obvious signs of corruption. */
> +static inline xfs_failaddr_t
> +xfs_dir2_data_check_free(
> +	struct xfs_dir2_data_hdr	*hdr,
> +	struct xfs_dir2_data_unused	*dup,
> +	xfs_dir2_data_aoff_t		offset,
> +	xfs_dir2_data_aoff_t		len)
> +{
> +	if (hdr->magic != cpu_to_be32(XFS_DIR2_DATA_MAGIC) &&
> +	    hdr->magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC) &&
> +	    hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) &&
> +	    hdr->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
> +		return __this_address;
> +	if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG)
> +		return __this_address;
> +	if (offset < (char *)dup - (char *)hdr)
> +		return __this_address;
> +	if (offset + len > (char *)dup + be16_to_cpu(dup->length) - (char *)hdr)
> +		return __this_address;
> +	if ((char *)dup - (char *)hdr !=
> +	    be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
> +		return __this_address;
> +	return NULL;
> +}
> +
> +/* Sanity-check a new bestfree entry. */
> +static inline xfs_failaddr_t
> +xfs_dir2_data_check_new_free(
> +	struct xfs_dir2_data_hdr	*hdr,
> +	struct xfs_dir2_data_free	*dfp,
> +	struct xfs_dir2_data_unused	*newdup)
> +{
> +	if (dfp == NULL)
> +		return __this_address;
> +	if (dfp->length != newdup->length)
> +		return __this_address;
> +	if (be16_to_cpu(dfp->offset) != (char *)newdup - (char *)hdr)
> +		return __this_address;
> +	return NULL;
> +}
> +
>  /*
>   * Take a byte range out of an existing unused space and make it un-free.
>   */
> -void
> +int
>  xfs_dir2_data_use_free(
>  	struct xfs_da_args	*args,
>  	struct xfs_buf		*bp,
> @@ -947,23 +988,19 @@ xfs_dir2_data_use_free(
>  {
>  	xfs_dir2_data_hdr_t	*hdr;		/* data block header */
>  	xfs_dir2_data_free_t	*dfp;		/* bestfree pointer */
> +	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
> +	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
> +	struct xfs_dir2_data_free *bf;
> +	xfs_failaddr_t		fa;
>  	int			matchback;	/* matches end of freespace */
>  	int			matchfront;	/* matches start of freespace */
>  	int			needscan;	/* need to regen bestfree */
> -	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
> -	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
>  	int			oldlen;		/* old unused entry's length */
> -	struct xfs_dir2_data_free *bf;
>  
>  	hdr = bp->b_addr;
> -	ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
> -	       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
> -	       hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
> -	       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
> -	ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
> -	ASSERT(offset >= (char *)dup - (char *)hdr);
> -	ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)hdr);
> -	ASSERT((char *)dup - (char *)hdr == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
> +	fa = xfs_dir2_data_check_free(hdr, dup, offset, len);
> +	if (fa)
> +		goto corrupt;
>  	/*
>  	 * Look up the entry in the bestfree table.
>  	 */
> @@ -1008,9 +1045,9 @@ xfs_dir2_data_use_free(
>  			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
>  			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
>  						       needlogp);
> -			ASSERT(dfp != NULL);
> -			ASSERT(dfp->length == newdup->length);
> -			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
> +			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
> +			if (fa)
> +				goto corrupt;
>  			/*
>  			 * If we got inserted at the last slot,
>  			 * that means we don't know if there was a better
> @@ -1036,9 +1073,9 @@ xfs_dir2_data_use_free(
>  			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
>  			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
>  						       needlogp);
> -			ASSERT(dfp != NULL);
> -			ASSERT(dfp->length == newdup->length);
> -			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
> +			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
> +			if (fa)
> +				goto corrupt;
>  			/*
>  			 * If we got inserted at the last slot,
>  			 * that means we don't know if there was a better
> @@ -1084,6 +1121,11 @@ xfs_dir2_data_use_free(
>  		}
>  	}
>  	*needscanp = needscan;
> +	return 0;
> +corrupt:
> +	xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
> +			hdr, __FILE__, __LINE__, fa);
> +	return -EFSCORRUPTED;
>  }
>  
>  /* Find the end of the entry data in a data/block format dir block. */
> diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
> index d61d52d..f746921 100644
> --- a/fs/xfs/libxfs/xfs_dir2_leaf.c
> +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
> @@ -877,9 +877,13 @@ xfs_dir2_leaf_addname(
>  	/*
>  	 * Mark the initial part of our freespace in use for the new entry.
>  	 */
> -	xfs_dir2_data_use_free(args, dbp, dup,
> +	error = xfs_dir2_data_use_free(args, dbp, dup,
>  		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
>  		&needlog, &needscan);
> +	if (error) {
> +		xfs_trans_brelse(tp, lbp);
> +		return error;
> +	}
>  	/*
>  	 * Initialize our new entry (at last).
>  	 */
> diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
> index 0839ffe..57b6c4fa 100644
> --- a/fs/xfs/libxfs/xfs_dir2_node.c
> +++ b/fs/xfs/libxfs/xfs_dir2_node.c
> @@ -1729,6 +1729,7 @@ xfs_dir2_node_addname_int(
>  	__be16			*bests;
>  	struct xfs_dir3_icfree_hdr freehdr;
>  	struct xfs_dir2_data_free *bf;
> +	xfs_dir2_data_aoff_t	aoff;
>  
>  	dp = args->dp;
>  	mp = dp->i_mount;
> @@ -2023,9 +2024,13 @@ xfs_dir2_node_addname_int(
>  	/*
>  	 * Mark the first part of the unused space, inuse for us.
>  	 */
> -	xfs_dir2_data_use_free(args, dbp, dup,
> -		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
> +	aoff = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
> +	error = xfs_dir2_data_use_free(args, dbp, dup, aoff, length,
>  		&needlog, &needscan);
> +	if (error) {
> +		xfs_trans_brelse(tp, dbp);
> +		return error;
> +	}
>  	/*
>  	 * Fill in the new entry and log it.
>  	 */
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 2/9] xfs: refactor bmap record valiation
  2018-03-22  6:01   ` [PATCH v2 " Darrick J. Wong
@ 2018-03-22 14:33     ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-22 14:33 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 11:01:13PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Refactor the bmap validator into a more complete helper that looks for
> extents that run off the end of the device, overflow into the next AG,
> or have invalid flag states.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v2: defer repair-oriented wrappers until they're needed
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c       |   46 +++++++++++++++++++++++++++++++++++++---
>  fs/xfs/libxfs/xfs_bmap.h       |    3 +++
>  fs/xfs/libxfs/xfs_bmap_btree.h |   14 ------------
>  fs/xfs/libxfs/xfs_inode_fork.c |   12 +++++++---
>  4 files changed, 54 insertions(+), 21 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index daae00e..91214f2 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1261,11 +1261,15 @@ xfs_iread_extents(
>  		 */
>  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
>  		for (j = 0; j < num_recs; j++, frp++, i++) {
> +			xfs_failaddr_t	fa;
> +
>  			xfs_bmbt_disk_get_all(frp, &new);
> -			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
> -				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> -						 XFS_ERRLEVEL_LOW, mp);
> +			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
> +			if (fa) {
>  				error = -EFSCORRUPTED;
> +				xfs_inode_verifier_error(ip, error,
> +						"xfs_iread_extents(2)",
> +						frp, sizeof(*frp), fa);
>  				goto out_brelse;
>  			}
>  			xfs_iext_insert(ip, &icur, &new, state);
> @@ -6154,3 +6158,39 @@ xfs_bmap_finish_one(
>  
>  	return error;
>  }
> +
> +/* Check that an inode's extent does not have invalid flags or bad ranges. */
> +xfs_failaddr_t
> +xfs_bmap_validate_extent(
> +	struct xfs_inode	*ip,
> +	int			whichfork,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	struct xfs_mount	*mp = ip->i_mount;
> +	xfs_fsblock_t		endfsb;
> +	bool			isrt;
> +
> +	isrt = XFS_IS_REALTIME_INODE(ip);
> +	endfsb = irec->br_startblock + irec->br_blockcount - 1;
> +	if (isrt) {
> +		if (!xfs_verify_rtbno(mp, irec->br_startblock))
> +			return __this_address;
> +		if (!xfs_verify_rtbno(mp, endfsb))
> +			return __this_address;
> +	} else {
> +		if (!xfs_verify_fsbno(mp, irec->br_startblock))
> +			return __this_address;
> +		if (!xfs_verify_fsbno(mp, endfsb))
> +			return __this_address;
> +		if (XFS_FSB_TO_AGNO(mp, irec->br_startblock) !=
> +		    XFS_FSB_TO_AGNO(mp, endfsb))
> +			return __this_address;
> +	}
> +	if (irec->br_state != XFS_EXT_NORM) {
> +		if (whichfork != XFS_DATA_FORK)
> +			return __this_address;
> +		if (!xfs_sb_version_hasextflgbit(&mp->m_sb))
> +			return __this_address;
> +	}
> +	return NULL;
> +}
> diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> index e36d757..f3be641 100644
> --- a/fs/xfs/libxfs/xfs_bmap.h
> +++ b/fs/xfs/libxfs/xfs_bmap.h
> @@ -274,4 +274,7 @@ static inline int xfs_bmap_fork_to_state(int whichfork)
>  	}
>  }
>  
> +xfs_failaddr_t xfs_bmap_validate_extent(struct xfs_inode *ip, int whichfork,
> +		struct xfs_bmbt_irec *irec);
> +
>  #endif	/* __XFS_BMAP_H__ */
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> index 135b8c5..e450574 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> @@ -118,18 +118,4 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
>  extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
>  		struct xfs_trans *, struct xfs_inode *, int);
>  
> -/*
> - * Check that the extent does not contain an invalid unwritten extent flag.
> - */
> -static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> -		struct xfs_bmbt_irec *irec)
> -{
> -	if (irec->br_state == XFS_EXT_NORM)
> -		return true;
> -	if (whichfork == XFS_DATA_FORK &&
> -	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> -		return true;
> -	return false;
> -}
> -
>  #endif	/* __XFS_BMAP_BTREE_H__ */
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 866d2861..613fba2 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -245,10 +245,14 @@ xfs_iformat_extents(
>  
>  		xfs_iext_first(ifp, &icur);
>  		for (i = 0; i < nex; i++, dp++) {
> +			xfs_failaddr_t	fa;
> +
>  			xfs_bmbt_disk_get_all(dp, &new);
> -			if (!xfs_bmbt_validate_extent(mp, whichfork, &new)) {
> -				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> -						 XFS_ERRLEVEL_LOW, mp);
> +			fa = xfs_bmap_validate_extent(ip, whichfork, &new);
> +			if (fa) {
> +				xfs_inode_verifier_error(ip, -EFSCORRUPTED,
> +						"xfs_iformat_extents(2)",
> +						dp, sizeof(*dp), fa);
>  				return -EFSCORRUPTED;
>  			}
>  
> @@ -595,7 +599,7 @@ xfs_iextents_copy(
>  	for_each_xfs_iext(ifp, &icur, &rec) {
>  		if (isnullstartblock(rec.br_startblock))
>  			continue;
> -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, &rec));
> +		ASSERT(xfs_bmap_validate_extent(ip, whichfork, &rec) == NULL);
>  		xfs_bmbt_disk_set_all(dp, &rec);
>  		trace_xfs_write_extent(ip, &icur, state, _RET_IP_);
>  		copied += sizeof(struct xfs_bmbt_rec);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files
  2018-03-22  6:02   ` [PATCH v2 " Darrick J. Wong
@ 2018-03-22 14:33     ` Brian Foster
  2018-03-22 17:35       ` Darrick J. Wong
  0 siblings, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-22 14:33 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 11:02:18PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> When we're scanning an extent mapping inode fork, ensure that every rmap
> record for this ifork has a corresponding bmbt record too.  This
> (mostly) provides the ability to cross-reference rmap records with bmap
> data.  The rmap scrubber cannot do the xref on its own because that
> requires taking an ilock with the agf lock held, which violates our
> locking order rules (inode, then agf).
> 
> Note that we only do this for forks that are in btree format due to the
> increased complexity; or forks that should have data but suspiciously
> have zero extents because the inode could have just had its iforks
> zapped by the inode repair code and now we need to reclaim the old
> extents.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v2: enhance comments, fix XFS_BTREE_ERROR usage
> ---
>  fs/xfs/scrub/bmap.c |  170 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 169 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> index d002821..75ea2d6 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -37,6 +37,7 @@
>  #include "xfs_bmap_util.h"
>  #include "xfs_bmap_btree.h"
>  #include "xfs_rmap.h"
> +#include "xfs_rmap_btree.h"
>  #include "xfs_refcount.h"
>  #include "scrub/xfs_scrub.h"
>  #include "scrub/scrub.h"
> @@ -423,6 +424,169 @@ xfs_scrub_bmap_btree(
>  	return error;
>  }
>  
...
> +/* Make sure each rmap has a corresponding bmbt entry. */
> +STATIC int
> +xfs_scrub_bmap_check_ag_rmaps(
> +	struct xfs_scrub_context	*sc,
> +	int				whichfork,
> +	xfs_agnumber_t			agno)
> +{
> +	struct xfs_scrub_bmap_check_rmap_info	sbcri;
> +	struct xfs_btree_cur		*cur;
> +	struct xfs_buf			*agf;
> +	int				error;
> +
> +	error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
> +	if (error)
> +		return error;
> +
> +	cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
> +	if (!cur) {
> +		error = -ENOMEM;
> +		goto out_agf;
> +	}
> +
> +	sbcri.sc = sc;
> +	sbcri.whichfork = whichfork;
> +	error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri);
> +	if (error == XFS_BTREE_QUERY_RANGE_ABORT)
> +		error = 0;
> +
> +	xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);

So I assume it's intentional that we don't care about _QUERY_RANGE_ABORT
here? Otherwise looks fine.

Brian

> +out_agf:
> +	xfs_trans_brelse(sc->tp, agf);
> +	return error;
> +}
> +
> +/* Make sure each rmap has a corresponding bmbt entry. */
> +STATIC int
> +xfs_scrub_bmap_check_rmaps(
> +	struct xfs_scrub_context	*sc,
> +	int				whichfork)
> +{
> +	loff_t				size;
> +	xfs_agnumber_t			agno;
> +	int				error;
> +
> +	if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
> +	    whichfork == XFS_COW_FORK ||
> +	    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
> +		return 0;
> +
> +	/* Don't support realtime rmap checks yet. */
> +	if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
> +		return 0;
> +
> +	/*
> +	 * Only do this for complex maps that are in btree format, or for
> +	 * situations where we would seem to have a size but zero extents.
> +	 * The inode repair code can zap broken iforks, which means we have
> +	 * to flag this bmap as corrupt if there are rmaps that need to be
> +	 * reattached.
> +	 */
> +	switch (whichfork) {
> +	case XFS_DATA_FORK:
> +		size = i_size_read(VFS_I(sc->ip));
> +		break;
> +	case XFS_ATTR_FORK:
> +		size = XFS_IFORK_Q(sc->ip);
> +		break;
> +	default:
> +		size = 0;
> +		break;
> +	}
> +	if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
> +	    (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
> +		return 0;
> +
> +	for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
> +		error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno);
> +		if (error)
> +			return error;
> +		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> +			break;
> +	}
> +
> +	return 0;
> +}
> +
>  /*
>   * Scrub an inode fork's block mappings.
>   *
> @@ -463,7 +627,7 @@ xfs_scrub_bmap(
>  		break;
>  	case XFS_ATTR_FORK:
>  		if (!ifp)
> -			goto out;
> +			goto out_check_rmap;
>  		if (!xfs_sb_version_hasattr(&mp->m_sb) &&
>  		    !xfs_sb_version_hasattr2(&mp->m_sb))
>  			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
> @@ -534,6 +698,10 @@ xfs_scrub_bmap(
>  			goto out;
>  	}
>  
> +out_check_rmap:
> +	error = xfs_scrub_bmap_check_rmaps(sc, whichfork);
> +	if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error))
> +		goto out;
>  out:
>  	return error;
>  }
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber
  2018-03-21 20:50     ` Darrick J. Wong
@ 2018-03-22 14:34       ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-22 14:34 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 01:50:08PM -0700, Darrick J. Wong wrote:
> On Wed, Mar 21, 2018 at 01:42:49PM -0400, Brian Foster wrote:
> > On Wed, Mar 14, 2018 at 05:30:20PM -0700, Darrick J. Wong wrote:
> > > From: Darrick J. Wong <darrick.wong@oracle.com>
> > > 
> > > During the inode btree scrubs we try to confirm the freemask bits
> > > against the inode records.  If the inode buffer read fails, this is a
> > > cross-referencing error, not a corruption of the inode btree itself.
> > > Use the xref_process_error call here.  Found via core.version middlebit
> > > fuzz in xfs/415.
> > > 
> > 
> > What's the difference/consequence of this change?
> 
> For now, more accurate reporting of the outcome of scrubbing the inobt.
> If xfs_imap_to_bp returns EFSCORRUPTED that means that it found the
> inode buffer, loaded it, and the verifier for the inode records failed.
> Therefore, we need to communicate to userspace that the inobt looks ok
> but we were unable to confirm that something the inobt points to (the
> inode chunk) agrees with the the pointer.  That's an xref error, not a
> corruption error.
> 
> Once we start on the xfs_scrub repair support, you'll see in the patches
> that prior to scanning the inodes in the filesystem, we need to know
> whether or not the agi and the inobt are ok.  If the agi or inobt scan
> shows corruption, we need to rebuild them (or abort).  If however, the
> scan only says cross-referencing error, then we conclude that the inobt
> is ok but the inodes are not, and proceed with the inode scan to find
> the bad inode and repair it.
> 

Ok, so it's a reporting/conceptual thing. Seems fine, it's just hard to
confirm that from deep in the scrub code unless the commit log explains
why the change is made (which looks like v2 does...).

Brian

> --D
> 
> > Brian
> > 
> > > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > > ---
> > >  fs/xfs/scrub/ialloc.c |    3 ++-
> > >  1 file changed, 2 insertions(+), 1 deletion(-)
> > > 
> > > 
> > > diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> > > index 63ab3f9..32e0d1a 100644
> > > --- a/fs/xfs/scrub/ialloc.c
> > > +++ b/fs/xfs/scrub/ialloc.c
> > > @@ -259,7 +259,8 @@ xfs_scrub_iallocbt_check_freemask(
> > >  
> > >  		error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap,
> > >  				&dip, &bp, 0, 0);
> > > -		if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error))
> > > +		if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur, 0,
> > > +				&error))
> > >  			continue;
> > >  
> > >  		/* Which inodes are free? */
> > > 
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 8/9] xfs: record inode buf errors as a xref error in inode scrubber
  2018-03-22  6:24   ` [PATCH v2 " Darrick J. Wong
@ 2018-03-22 14:34     ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-22 14:34 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 11:24:10PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> During the inode btree scrubs we try to confirm the freemask bits
> against the inode records.  If the inode buffer read fails, this is a
> cross-referencing error, not a corruption of the inode btree itself.
> Use the xref_process_error call here.  Found via core.version middlebit
> fuzz in xfs/415.
> 
> The userspace xfs_scrub program will try to repair outright corruptions
> in the agi/inobt prior to phase 3 so that the inode scan will proceed.
> If only a cross-referencing error is noted, the repair program defers
> the repair attempt until it can check the other space metadata at least
> once.
> 
> It is therefore essential that the inobt scrubber can correctly
> distinguish between corruptions and "unable to cross-reference something
> else with this inobt".
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v2: clarify why we're doing this in the commit message
> ---

Thanks!

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/scrub/ialloc.c |    3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> index 63ab3f9..32e0d1a 100644
> --- a/fs/xfs/scrub/ialloc.c
> +++ b/fs/xfs/scrub/ialloc.c
> @@ -259,7 +259,8 @@ xfs_scrub_iallocbt_check_freemask(
>  
>  		error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap,
>  				&dip, &bp, 0, 0);
> -		if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error))
> +		if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur, 0,
> +				&error))
>  			continue;
>  
>  		/* Which inodes are free? */
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 10/9] xfs: don't accept inode buffers with suspicious unlinked chains
  2018-03-22  6:08   ` [PATCH v2 " Darrick J. Wong
@ 2018-03-22 14:34     ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-22 14:34 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 11:08:22PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> When we're verifying inode buffers, sanity-check the unlinked pointer.
> We don't want to run the risk of trying to purge something that's
> obviously broken.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v2: fix merge problems, hopefully; this accidentally landed in the wrong
> place in my patch stack
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_inode_buf.c |   10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> index cdd4c1d..f0cfcd79 100644
> --- a/fs/xfs/libxfs/xfs_inode_buf.c
> +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> @@ -93,20 +93,26 @@ xfs_inode_buf_verify(
>  	bool		readahead)
>  {
>  	struct xfs_mount *mp = bp->b_target->bt_mount;
> +	xfs_agnumber_t	agno;
>  	int		i;
>  	int		ni;
>  
>  	/*
>  	 * Validate the magic number and version of every inode in the buffer
>  	 */
> +	agno = xfs_daddr_to_agno(mp, XFS_BUF_ADDR(bp));
>  	ni = XFS_BB_TO_FSB(mp, bp->b_length) * mp->m_sb.sb_inopblock;
>  	for (i = 0; i < ni; i++) {
>  		int		di_ok;
>  		xfs_dinode_t	*dip;
> +		xfs_agino_t	unlinked_ino;
>  
>  		dip = xfs_buf_offset(bp, (i << mp->m_sb.sb_inodelog));
> +		unlinked_ino = be32_to_cpu(dip->di_next_unlinked);
>  		di_ok = dip->di_magic == cpu_to_be16(XFS_DINODE_MAGIC) &&
> -			xfs_dinode_good_version(mp, dip->di_version);
> +			xfs_dinode_good_version(mp, dip->di_version) &&
> +			(unlinked_ino == NULLAGINO ||
> +			 xfs_verify_agino(mp, agno, unlinked_ino));
>  		if (unlikely(XFS_TEST_ERROR(!di_ok, mp,
>  						XFS_ERRTAG_ITOBP_INOTOBP))) {
>  			if (readahead) {
> @@ -124,9 +130,9 @@ xfs_inode_buf_verify(
>  			xfs_buf_verifier_error(bp, -EFSCORRUPTED,
>  					__func__, dip, sizeof(*dip),
>  					NULL);
> +			return;
>  		}
>  	}
> -	xfs_inobp_check(mp, bp);
>  }
>  
>  
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode
  2018-03-21  3:21 ` [PATCH 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode Darrick J. Wong
@ 2018-03-22 14:34   ` Brian Foster
  2018-03-22 17:49     ` Darrick J. Wong
  2018-03-22 17:57   ` [PATCH v2 " Darrick J. Wong
  1 sibling, 1 reply; 53+ messages in thread
From: Brian Foster @ 2018-03-22 14:34 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Tue, Mar 20, 2018 at 08:21:58PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> If a directory's parent inode pointer doesn't point to an inode, flag
> the directory as corrupt.  Enable IGET_UNTRUSTED here so that we
> can confirm with the inobt that the inode is present and allocated.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/scrub/parent.c |    9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
> index 0d38514..d1547ca 100644
> --- a/fs/xfs/scrub/parent.c
> +++ b/fs/xfs/scrub/parent.c
> @@ -167,9 +167,14 @@ xfs_scrub_parent_validate(
>  	 * if the parent pointer erroneously points to a file, we
>  	 * can't use DONTCACHE here because DONTCACHE inodes can trigger
>  	 * immediate inactive cleanup of the inode.
> +	 *
> +	 * If _iget returns -EINVAL then the parent inode number is garbage
> +	 * and the directory is corrupt.
>  	 */
> -	error = xfs_iget(mp, sc->tp, dnum, 0, 0, &dp);
> -	if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
> +	error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp);
> +	if (error == -EINVAL)
> +		error = -EFSCORRUPTED;
> +	if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))

Ok, but it looks like we're also changing the error semantics here
(xref_process_error() -> process_error()). Could you comment on that in
the commit log as well?

With that, looks fine:

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  		goto out;
>  	if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) {
>  		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 12/9] xfs: xfs_scrub_iallocbt_xref_rmap_inodes should use xref_set_corrupt
  2018-03-22  6:19 ` [PATCH 12/9] xfs: xfs_scrub_iallocbt_xref_rmap_inodes should use xref_set_corrupt Darrick J. Wong
@ 2018-03-22 14:34   ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-22 14:34 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Wed, Mar 21, 2018 at 11:19:16PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> In xfs_scrub_iallocbt_xref_rmap_inodes we're checking inodes against
> rmap records, so we should use xfs_scrub_btree_xref_set_corrupt if we
> encounter discrepancies here so that we know that it's a cross
> referencing error, not necessarily a corruption in the inobt itself.
> 
> The userspace xfs_scrub program will try to repair outright corruptions
> in the agi/inobt prior to phase 3 so that the inode scan will proceed.
> If only a cross-referencing error is noted, the repair program defers
> the repair attempt until it can check the other space metadata at least
> once.
> 
> It is therefore essential that the inobt scrubber can correctly
> distinguish between corruptions and "unable to cross-reference something
> else with this inobt".  The same reasoning applies to "xfs: record inode
> buf errors as a xref error in inobt scrubber".
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/scrub/ialloc.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c
> index 32e0d1a..106ca4b 100644
> --- a/fs/xfs/scrub/ialloc.c
> +++ b/fs/xfs/scrub/ialloc.c
> @@ -434,7 +434,7 @@ xfs_scrub_iallocbt_xref_rmap_inodes(
>  	if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur))
>  		return;
>  	if (blocks != inode_blocks)
> -		xfs_scrub_btree_set_corrupt(sc, sc->sa.ino_cur, 0);
> +		xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
>  }
>  
>  /* Scrub the inode btrees for some AG. */
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-22 14:33     ` Brian Foster
@ 2018-03-22 17:23       ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22 17:23 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Thu, Mar 22, 2018 at 10:33:40AM -0400, Brian Foster wrote:
> On Wed, Mar 21, 2018 at 10:59:12PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
> > it doesn't make sense.  Since a carefully crafted fuzzed image can cause
> > the kernel to crash after blowing a bunch of assertions, let's move
> > those checks into a validator function and rig everything up to return
> > EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
> > xfs/391.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> > v2: release buffers defensively, fix some formatting weirdness
> > ---
> >  fs/xfs/libxfs/xfs_dir2.h       |    2 +
> >  fs/xfs/libxfs/xfs_dir2_block.c |   49 +++++++++++++++----------
> >  fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
> >  fs/xfs/libxfs/xfs_dir2_leaf.c  |    6 +++
> >  fs/xfs/libxfs/xfs_dir2_node.c  |    9 ++++-
> >  5 files changed, 103 insertions(+), 41 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
> > index 388d67c..989e95a 100644
> > --- a/fs/xfs/libxfs/xfs_dir2.h
> > +++ b/fs/xfs/libxfs/xfs_dir2.h
> > @@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
> >  extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
> >  		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
> >  		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
> > -extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
> > +extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
> >  		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
> >  		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
> >  		int *needlogp, int *needscanp);
> > diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
> > index 2da86a3..e8256e2 100644
> > --- a/fs/xfs/libxfs/xfs_dir2_block.c
> > +++ b/fs/xfs/libxfs/xfs_dir2_block.c
> ...
> > @@ -541,9 +545,11 @@ xfs_dir2_block_addname(
> >  	/*
> >  	 * Mark space for the data entry used.
> >  	 */
> > -	xfs_dir2_data_use_free(args, bp, dup,
> > +	error = xfs_dir2_data_use_free(args, bp, dup,
> >  		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
> >  		(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
> 
> Still several of these broken alignments (i.e., the ones where the
> 'error = ...' indents the first line).

Ok, will fix them all up.

--D

> Brian
> 
> > +	if (error)
> > +		return error;
> >  	/*
> >  	 * Create the new data entry.
> >  	 */
> > @@ -997,8 +1003,10 @@ xfs_dir2_leaf_to_block(
> >  	/*
> >  	 * Use up the space at the end of the block (blp/btp).
> >  	 */
> > -	xfs_dir2_data_use_free(args, dbp, dup, args->geo->blksize - size, size,
> > -		&needlog, &needscan);
> > +	error = xfs_dir2_data_use_free(args, dbp, dup,
> > +		args->geo->blksize - size, size, &needlog, &needscan);
> > +	if (error)
> > +		return error;
> >  	/*
> >  	 * Initialize the block tail.
> >  	 */
> > @@ -1110,18 +1118,14 @@ xfs_dir2_sf_to_block(
> >  	 * Add block 0 to the inode.
> >  	 */
> >  	error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
> > -	if (error) {
> > -		kmem_free(sfp);
> > -		return error;
> > -	}
> > +	if (error)
> > +		goto out_free;
> >  	/*
> >  	 * Initialize the data block, then convert it to block format.
> >  	 */
> >  	error = xfs_dir3_data_init(args, blkno, &bp);
> > -	if (error) {
> > -		kmem_free(sfp);
> > -		return error;
> > -	}
> > +	if (error)
> > +		goto out_free;
> >  	xfs_dir3_block_init(mp, tp, bp, dp);
> >  	hdr = bp->b_addr;
> >  
> > @@ -1136,8 +1140,10 @@ xfs_dir2_sf_to_block(
> >  	 */
> >  	dup = dp->d_ops->data_unused_p(hdr);
> >  	needlog = needscan = 0;
> > -	xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
> > +	error = xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
> >  			       i, &needlog, &needscan);
> > +	if (error)
> > +		goto out_free;
> >  	ASSERT(needscan == 0);
> >  	/*
> >  	 * Fill in the tail.
> > @@ -1150,9 +1156,11 @@ xfs_dir2_sf_to_block(
> >  	/*
> >  	 * Remove the freespace, we'll manage it.
> >  	 */
> > -	xfs_dir2_data_use_free(args, bp, dup,
> > +	error = xfs_dir2_data_use_free(args, bp, dup,
> >  		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
> >  		be16_to_cpu(dup->length), &needlog, &needscan);
> > +	if (error)
> > +		goto out_free;
> >  	/*
> >  	 * Create entry for .
> >  	 */
> > @@ -1256,4 +1264,7 @@ xfs_dir2_sf_to_block(
> >  	xfs_dir2_block_log_tail(tp, bp);
> >  	xfs_dir3_data_check(dp, bp);
> >  	return 0;
> > +out_free:
> > +	kmem_free(sfp);
> > +	return error;
> >  }
> > diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
> > index 9202794..167503d 100644
> > --- a/fs/xfs/libxfs/xfs_dir2_data.c
> > +++ b/fs/xfs/libxfs/xfs_dir2_data.c
> > @@ -932,10 +932,51 @@ xfs_dir2_data_make_free(
> >  	*needscanp = needscan;
> >  }
> >  
> > +/* Check our free data for obvious signs of corruption. */
> > +static inline xfs_failaddr_t
> > +xfs_dir2_data_check_free(
> > +	struct xfs_dir2_data_hdr	*hdr,
> > +	struct xfs_dir2_data_unused	*dup,
> > +	xfs_dir2_data_aoff_t		offset,
> > +	xfs_dir2_data_aoff_t		len)
> > +{
> > +	if (hdr->magic != cpu_to_be32(XFS_DIR2_DATA_MAGIC) &&
> > +	    hdr->magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC) &&
> > +	    hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) &&
> > +	    hdr->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
> > +		return __this_address;
> > +	if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG)
> > +		return __this_address;
> > +	if (offset < (char *)dup - (char *)hdr)
> > +		return __this_address;
> > +	if (offset + len > (char *)dup + be16_to_cpu(dup->length) - (char *)hdr)
> > +		return __this_address;
> > +	if ((char *)dup - (char *)hdr !=
> > +	    be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
> > +		return __this_address;
> > +	return NULL;
> > +}
> > +
> > +/* Sanity-check a new bestfree entry. */
> > +static inline xfs_failaddr_t
> > +xfs_dir2_data_check_new_free(
> > +	struct xfs_dir2_data_hdr	*hdr,
> > +	struct xfs_dir2_data_free	*dfp,
> > +	struct xfs_dir2_data_unused	*newdup)
> > +{
> > +	if (dfp == NULL)
> > +		return __this_address;
> > +	if (dfp->length != newdup->length)
> > +		return __this_address;
> > +	if (be16_to_cpu(dfp->offset) != (char *)newdup - (char *)hdr)
> > +		return __this_address;
> > +	return NULL;
> > +}
> > +
> >  /*
> >   * Take a byte range out of an existing unused space and make it un-free.
> >   */
> > -void
> > +int
> >  xfs_dir2_data_use_free(
> >  	struct xfs_da_args	*args,
> >  	struct xfs_buf		*bp,
> > @@ -947,23 +988,19 @@ xfs_dir2_data_use_free(
> >  {
> >  	xfs_dir2_data_hdr_t	*hdr;		/* data block header */
> >  	xfs_dir2_data_free_t	*dfp;		/* bestfree pointer */
> > +	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
> > +	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
> > +	struct xfs_dir2_data_free *bf;
> > +	xfs_failaddr_t		fa;
> >  	int			matchback;	/* matches end of freespace */
> >  	int			matchfront;	/* matches start of freespace */
> >  	int			needscan;	/* need to regen bestfree */
> > -	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
> > -	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
> >  	int			oldlen;		/* old unused entry's length */
> > -	struct xfs_dir2_data_free *bf;
> >  
> >  	hdr = bp->b_addr;
> > -	ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
> > -	       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
> > -	       hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
> > -	       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
> > -	ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
> > -	ASSERT(offset >= (char *)dup - (char *)hdr);
> > -	ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)hdr);
> > -	ASSERT((char *)dup - (char *)hdr == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
> > +	fa = xfs_dir2_data_check_free(hdr, dup, offset, len);
> > +	if (fa)
> > +		goto corrupt;
> >  	/*
> >  	 * Look up the entry in the bestfree table.
> >  	 */
> > @@ -1008,9 +1045,9 @@ xfs_dir2_data_use_free(
> >  			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
> >  			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
> >  						       needlogp);
> > -			ASSERT(dfp != NULL);
> > -			ASSERT(dfp->length == newdup->length);
> > -			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
> > +			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
> > +			if (fa)
> > +				goto corrupt;
> >  			/*
> >  			 * If we got inserted at the last slot,
> >  			 * that means we don't know if there was a better
> > @@ -1036,9 +1073,9 @@ xfs_dir2_data_use_free(
> >  			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
> >  			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
> >  						       needlogp);
> > -			ASSERT(dfp != NULL);
> > -			ASSERT(dfp->length == newdup->length);
> > -			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
> > +			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
> > +			if (fa)
> > +				goto corrupt;
> >  			/*
> >  			 * If we got inserted at the last slot,
> >  			 * that means we don't know if there was a better
> > @@ -1084,6 +1121,11 @@ xfs_dir2_data_use_free(
> >  		}
> >  	}
> >  	*needscanp = needscan;
> > +	return 0;
> > +corrupt:
> > +	xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
> > +			hdr, __FILE__, __LINE__, fa);
> > +	return -EFSCORRUPTED;
> >  }
> >  
> >  /* Find the end of the entry data in a data/block format dir block. */
> > diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
> > index d61d52d..f746921 100644
> > --- a/fs/xfs/libxfs/xfs_dir2_leaf.c
> > +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
> > @@ -877,9 +877,13 @@ xfs_dir2_leaf_addname(
> >  	/*
> >  	 * Mark the initial part of our freespace in use for the new entry.
> >  	 */
> > -	xfs_dir2_data_use_free(args, dbp, dup,
> > +	error = xfs_dir2_data_use_free(args, dbp, dup,
> >  		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
> >  		&needlog, &needscan);
> > +	if (error) {
> > +		xfs_trans_brelse(tp, lbp);
> > +		return error;
> > +	}
> >  	/*
> >  	 * Initialize our new entry (at last).
> >  	 */
> > diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
> > index 0839ffe..57b6c4fa 100644
> > --- a/fs/xfs/libxfs/xfs_dir2_node.c
> > +++ b/fs/xfs/libxfs/xfs_dir2_node.c
> > @@ -1729,6 +1729,7 @@ xfs_dir2_node_addname_int(
> >  	__be16			*bests;
> >  	struct xfs_dir3_icfree_hdr freehdr;
> >  	struct xfs_dir2_data_free *bf;
> > +	xfs_dir2_data_aoff_t	aoff;
> >  
> >  	dp = args->dp;
> >  	mp = dp->i_mount;
> > @@ -2023,9 +2024,13 @@ xfs_dir2_node_addname_int(
> >  	/*
> >  	 * Mark the first part of the unused space, inuse for us.
> >  	 */
> > -	xfs_dir2_data_use_free(args, dbp, dup,
> > -		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
> > +	aoff = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
> > +	error = xfs_dir2_data_use_free(args, dbp, dup, aoff, length,
> >  		&needlog, &needscan);
> > +	if (error) {
> > +		xfs_trans_brelse(tp, dbp);
> > +		return error;
> > +	}
> >  	/*
> >  	 * Fill in the new entry and log it.
> >  	 */
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files
  2018-03-22 14:33     ` Brian Foster
@ 2018-03-22 17:35       ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22 17:35 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Thu, Mar 22, 2018 at 10:33:58AM -0400, Brian Foster wrote:
> On Wed, Mar 21, 2018 at 11:02:18PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > When we're scanning an extent mapping inode fork, ensure that every rmap
> > record for this ifork has a corresponding bmbt record too.  This
> > (mostly) provides the ability to cross-reference rmap records with bmap
> > data.  The rmap scrubber cannot do the xref on its own because that
> > requires taking an ilock with the agf lock held, which violates our
> > locking order rules (inode, then agf).
> > 
> > Note that we only do this for forks that are in btree format due to the
> > increased complexity; or forks that should have data but suspiciously
> > have zero extents because the inode could have just had its iforks
> > zapped by the inode repair code and now we need to reclaim the old
> > extents.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> > v2: enhance comments, fix XFS_BTREE_ERROR usage
> > ---
> >  fs/xfs/scrub/bmap.c |  170 +++++++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 169 insertions(+), 1 deletion(-)
> > 
> > diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> > index d002821..75ea2d6 100644
> > --- a/fs/xfs/scrub/bmap.c
> > +++ b/fs/xfs/scrub/bmap.c
> > @@ -37,6 +37,7 @@
> >  #include "xfs_bmap_util.h"
> >  #include "xfs_bmap_btree.h"
> >  #include "xfs_rmap.h"
> > +#include "xfs_rmap_btree.h"
> >  #include "xfs_refcount.h"
> >  #include "scrub/xfs_scrub.h"
> >  #include "scrub/scrub.h"
> > @@ -423,6 +424,169 @@ xfs_scrub_bmap_btree(
> >  	return error;
> >  }
> >  
> ...
> > +/* Make sure each rmap has a corresponding bmbt entry. */
> > +STATIC int
> > +xfs_scrub_bmap_check_ag_rmaps(
> > +	struct xfs_scrub_context	*sc,
> > +	int				whichfork,
> > +	xfs_agnumber_t			agno)
> > +{
> > +	struct xfs_scrub_bmap_check_rmap_info	sbcri;
> > +	struct xfs_btree_cur		*cur;
> > +	struct xfs_buf			*agf;
> > +	int				error;
> > +
> > +	error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf);
> > +	if (error)
> > +		return error;
> > +
> > +	cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno);
> > +	if (!cur) {
> > +		error = -ENOMEM;
> > +		goto out_agf;
> > +	}
> > +
> > +	sbcri.sc = sc;
> > +	sbcri.whichfork = whichfork;
> > +	error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri);
> > +	if (error == XFS_BTREE_QUERY_RANGE_ABORT)
> > +		error = 0;
> > +
> > +	xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
> 
> So I assume it's intentional that we don't care about _QUERY_RANGE_ABORT
> here? Otherwise looks fine.

Yes.  The only difference between XFS_BTREE_ERROR and XFS_BTREE_NOERROR
is that _ERROR forces _del_cursor to scan every bc_bufs entry for
buffers to release, whereas _NOERROR stops after the first NULL it finds
while going from level 0 to level n.  I've had half a mind to remove
that parameter since it clutters up the callsites and most of the time
all the btree pointers fit in a single cacheline.

That said, the btree query_range/query_all functions only return
_QUERY_RANGE_ABORT if the helper function (i.e. xfs_scrub_bmap_check_rmap)
returns _ABORT.  The helper only gets called if we find a record in the
btree which means that we can use _NOERROR since bc_bufs[0] is set.

--D

> Brian
> 
> > +out_agf:
> > +	xfs_trans_brelse(sc->tp, agf);
> > +	return error;
> > +}
> > +
> > +/* Make sure each rmap has a corresponding bmbt entry. */
> > +STATIC int
> > +xfs_scrub_bmap_check_rmaps(
> > +	struct xfs_scrub_context	*sc,
> > +	int				whichfork)
> > +{
> > +	loff_t				size;
> > +	xfs_agnumber_t			agno;
> > +	int				error;
> > +
> > +	if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) ||
> > +	    whichfork == XFS_COW_FORK ||
> > +	    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
> > +		return 0;
> > +
> > +	/* Don't support realtime rmap checks yet. */
> > +	if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK)
> > +		return 0;
> > +
> > +	/*
> > +	 * Only do this for complex maps that are in btree format, or for
> > +	 * situations where we would seem to have a size but zero extents.
> > +	 * The inode repair code can zap broken iforks, which means we have
> > +	 * to flag this bmap as corrupt if there are rmaps that need to be
> > +	 * reattached.
> > +	 */
> > +	switch (whichfork) {
> > +	case XFS_DATA_FORK:
> > +		size = i_size_read(VFS_I(sc->ip));
> > +		break;
> > +	case XFS_ATTR_FORK:
> > +		size = XFS_IFORK_Q(sc->ip);
> > +		break;
> > +	default:
> > +		size = 0;
> > +		break;
> > +	}
> > +	if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE &&
> > +	    (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0))
> > +		return 0;
> > +
> > +	for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) {
> > +		error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno);
> > +		if (error)
> > +			return error;
> > +		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
> > +			break;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  /*
> >   * Scrub an inode fork's block mappings.
> >   *
> > @@ -463,7 +627,7 @@ xfs_scrub_bmap(
> >  		break;
> >  	case XFS_ATTR_FORK:
> >  		if (!ifp)
> > -			goto out;
> > +			goto out_check_rmap;
> >  		if (!xfs_sb_version_hasattr(&mp->m_sb) &&
> >  		    !xfs_sb_version_hasattr2(&mp->m_sb))
> >  			xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
> > @@ -534,6 +698,10 @@ xfs_scrub_bmap(
> >  			goto out;
> >  	}
> >  
> > +out_check_rmap:
> > +	error = xfs_scrub_bmap_check_rmaps(sc, whichfork);
> > +	if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error))
> > +		goto out;
> >  out:
> >  	return error;
> >  }
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode
  2018-03-22 14:34   ` Brian Foster
@ 2018-03-22 17:49     ` Darrick J. Wong
  0 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22 17:49 UTC (permalink / raw)
  To: Brian Foster; +Cc: linux-xfs

On Thu, Mar 22, 2018 at 10:34:35AM -0400, Brian Foster wrote:
> On Tue, Mar 20, 2018 at 08:21:58PM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > If a directory's parent inode pointer doesn't point to an inode, flag
> > the directory as corrupt.  Enable IGET_UNTRUSTED here so that we
> > can confirm with the inobt that the inode is present and allocated.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/scrub/parent.c |    9 +++++++--
> >  1 file changed, 7 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
> > index 0d38514..d1547ca 100644
> > --- a/fs/xfs/scrub/parent.c
> > +++ b/fs/xfs/scrub/parent.c
> > @@ -167,9 +167,14 @@ xfs_scrub_parent_validate(
> >  	 * if the parent pointer erroneously points to a file, we
> >  	 * can't use DONTCACHE here because DONTCACHE inodes can trigger
> >  	 * immediate inactive cleanup of the inode.
> > +	 *
> > +	 * If _iget returns -EINVAL then the parent inode number is garbage
> > +	 * and the directory is corrupt.
> >  	 */
> > -	error = xfs_iget(mp, sc->tp, dnum, 0, 0, &dp);
> > -	if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
> > +	error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp);
> > +	if (error == -EINVAL)
> > +		error = -EFSCORRUPTED;
> > +	if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
> 
> Ok, but it looks like we're also changing the error semantics here
> (xref_process_error() -> process_error()). Could you comment on that in
> the commit log as well?

Ok, though it occurs to me that iget returning EFSCORRUPTED is still an
xref error, not a corruption error, so this really ought to be:

error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp);
if (error == -EINVAL) {
	error = -EFSCORRUPTED;
	xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
	goto out;
}
if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
	goto out;

So I'll fix the code + commit message and send out a new patch.

Thanks for reviewing!

--D

> With that, looks fine:
> 
> Reviewed-by: Brian Foster <bfoster@redhat.com>
> 
> >  		goto out;
> >  	if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) {
> >  		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v3 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-15  0:29 ` [PATCH 1/9] xfs: sanity-check the unused space before trying to use it Darrick J. Wong
  2018-03-21 13:52   ` Brian Foster
  2018-03-22  5:59   ` [PATCH v2 " Darrick J. Wong
@ 2018-03-22 17:53   ` Darrick J. Wong
  2018-03-22 22:21   ` [PATCH v4 " Darrick J. Wong
  3 siblings, 0 replies; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22 17:53 UTC (permalink / raw)
  To: linux-xfs; +Cc: Brian Foster

From: Darrick J. Wong <darrick.wong@oracle.com>

In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
it doesn't make sense.  Since a carefully crafted fuzzed image can cause
the kernel to crash after blowing a bunch of assertions, let's move
those checks into a validator function and rig everything up to return
EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
xfs/391.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v3: fix all the formatting weirdness
v2: release buffers defensively, fix some formatting weirdness
---
 fs/xfs/libxfs/xfs_dir2.h       |    2 +
 fs/xfs/libxfs/xfs_dir2_block.c |   59 ++++++++++++++++++------------
 fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
 fs/xfs/libxfs/xfs_dir2_leaf.c  |   10 ++++-
 fs/xfs/libxfs/xfs_dir2_node.c  |   11 ++++--
 5 files changed, 111 insertions(+), 49 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index 388d67c..989e95a 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
 extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
 		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
 		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
-extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
+extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
 		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
 		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
 		int *needlogp, int *needscanp);
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 2da86a3..875893d 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -451,15 +451,19 @@ xfs_dir2_block_addname(
 	 * No stale entries, will use enddup space to hold new leaf.
 	 */
 	if (!btp->stale) {
+		xfs_dir2_data_aoff_t	aoff;
+
 		/*
 		 * Mark the space needed for the new leaf entry, now in use.
 		 */
-		xfs_dir2_data_use_free(args, bp, enddup,
-			(xfs_dir2_data_aoff_t)
-			((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) -
-			 sizeof(*blp)),
-			(xfs_dir2_data_aoff_t)sizeof(*blp),
-			&needlog, &needscan);
+		aoff = (xfs_dir2_data_aoff_t)((char *)enddup - (char *)hdr +
+				be16_to_cpu(enddup->length) - sizeof(*blp));
+		error = xfs_dir2_data_use_free(args, bp, enddup, aoff,
+				(xfs_dir2_data_aoff_t)sizeof(*blp), &needlog,
+				&needscan);
+		if (error)
+			return error;
+
 		/*
 		 * Update the tail (entry count).
 		 */
@@ -541,9 +545,11 @@ xfs_dir2_block_addname(
 	/*
 	 * Mark space for the data entry used.
 	 */
-	xfs_dir2_data_use_free(args, bp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
-		(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, bp, dup,
+			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
+			(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Create the new data entry.
 	 */
@@ -997,8 +1003,10 @@ xfs_dir2_leaf_to_block(
 	/*
 	 * Use up the space at the end of the block (blp/btp).
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup, args->geo->blksize - size, size,
-		&needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, dbp, dup,
+			args->geo->blksize - size, size, &needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Initialize the block tail.
 	 */
@@ -1110,18 +1118,14 @@ xfs_dir2_sf_to_block(
 	 * Add block 0 to the inode.
 	 */
 	error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
-	if (error) {
-		kmem_free(sfp);
-		return error;
-	}
+	if (error)
+		goto out_free;
 	/*
 	 * Initialize the data block, then convert it to block format.
 	 */
 	error = xfs_dir3_data_init(args, blkno, &bp);
-	if (error) {
-		kmem_free(sfp);
-		return error;
-	}
+	if (error)
+		goto out_free;
 	xfs_dir3_block_init(mp, tp, bp, dp);
 	hdr = bp->b_addr;
 
@@ -1136,8 +1140,10 @@ xfs_dir2_sf_to_block(
 	 */
 	dup = dp->d_ops->data_unused_p(hdr);
 	needlog = needscan = 0;
-	xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
-			       i, &needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
+			i, &needlog, &needscan);
+	if (error)
+		goto out_free;
 	ASSERT(needscan == 0);
 	/*
 	 * Fill in the tail.
@@ -1150,9 +1156,11 @@ xfs_dir2_sf_to_block(
 	/*
 	 * Remove the freespace, we'll manage it.
 	 */
-	xfs_dir2_data_use_free(args, bp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
-		be16_to_cpu(dup->length), &needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, bp, dup,
+			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
+			be16_to_cpu(dup->length), &needlog, &needscan);
+	if (error)
+		goto out_free;
 	/*
 	 * Create entry for .
 	 */
@@ -1256,4 +1264,7 @@ xfs_dir2_sf_to_block(
 	xfs_dir2_block_log_tail(tp, bp);
 	xfs_dir3_data_check(dp, bp);
 	return 0;
+out_free:
+	kmem_free(sfp);
+	return error;
 }
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index 9202794..167503d 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -932,10 +932,51 @@ xfs_dir2_data_make_free(
 	*needscanp = needscan;
 }
 
+/* Check our free data for obvious signs of corruption. */
+static inline xfs_failaddr_t
+xfs_dir2_data_check_free(
+	struct xfs_dir2_data_hdr	*hdr,
+	struct xfs_dir2_data_unused	*dup,
+	xfs_dir2_data_aoff_t		offset,
+	xfs_dir2_data_aoff_t		len)
+{
+	if (hdr->magic != cpu_to_be32(XFS_DIR2_DATA_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
+		return __this_address;
+	if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG)
+		return __this_address;
+	if (offset < (char *)dup - (char *)hdr)
+		return __this_address;
+	if (offset + len > (char *)dup + be16_to_cpu(dup->length) - (char *)hdr)
+		return __this_address;
+	if ((char *)dup - (char *)hdr !=
+	    be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
+		return __this_address;
+	return NULL;
+}
+
+/* Sanity-check a new bestfree entry. */
+static inline xfs_failaddr_t
+xfs_dir2_data_check_new_free(
+	struct xfs_dir2_data_hdr	*hdr,
+	struct xfs_dir2_data_free	*dfp,
+	struct xfs_dir2_data_unused	*newdup)
+{
+	if (dfp == NULL)
+		return __this_address;
+	if (dfp->length != newdup->length)
+		return __this_address;
+	if (be16_to_cpu(dfp->offset) != (char *)newdup - (char *)hdr)
+		return __this_address;
+	return NULL;
+}
+
 /*
  * Take a byte range out of an existing unused space and make it un-free.
  */
-void
+int
 xfs_dir2_data_use_free(
 	struct xfs_da_args	*args,
 	struct xfs_buf		*bp,
@@ -947,23 +988,19 @@ xfs_dir2_data_use_free(
 {
 	xfs_dir2_data_hdr_t	*hdr;		/* data block header */
 	xfs_dir2_data_free_t	*dfp;		/* bestfree pointer */
+	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
+	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
+	struct xfs_dir2_data_free *bf;
+	xfs_failaddr_t		fa;
 	int			matchback;	/* matches end of freespace */
 	int			matchfront;	/* matches start of freespace */
 	int			needscan;	/* need to regen bestfree */
-	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
-	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
 	int			oldlen;		/* old unused entry's length */
-	struct xfs_dir2_data_free *bf;
 
 	hdr = bp->b_addr;
-	ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
-	ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
-	ASSERT(offset >= (char *)dup - (char *)hdr);
-	ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)hdr);
-	ASSERT((char *)dup - (char *)hdr == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
+	fa = xfs_dir2_data_check_free(hdr, dup, offset, len);
+	if (fa)
+		goto corrupt;
 	/*
 	 * Look up the entry in the bestfree table.
 	 */
@@ -1008,9 +1045,9 @@ xfs_dir2_data_use_free(
 			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
 			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
 						       needlogp);
-			ASSERT(dfp != NULL);
-			ASSERT(dfp->length == newdup->length);
-			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
+			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
+			if (fa)
+				goto corrupt;
 			/*
 			 * If we got inserted at the last slot,
 			 * that means we don't know if there was a better
@@ -1036,9 +1073,9 @@ xfs_dir2_data_use_free(
 			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
 			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
 						       needlogp);
-			ASSERT(dfp != NULL);
-			ASSERT(dfp->length == newdup->length);
-			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
+			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
+			if (fa)
+				goto corrupt;
 			/*
 			 * If we got inserted at the last slot,
 			 * that means we don't know if there was a better
@@ -1084,6 +1121,11 @@ xfs_dir2_data_use_free(
 		}
 	}
 	*needscanp = needscan;
+	return 0;
+corrupt:
+	xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
+			hdr, __FILE__, __LINE__, fa);
+	return -EFSCORRUPTED;
 }
 
 /* Find the end of the entry data in a data/block format dir block. */
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index d61d52d..50fc9c0 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -877,9 +877,13 @@ xfs_dir2_leaf_addname(
 	/*
 	 * Mark the initial part of our freespace in use for the new entry.
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
-		&needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, dbp, dup,
+			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
+			length, &needlog, &needscan);
+	if (error) {
+		xfs_trans_brelse(tp, lbp);
+		return error;
+	}
 	/*
 	 * Initialize our new entry (at last).
 	 */
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 0839ffe..9df096c 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -1729,6 +1729,7 @@ xfs_dir2_node_addname_int(
 	__be16			*bests;
 	struct xfs_dir3_icfree_hdr freehdr;
 	struct xfs_dir2_data_free *bf;
+	xfs_dir2_data_aoff_t	aoff;
 
 	dp = args->dp;
 	mp = dp->i_mount;
@@ -2023,9 +2024,13 @@ xfs_dir2_node_addname_int(
 	/*
 	 * Mark the first part of the unused space, inuse for us.
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
-		&needlog, &needscan);
+	aoff = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
+	error = xfs_dir2_data_use_free(args, dbp, dup, aoff, length,
+			&needlog, &needscan);
+	if (error) {
+		xfs_trans_brelse(tp, dbp);
+		return error;
+	}
 	/*
 	 * Fill in the new entry and log it.
 	 */

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* [PATCH v2 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode
  2018-03-21  3:21 ` [PATCH 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode Darrick J. Wong
  2018-03-22 14:34   ` Brian Foster
@ 2018-03-22 17:57   ` Darrick J. Wong
  2018-03-23 12:29     ` Brian Foster
  1 sibling, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22 17:57 UTC (permalink / raw)
  To: linux-xfs; +Cc: Brian Foster

From: Darrick J. Wong <darrick.wong@oracle.com>

If a directory's parent inode pointer doesn't point to an inode, the
directory should be flagged as corrupt.  Enable IGET_UNTRUSTED here so
that _iget will return -EINVAL if the inobt does not confirm that the
inode is present and allocated and we can flag the directory corruption.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: we only want to flag EINVAL as a directory corruption, everything
else is still a cross-referencing error
---
 fs/xfs/scrub/parent.c |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index 0d38514..1fb88c1 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -167,8 +167,18 @@ xfs_scrub_parent_validate(
 	 * if the parent pointer erroneously points to a file, we
 	 * can't use DONTCACHE here because DONTCACHE inodes can trigger
 	 * immediate inactive cleanup of the inode.
+	 *
+	 * If _iget returns -EINVAL then the parent inode number is garbage
+	 * and the directory is corrupt.  If the _iget returns -EFSCORRUPTED
+	 * or -EFSBADCRC then the parent is corrupt which is a cross
+	 * referencing error.  Any other error is an operational error.
 	 */
-	error = xfs_iget(mp, sc->tp, dnum, 0, 0, &dp);
+	error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp);
+	if (error == -EINVAL) {
+		error = -EFSCORRUPTED;
+		xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
+		goto out;
+	}
 	if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
 		goto out;
 	if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) {

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-22  5:59   ` [PATCH v2 " Darrick J. Wong
  2018-03-22 14:33     ` Brian Foster
@ 2018-03-22 22:04     ` Dave Chinner
  1 sibling, 0 replies; 53+ messages in thread
From: Dave Chinner @ 2018-03-22 22:04 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs, Brian Foster

On Wed, Mar 21, 2018 at 10:59:12PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
> it doesn't make sense.  Since a carefully crafted fuzzed image can cause
> the kernel to crash after blowing a bunch of assertions, let's move
> those checks into a validator function and rig everything up to return
> EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
> xfs/391.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v2: release buffers defensively, fix some formatting weirdness
....
> +	if ((char *)dup - (char *)hdr !=
> +	    be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
> +		return __this_address;

Also this (new logic statement vs multiline continuation).

	if ((char *)dup - (char *)hdr !=
			be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
		return __this_address;

CHeers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 53+ messages in thread

* [PATCH v4 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-15  0:29 ` [PATCH 1/9] xfs: sanity-check the unused space before trying to use it Darrick J. Wong
                     ` (2 preceding siblings ...)
  2018-03-22 17:53   ` [PATCH v3 " Darrick J. Wong
@ 2018-03-22 22:21   ` Darrick J. Wong
  2018-03-23 12:29     ` Brian Foster
  3 siblings, 1 reply; 53+ messages in thread
From: Darrick J. Wong @ 2018-03-22 22:21 UTC (permalink / raw)
  To: linux-xfs; +Cc: Brian Foster, Dave Chinner

From: Darrick J. Wong <darrick.wong@oracle.com>

In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
it doesn't make sense.  Since a carefully crafted fuzzed image can cause
the kernel to crash after blowing a bunch of assertions, let's move
those checks into a validator function and rig everything up to return
EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
xfs/391.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v4: fix harder the formatting weirdness
v3: fix all the formatting weirdness
v2: release buffers defensively, fix some formatting weirdness
---
 fs/xfs/libxfs/xfs_dir2.h       |    2 +
 fs/xfs/libxfs/xfs_dir2_block.c |   59 ++++++++++++++++++------------
 fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
 fs/xfs/libxfs/xfs_dir2_leaf.c  |   10 ++++-
 fs/xfs/libxfs/xfs_dir2_node.c  |   11 ++++--
 5 files changed, 111 insertions(+), 49 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index 388d67c..989e95a 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
 extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
 		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
 		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
-extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
+extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
 		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
 		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
 		int *needlogp, int *needscanp);
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 2da86a3..875893d 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -451,15 +451,19 @@ xfs_dir2_block_addname(
 	 * No stale entries, will use enddup space to hold new leaf.
 	 */
 	if (!btp->stale) {
+		xfs_dir2_data_aoff_t	aoff;
+
 		/*
 		 * Mark the space needed for the new leaf entry, now in use.
 		 */
-		xfs_dir2_data_use_free(args, bp, enddup,
-			(xfs_dir2_data_aoff_t)
-			((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) -
-			 sizeof(*blp)),
-			(xfs_dir2_data_aoff_t)sizeof(*blp),
-			&needlog, &needscan);
+		aoff = (xfs_dir2_data_aoff_t)((char *)enddup - (char *)hdr +
+				be16_to_cpu(enddup->length) - sizeof(*blp));
+		error = xfs_dir2_data_use_free(args, bp, enddup, aoff,
+				(xfs_dir2_data_aoff_t)sizeof(*blp), &needlog,
+				&needscan);
+		if (error)
+			return error;
+
 		/*
 		 * Update the tail (entry count).
 		 */
@@ -541,9 +545,11 @@ xfs_dir2_block_addname(
 	/*
 	 * Mark space for the data entry used.
 	 */
-	xfs_dir2_data_use_free(args, bp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
-		(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, bp, dup,
+			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
+			(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Create the new data entry.
 	 */
@@ -997,8 +1003,10 @@ xfs_dir2_leaf_to_block(
 	/*
 	 * Use up the space at the end of the block (blp/btp).
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup, args->geo->blksize - size, size,
-		&needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, dbp, dup,
+			args->geo->blksize - size, size, &needlog, &needscan);
+	if (error)
+		return error;
 	/*
 	 * Initialize the block tail.
 	 */
@@ -1110,18 +1118,14 @@ xfs_dir2_sf_to_block(
 	 * Add block 0 to the inode.
 	 */
 	error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
-	if (error) {
-		kmem_free(sfp);
-		return error;
-	}
+	if (error)
+		goto out_free;
 	/*
 	 * Initialize the data block, then convert it to block format.
 	 */
 	error = xfs_dir3_data_init(args, blkno, &bp);
-	if (error) {
-		kmem_free(sfp);
-		return error;
-	}
+	if (error)
+		goto out_free;
 	xfs_dir3_block_init(mp, tp, bp, dp);
 	hdr = bp->b_addr;
 
@@ -1136,8 +1140,10 @@ xfs_dir2_sf_to_block(
 	 */
 	dup = dp->d_ops->data_unused_p(hdr);
 	needlog = needscan = 0;
-	xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
-			       i, &needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
+			i, &needlog, &needscan);
+	if (error)
+		goto out_free;
 	ASSERT(needscan == 0);
 	/*
 	 * Fill in the tail.
@@ -1150,9 +1156,11 @@ xfs_dir2_sf_to_block(
 	/*
 	 * Remove the freespace, we'll manage it.
 	 */
-	xfs_dir2_data_use_free(args, bp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
-		be16_to_cpu(dup->length), &needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, bp, dup,
+			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
+			be16_to_cpu(dup->length), &needlog, &needscan);
+	if (error)
+		goto out_free;
 	/*
 	 * Create entry for .
 	 */
@@ -1256,4 +1264,7 @@ xfs_dir2_sf_to_block(
 	xfs_dir2_block_log_tail(tp, bp);
 	xfs_dir3_data_check(dp, bp);
 	return 0;
+out_free:
+	kmem_free(sfp);
+	return error;
 }
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index 9202794..cb67ec7 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -932,10 +932,51 @@ xfs_dir2_data_make_free(
 	*needscanp = needscan;
 }
 
+/* Check our free data for obvious signs of corruption. */
+static inline xfs_failaddr_t
+xfs_dir2_data_check_free(
+	struct xfs_dir2_data_hdr	*hdr,
+	struct xfs_dir2_data_unused	*dup,
+	xfs_dir2_data_aoff_t		offset,
+	xfs_dir2_data_aoff_t		len)
+{
+	if (hdr->magic != cpu_to_be32(XFS_DIR2_DATA_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) &&
+	    hdr->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
+		return __this_address;
+	if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG)
+		return __this_address;
+	if (offset < (char *)dup - (char *)hdr)
+		return __this_address;
+	if (offset + len > (char *)dup + be16_to_cpu(dup->length) - (char *)hdr)
+		return __this_address;
+	if ((char *)dup - (char *)hdr !=
+			be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
+		return __this_address;
+	return NULL;
+}
+
+/* Sanity-check a new bestfree entry. */
+static inline xfs_failaddr_t
+xfs_dir2_data_check_new_free(
+	struct xfs_dir2_data_hdr	*hdr,
+	struct xfs_dir2_data_free	*dfp,
+	struct xfs_dir2_data_unused	*newdup)
+{
+	if (dfp == NULL)
+		return __this_address;
+	if (dfp->length != newdup->length)
+		return __this_address;
+	if (be16_to_cpu(dfp->offset) != (char *)newdup - (char *)hdr)
+		return __this_address;
+	return NULL;
+}
+
 /*
  * Take a byte range out of an existing unused space and make it un-free.
  */
-void
+int
 xfs_dir2_data_use_free(
 	struct xfs_da_args	*args,
 	struct xfs_buf		*bp,
@@ -947,23 +988,19 @@ xfs_dir2_data_use_free(
 {
 	xfs_dir2_data_hdr_t	*hdr;		/* data block header */
 	xfs_dir2_data_free_t	*dfp;		/* bestfree pointer */
+	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
+	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
+	struct xfs_dir2_data_free *bf;
+	xfs_failaddr_t		fa;
 	int			matchback;	/* matches end of freespace */
 	int			matchfront;	/* matches start of freespace */
 	int			needscan;	/* need to regen bestfree */
-	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
-	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
 	int			oldlen;		/* old unused entry's length */
-	struct xfs_dir2_data_free *bf;
 
 	hdr = bp->b_addr;
-	ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
-	       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
-	ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
-	ASSERT(offset >= (char *)dup - (char *)hdr);
-	ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)hdr);
-	ASSERT((char *)dup - (char *)hdr == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
+	fa = xfs_dir2_data_check_free(hdr, dup, offset, len);
+	if (fa)
+		goto corrupt;
 	/*
 	 * Look up the entry in the bestfree table.
 	 */
@@ -1008,9 +1045,9 @@ xfs_dir2_data_use_free(
 			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
 			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
 						       needlogp);
-			ASSERT(dfp != NULL);
-			ASSERT(dfp->length == newdup->length);
-			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
+			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
+			if (fa)
+				goto corrupt;
 			/*
 			 * If we got inserted at the last slot,
 			 * that means we don't know if there was a better
@@ -1036,9 +1073,9 @@ xfs_dir2_data_use_free(
 			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
 			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
 						       needlogp);
-			ASSERT(dfp != NULL);
-			ASSERT(dfp->length == newdup->length);
-			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
+			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
+			if (fa)
+				goto corrupt;
 			/*
 			 * If we got inserted at the last slot,
 			 * that means we don't know if there was a better
@@ -1084,6 +1121,11 @@ xfs_dir2_data_use_free(
 		}
 	}
 	*needscanp = needscan;
+	return 0;
+corrupt:
+	xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
+			hdr, __FILE__, __LINE__, fa);
+	return -EFSCORRUPTED;
 }
 
 /* Find the end of the entry data in a data/block format dir block. */
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index d61d52d..50fc9c0 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -877,9 +877,13 @@ xfs_dir2_leaf_addname(
 	/*
 	 * Mark the initial part of our freespace in use for the new entry.
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
-		&needlog, &needscan);
+	error = xfs_dir2_data_use_free(args, dbp, dup,
+			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
+			length, &needlog, &needscan);
+	if (error) {
+		xfs_trans_brelse(tp, lbp);
+		return error;
+	}
 	/*
 	 * Initialize our new entry (at last).
 	 */
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 0839ffe..9df096c 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -1729,6 +1729,7 @@ xfs_dir2_node_addname_int(
 	__be16			*bests;
 	struct xfs_dir3_icfree_hdr freehdr;
 	struct xfs_dir2_data_free *bf;
+	xfs_dir2_data_aoff_t	aoff;
 
 	dp = args->dp;
 	mp = dp->i_mount;
@@ -2023,9 +2024,13 @@ xfs_dir2_node_addname_int(
 	/*
 	 * Mark the first part of the unused space, inuse for us.
 	 */
-	xfs_dir2_data_use_free(args, dbp, dup,
-		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
-		&needlog, &needscan);
+	aoff = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
+	error = xfs_dir2_data_use_free(args, dbp, dup, aoff, length,
+			&needlog, &needscan);
+	if (error) {
+		xfs_trans_brelse(tp, dbp);
+		return error;
+	}
 	/*
 	 * Fill in the new entry and log it.
 	 */

^ permalink raw reply related	[flat|nested] 53+ messages in thread

* Re: [PATCH v4 1/9] xfs: sanity-check the unused space before trying to use it
  2018-03-22 22:21   ` [PATCH v4 " Darrick J. Wong
@ 2018-03-23 12:29     ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-23 12:29 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs, Dave Chinner

On Thu, Mar 22, 2018 at 03:21:43PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> In xfs_dir2_data_use_free, we examine on-disk metadata and ASSERT if
> it doesn't make sense.  Since a carefully crafted fuzzed image can cause
> the kernel to crash after blowing a bunch of assertions, let's move
> those checks into a validator function and rig everything up to return
> EFSCORRUPTED to userspace.  Found by lastbit fuzzing ltail.bestcount via
> xfs/391.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v4: fix harder the formatting weirdness
> v3: fix all the formatting weirdness
> v2: release buffers defensively, fix some formatting weirdness
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_dir2.h       |    2 +
>  fs/xfs/libxfs/xfs_dir2_block.c |   59 ++++++++++++++++++------------
>  fs/xfs/libxfs/xfs_dir2_data.c  |   78 +++++++++++++++++++++++++++++++---------
>  fs/xfs/libxfs/xfs_dir2_leaf.c  |   10 ++++-
>  fs/xfs/libxfs/xfs_dir2_node.c  |   11 ++++--
>  5 files changed, 111 insertions(+), 49 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
> index 388d67c..989e95a 100644
> --- a/fs/xfs/libxfs/xfs_dir2.h
> +++ b/fs/xfs/libxfs/xfs_dir2.h
> @@ -173,7 +173,7 @@ extern void xfs_dir2_data_log_unused(struct xfs_da_args *args,
>  extern void xfs_dir2_data_make_free(struct xfs_da_args *args,
>  		struct xfs_buf *bp, xfs_dir2_data_aoff_t offset,
>  		xfs_dir2_data_aoff_t len, int *needlogp, int *needscanp);
> -extern void xfs_dir2_data_use_free(struct xfs_da_args *args,
> +extern int xfs_dir2_data_use_free(struct xfs_da_args *args,
>  		struct xfs_buf *bp, struct xfs_dir2_data_unused *dup,
>  		xfs_dir2_data_aoff_t offset, xfs_dir2_data_aoff_t len,
>  		int *needlogp, int *needscanp);
> diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
> index 2da86a3..875893d 100644
> --- a/fs/xfs/libxfs/xfs_dir2_block.c
> +++ b/fs/xfs/libxfs/xfs_dir2_block.c
> @@ -451,15 +451,19 @@ xfs_dir2_block_addname(
>  	 * No stale entries, will use enddup space to hold new leaf.
>  	 */
>  	if (!btp->stale) {
> +		xfs_dir2_data_aoff_t	aoff;
> +
>  		/*
>  		 * Mark the space needed for the new leaf entry, now in use.
>  		 */
> -		xfs_dir2_data_use_free(args, bp, enddup,
> -			(xfs_dir2_data_aoff_t)
> -			((char *)enddup - (char *)hdr + be16_to_cpu(enddup->length) -
> -			 sizeof(*blp)),
> -			(xfs_dir2_data_aoff_t)sizeof(*blp),
> -			&needlog, &needscan);
> +		aoff = (xfs_dir2_data_aoff_t)((char *)enddup - (char *)hdr +
> +				be16_to_cpu(enddup->length) - sizeof(*blp));
> +		error = xfs_dir2_data_use_free(args, bp, enddup, aoff,
> +				(xfs_dir2_data_aoff_t)sizeof(*blp), &needlog,
> +				&needscan);
> +		if (error)
> +			return error;
> +
>  		/*
>  		 * Update the tail (entry count).
>  		 */
> @@ -541,9 +545,11 @@ xfs_dir2_block_addname(
>  	/*
>  	 * Mark space for the data entry used.
>  	 */
> -	xfs_dir2_data_use_free(args, bp, dup,
> -		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
> -		(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
> +	error = xfs_dir2_data_use_free(args, bp, dup,
> +			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
> +			(xfs_dir2_data_aoff_t)len, &needlog, &needscan);
> +	if (error)
> +		return error;
>  	/*
>  	 * Create the new data entry.
>  	 */
> @@ -997,8 +1003,10 @@ xfs_dir2_leaf_to_block(
>  	/*
>  	 * Use up the space at the end of the block (blp/btp).
>  	 */
> -	xfs_dir2_data_use_free(args, dbp, dup, args->geo->blksize - size, size,
> -		&needlog, &needscan);
> +	error = xfs_dir2_data_use_free(args, dbp, dup,
> +			args->geo->blksize - size, size, &needlog, &needscan);
> +	if (error)
> +		return error;
>  	/*
>  	 * Initialize the block tail.
>  	 */
> @@ -1110,18 +1118,14 @@ xfs_dir2_sf_to_block(
>  	 * Add block 0 to the inode.
>  	 */
>  	error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
> -	if (error) {
> -		kmem_free(sfp);
> -		return error;
> -	}
> +	if (error)
> +		goto out_free;
>  	/*
>  	 * Initialize the data block, then convert it to block format.
>  	 */
>  	error = xfs_dir3_data_init(args, blkno, &bp);
> -	if (error) {
> -		kmem_free(sfp);
> -		return error;
> -	}
> +	if (error)
> +		goto out_free;
>  	xfs_dir3_block_init(mp, tp, bp, dp);
>  	hdr = bp->b_addr;
>  
> @@ -1136,8 +1140,10 @@ xfs_dir2_sf_to_block(
>  	 */
>  	dup = dp->d_ops->data_unused_p(hdr);
>  	needlog = needscan = 0;
> -	xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
> -			       i, &needlog, &needscan);
> +	error = xfs_dir2_data_use_free(args, bp, dup, args->geo->blksize - i,
> +			i, &needlog, &needscan);
> +	if (error)
> +		goto out_free;
>  	ASSERT(needscan == 0);
>  	/*
>  	 * Fill in the tail.
> @@ -1150,9 +1156,11 @@ xfs_dir2_sf_to_block(
>  	/*
>  	 * Remove the freespace, we'll manage it.
>  	 */
> -	xfs_dir2_data_use_free(args, bp, dup,
> -		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
> -		be16_to_cpu(dup->length), &needlog, &needscan);
> +	error = xfs_dir2_data_use_free(args, bp, dup,
> +			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
> +			be16_to_cpu(dup->length), &needlog, &needscan);
> +	if (error)
> +		goto out_free;
>  	/*
>  	 * Create entry for .
>  	 */
> @@ -1256,4 +1264,7 @@ xfs_dir2_sf_to_block(
>  	xfs_dir2_block_log_tail(tp, bp);
>  	xfs_dir3_data_check(dp, bp);
>  	return 0;
> +out_free:
> +	kmem_free(sfp);
> +	return error;
>  }
> diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
> index 9202794..cb67ec7 100644
> --- a/fs/xfs/libxfs/xfs_dir2_data.c
> +++ b/fs/xfs/libxfs/xfs_dir2_data.c
> @@ -932,10 +932,51 @@ xfs_dir2_data_make_free(
>  	*needscanp = needscan;
>  }
>  
> +/* Check our free data for obvious signs of corruption. */
> +static inline xfs_failaddr_t
> +xfs_dir2_data_check_free(
> +	struct xfs_dir2_data_hdr	*hdr,
> +	struct xfs_dir2_data_unused	*dup,
> +	xfs_dir2_data_aoff_t		offset,
> +	xfs_dir2_data_aoff_t		len)
> +{
> +	if (hdr->magic != cpu_to_be32(XFS_DIR2_DATA_MAGIC) &&
> +	    hdr->magic != cpu_to_be32(XFS_DIR3_DATA_MAGIC) &&
> +	    hdr->magic != cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) &&
> +	    hdr->magic != cpu_to_be32(XFS_DIR3_BLOCK_MAGIC))
> +		return __this_address;
> +	if (be16_to_cpu(dup->freetag) != XFS_DIR2_DATA_FREE_TAG)
> +		return __this_address;
> +	if (offset < (char *)dup - (char *)hdr)
> +		return __this_address;
> +	if (offset + len > (char *)dup + be16_to_cpu(dup->length) - (char *)hdr)
> +		return __this_address;
> +	if ((char *)dup - (char *)hdr !=
> +			be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)))
> +		return __this_address;
> +	return NULL;
> +}
> +
> +/* Sanity-check a new bestfree entry. */
> +static inline xfs_failaddr_t
> +xfs_dir2_data_check_new_free(
> +	struct xfs_dir2_data_hdr	*hdr,
> +	struct xfs_dir2_data_free	*dfp,
> +	struct xfs_dir2_data_unused	*newdup)
> +{
> +	if (dfp == NULL)
> +		return __this_address;
> +	if (dfp->length != newdup->length)
> +		return __this_address;
> +	if (be16_to_cpu(dfp->offset) != (char *)newdup - (char *)hdr)
> +		return __this_address;
> +	return NULL;
> +}
> +
>  /*
>   * Take a byte range out of an existing unused space and make it un-free.
>   */
> -void
> +int
>  xfs_dir2_data_use_free(
>  	struct xfs_da_args	*args,
>  	struct xfs_buf		*bp,
> @@ -947,23 +988,19 @@ xfs_dir2_data_use_free(
>  {
>  	xfs_dir2_data_hdr_t	*hdr;		/* data block header */
>  	xfs_dir2_data_free_t	*dfp;		/* bestfree pointer */
> +	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
> +	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
> +	struct xfs_dir2_data_free *bf;
> +	xfs_failaddr_t		fa;
>  	int			matchback;	/* matches end of freespace */
>  	int			matchfront;	/* matches start of freespace */
>  	int			needscan;	/* need to regen bestfree */
> -	xfs_dir2_data_unused_t	*newdup;	/* new unused entry */
> -	xfs_dir2_data_unused_t	*newdup2;	/* another new unused entry */
>  	int			oldlen;		/* old unused entry's length */
> -	struct xfs_dir2_data_free *bf;
>  
>  	hdr = bp->b_addr;
> -	ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
> -	       hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC) ||
> -	       hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
> -	       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
> -	ASSERT(be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG);
> -	ASSERT(offset >= (char *)dup - (char *)hdr);
> -	ASSERT(offset + len <= (char *)dup + be16_to_cpu(dup->length) - (char *)hdr);
> -	ASSERT((char *)dup - (char *)hdr == be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)));
> +	fa = xfs_dir2_data_check_free(hdr, dup, offset, len);
> +	if (fa)
> +		goto corrupt;
>  	/*
>  	 * Look up the entry in the bestfree table.
>  	 */
> @@ -1008,9 +1045,9 @@ xfs_dir2_data_use_free(
>  			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
>  			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
>  						       needlogp);
> -			ASSERT(dfp != NULL);
> -			ASSERT(dfp->length == newdup->length);
> -			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
> +			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
> +			if (fa)
> +				goto corrupt;
>  			/*
>  			 * If we got inserted at the last slot,
>  			 * that means we don't know if there was a better
> @@ -1036,9 +1073,9 @@ xfs_dir2_data_use_free(
>  			xfs_dir2_data_freeremove(hdr, bf, dfp, needlogp);
>  			dfp = xfs_dir2_data_freeinsert(hdr, bf, newdup,
>  						       needlogp);
> -			ASSERT(dfp != NULL);
> -			ASSERT(dfp->length == newdup->length);
> -			ASSERT(be16_to_cpu(dfp->offset) == (char *)newdup - (char *)hdr);
> +			fa = xfs_dir2_data_check_new_free(hdr, dfp, newdup);
> +			if (fa)
> +				goto corrupt;
>  			/*
>  			 * If we got inserted at the last slot,
>  			 * that means we don't know if there was a better
> @@ -1084,6 +1121,11 @@ xfs_dir2_data_use_free(
>  		}
>  	}
>  	*needscanp = needscan;
> +	return 0;
> +corrupt:
> +	xfs_corruption_error(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount,
> +			hdr, __FILE__, __LINE__, fa);
> +	return -EFSCORRUPTED;
>  }
>  
>  /* Find the end of the entry data in a data/block format dir block. */
> diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
> index d61d52d..50fc9c0 100644
> --- a/fs/xfs/libxfs/xfs_dir2_leaf.c
> +++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
> @@ -877,9 +877,13 @@ xfs_dir2_leaf_addname(
>  	/*
>  	 * Mark the initial part of our freespace in use for the new entry.
>  	 */
> -	xfs_dir2_data_use_free(args, dbp, dup,
> -		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
> -		&needlog, &needscan);
> +	error = xfs_dir2_data_use_free(args, dbp, dup,
> +			(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr),
> +			length, &needlog, &needscan);
> +	if (error) {
> +		xfs_trans_brelse(tp, lbp);
> +		return error;
> +	}
>  	/*
>  	 * Initialize our new entry (at last).
>  	 */
> diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
> index 0839ffe..9df096c 100644
> --- a/fs/xfs/libxfs/xfs_dir2_node.c
> +++ b/fs/xfs/libxfs/xfs_dir2_node.c
> @@ -1729,6 +1729,7 @@ xfs_dir2_node_addname_int(
>  	__be16			*bests;
>  	struct xfs_dir3_icfree_hdr freehdr;
>  	struct xfs_dir2_data_free *bf;
> +	xfs_dir2_data_aoff_t	aoff;
>  
>  	dp = args->dp;
>  	mp = dp->i_mount;
> @@ -2023,9 +2024,13 @@ xfs_dir2_node_addname_int(
>  	/*
>  	 * Mark the first part of the unused space, inuse for us.
>  	 */
> -	xfs_dir2_data_use_free(args, dbp, dup,
> -		(xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr), length,
> -		&needlog, &needscan);
> +	aoff = (xfs_dir2_data_aoff_t)((char *)dup - (char *)hdr);
> +	error = xfs_dir2_data_use_free(args, dbp, dup, aoff, length,
> +			&needlog, &needscan);
> +	if (error) {
> +		xfs_trans_brelse(tp, dbp);
> +		return error;
> +	}
>  	/*
>  	 * Fill in the new entry and log it.
>  	 */
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH v2 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode
  2018-03-22 17:57   ` [PATCH v2 " Darrick J. Wong
@ 2018-03-23 12:29     ` Brian Foster
  0 siblings, 0 replies; 53+ messages in thread
From: Brian Foster @ 2018-03-23 12:29 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On Thu, Mar 22, 2018 at 10:57:59AM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> If a directory's parent inode pointer doesn't point to an inode, the
> directory should be flagged as corrupt.  Enable IGET_UNTRUSTED here so
> that _iget will return -EINVAL if the inobt does not confirm that the
> inode is present and allocated and we can flag the directory corruption.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
> v2: we only want to flag EINVAL as a directory corruption, everything
> else is still a cross-referencing error
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/scrub/parent.c |   12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
> index 0d38514..1fb88c1 100644
> --- a/fs/xfs/scrub/parent.c
> +++ b/fs/xfs/scrub/parent.c
> @@ -167,8 +167,18 @@ xfs_scrub_parent_validate(
>  	 * if the parent pointer erroneously points to a file, we
>  	 * can't use DONTCACHE here because DONTCACHE inodes can trigger
>  	 * immediate inactive cleanup of the inode.
> +	 *
> +	 * If _iget returns -EINVAL then the parent inode number is garbage
> +	 * and the directory is corrupt.  If the _iget returns -EFSCORRUPTED
> +	 * or -EFSBADCRC then the parent is corrupt which is a cross
> +	 * referencing error.  Any other error is an operational error.
>  	 */
> -	error = xfs_iget(mp, sc->tp, dnum, 0, 0, &dp);
> +	error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp);
> +	if (error == -EINVAL) {
> +		error = -EFSCORRUPTED;
> +		xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error);
> +		goto out;
> +	}
>  	if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
>  		goto out;
>  	if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) {
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 53+ messages in thread

* Re: [PATCH 4/9] xfs: refactor inode buffer verifier error logging
  2018-03-15  0:29 ` [PATCH 4/9] xfs: refactor inode buffer " Darrick J. Wong
  2018-03-21 13:55   ` Brian Foster
@ 2018-04-24 19:51   ` Eric Sandeen
  1 sibling, 0 replies; 53+ messages in thread
From: Eric Sandeen @ 2018-04-24 19:51 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 3/14/18 6:29 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> When the inode buffer verifier encounters an error, it's much more
> helpful to print a buffer from the offending inode instead of just the
> start of the inode chunk buffer.

Thanks, I was about to do this until you told me you did it weeks ago.  ;)
That said, we should use this in the dquot verifiers as well.  But I'll
need to hold off on that until we decide where my old

[PATCH 0/6] xfs: quota fixes and enhancements

series is going, I think?

-Eric

^ permalink raw reply	[flat|nested] 53+ messages in thread

end of thread, other threads:[~2018-04-24 19:51 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-15  0:29 [PATCH v2 0/9] xfs-4.17: online scrub fixes Darrick J. Wong
2018-03-15  0:29 ` [PATCH 1/9] xfs: sanity-check the unused space before trying to use it Darrick J. Wong
2018-03-21 13:52   ` Brian Foster
2018-03-21 17:44     ` Darrick J. Wong
2018-03-22  5:59   ` [PATCH v2 " Darrick J. Wong
2018-03-22 14:33     ` Brian Foster
2018-03-22 17:23       ` Darrick J. Wong
2018-03-22 22:04     ` Dave Chinner
2018-03-22 17:53   ` [PATCH v3 " Darrick J. Wong
2018-03-22 22:21   ` [PATCH v4 " Darrick J. Wong
2018-03-23 12:29     ` Brian Foster
2018-03-15  0:29 ` [PATCH 2/9] xfs: refactor bmap record valiation Darrick J. Wong
2018-03-21 13:55   ` Brian Foster
2018-03-21 20:30     ` Darrick J. Wong
2018-03-22  6:01   ` [PATCH v2 " Darrick J. Wong
2018-03-22 14:33     ` Brian Foster
2018-03-15  0:29 ` [PATCH 3/9] xfs: refactor inode verifier error logging Darrick J. Wong
2018-03-21 13:55   ` Brian Foster
2018-03-15  0:29 ` [PATCH 4/9] xfs: refactor inode buffer " Darrick J. Wong
2018-03-21 13:55   ` Brian Foster
2018-03-21 18:03     ` Darrick J. Wong
2018-04-24 19:51   ` Eric Sandeen
2018-03-15  0:30 ` [PATCH 5/9] xfs: bmap scrubber should do rmap xref with bmap for sparse files Darrick J. Wong
2018-03-21 17:42   ` Brian Foster
2018-03-21 18:11     ` Darrick J. Wong
2018-03-22  6:02   ` [PATCH v2 " Darrick J. Wong
2018-03-22 14:33     ` Brian Foster
2018-03-22 17:35       ` Darrick J. Wong
2018-03-15  0:30 ` [PATCH 6/9] xfs: inode scrubber shouldn't bother with raw checks Darrick J. Wong
2018-03-21 17:42   ` Brian Foster
2018-03-21 20:37     ` Darrick J. Wong
2018-03-15  0:30 ` [PATCH 7/9] xfs: remove xfs_buf parameter from inode scrub methods Darrick J. Wong
2018-03-21 17:42   ` Brian Foster
2018-03-15  0:30 ` [PATCH 8/9] xfs: record inode buf errors as a xref error in inode scrubber Darrick J. Wong
2018-03-21 17:42   ` Brian Foster
2018-03-21 20:50     ` Darrick J. Wong
2018-03-22 14:34       ` Brian Foster
2018-03-22  6:24   ` [PATCH v2 " Darrick J. Wong
2018-03-22 14:34     ` Brian Foster
2018-03-15  0:30 ` [PATCH 9/9] xfs: move inode extent size hint validation to libxfs Darrick J. Wong
2018-03-21 17:42   ` Brian Foster
2018-03-21  3:21 ` [PATCH 10/9] xfs: don't accept inode buffers with suspicious unlinked chains Darrick J. Wong
2018-03-21 17:43   ` Brian Foster
2018-03-21 20:52     ` Darrick J. Wong
2018-03-22  6:08   ` [PATCH v2 " Darrick J. Wong
2018-03-22 14:34     ` Brian Foster
2018-03-21  3:21 ` [PATCH 11/9] xfs: flag inode corruption if parent ptr doesn't get us a real inode Darrick J. Wong
2018-03-22 14:34   ` Brian Foster
2018-03-22 17:49     ` Darrick J. Wong
2018-03-22 17:57   ` [PATCH v2 " Darrick J. Wong
2018-03-23 12:29     ` Brian Foster
2018-03-22  6:19 ` [PATCH 12/9] xfs: xfs_scrub_iallocbt_xref_rmap_inodes should use xref_set_corrupt Darrick J. Wong
2018-03-22 14:34   ` Brian Foster

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.