Commit 10d587ec authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: check AGI unlinked inode buckets



Look for corruptions in the AGI unlinked bucket chains.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 2651923d
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include "xfs_ialloc.h"
#include "xfs_rmap.h"
#include "xfs_ag.h"
#include "xfs_inode.h"
#include "scrub/scrub.h"
#include "scrub/common.h"

@@ -865,6 +866,43 @@ xchk_agi_xref(
	/* scrub teardown will take care of sc->sa for us */
}

/*
 * Check the unlinked buckets for links to bad inodes.  We hold the AGI, so
 * there cannot be any threads updating unlinked list pointers in this AG.
 */
STATIC void
xchk_iunlink(
	struct xfs_scrub	*sc,
	struct xfs_agi		*agi)
{
	unsigned int		i;
	struct xfs_inode	*ip;

	for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
		xfs_agino_t	agino = be32_to_cpu(agi->agi_unlinked[i]);

		while (agino != NULLAGINO) {
			if (agino % XFS_AGI_UNLINKED_BUCKETS != i) {
				xchk_block_set_corrupt(sc, sc->sa.agi_bp);
				return;
			}

			ip = xfs_iunlink_lookup(sc->sa.pag, agino);
			if (!ip) {
				xchk_block_set_corrupt(sc, sc->sa.agi_bp);
				return;
			}

			if (!xfs_inode_on_unlinked_list(ip)) {
				xchk_block_set_corrupt(sc, sc->sa.agi_bp);
				return;
			}

			agino = ip->i_next_unlinked;
		}
	}
}

/* Scrub the AGI. */
int
xchk_agi(
@@ -949,6 +987,8 @@ xchk_agi(
	if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount))
		xchk_block_set_corrupt(sc, sc->sa.agi_bp);

	xchk_iunlink(sc, agi);

	xchk_agi_xref(sc);
out:
	return error;
+1 −1
Original line number Diff line number Diff line
@@ -1985,7 +1985,7 @@ xfs_inactive(
 * only unlinked, referenced inodes can be on the unlinked inode list.  If we
 * don't find the inode in cache, then let the caller handle the situation.
 */
static struct xfs_inode *
struct xfs_inode *
xfs_iunlink_lookup(
	struct xfs_perag	*pag,
	xfs_agino_t		agino)
+1 −0
Original line number Diff line number Diff line
@@ -619,6 +619,7 @@ bool xfs_inode_needs_inactive(struct xfs_inode *ip);
int xfs_iunlink(struct xfs_trans *tp, struct xfs_inode *ip);
int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
		struct xfs_inode *ip);
struct xfs_inode *xfs_iunlink_lookup(struct xfs_perag *pag, xfs_agino_t agino);

void xfs_end_io(struct work_struct *work);