Commit 5a8e07e7 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: repair the inode core and forks of a metadata inode



Add a helper function to repair the core and forks of a metadata inode,
so that we can get move onto the task of repairing higher level metadata
that lives in an inode.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent 20cc0d39
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -86,6 +86,9 @@ struct xrep_bmap {

	/* What d the REFLINK flag be set when the repair is over? */
	enum reflink_scan_state	reflink_scan;

	/* Do we allow unwritten extents? */
	bool			allow_unwritten;
};

/* Is this space extent shared?  Flag the inode if it is. */
@@ -262,6 +265,10 @@ xrep_bmap_walk_rmap(
	    !(rec->rm_flags & XFS_RMAP_ATTR_FORK))
		return 0;

	/* Reject unwritten extents if we don't allow those. */
	if ((rec->rm_flags & XFS_RMAP_UNWRITTEN) && !rb->allow_unwritten)
		return -EFSCORRUPTED;

	fsbno = XFS_AGB_TO_FSB(mp, cur->bc_ag.pag->pag_agno,
			rec->rm_startblock);

@@ -780,10 +787,11 @@ xrep_bmap_init_reflink_scan(
}

/* Repair an inode fork. */
STATIC int
int
xrep_bmap(
	struct xfs_scrub	*sc,
	int			whichfork)
	int			whichfork,
	bool			allow_unwritten)
{
	struct xrep_bmap	*rb;
	char			*descr;
@@ -803,6 +811,7 @@ xrep_bmap(
	rb->sc = sc;
	rb->whichfork = whichfork;
	rb->reflink_scan = xrep_bmap_init_reflink_scan(sc, whichfork);
	rb->allow_unwritten = allow_unwritten;

	/* Set up enough storage to handle the max records for this fork. */
	large_extcount = xfs_has_large_extent_counts(sc->mp);
@@ -846,7 +855,7 @@ int
xrep_bmap_data(
	struct xfs_scrub	*sc)
{
	return xrep_bmap(sc, XFS_DATA_FORK);
	return xrep_bmap(sc, XFS_DATA_FORK, true);
}

/* Repair an inode's attr fork. */
@@ -854,5 +863,5 @@ int
xrep_bmap_attr(
	struct xfs_scrub	*sc)
{
	return xrep_bmap(sc, XFS_ATTR_FORK);
	return xrep_bmap(sc, XFS_ATTR_FORK, false);
}
+153 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "xfs_defer.h"
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "xfs_reflink.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
@@ -962,3 +963,155 @@ xrep_will_attempt(

	return false;
}

/* Try to fix some part of a metadata inode by calling another scrubber. */
STATIC int
xrep_metadata_inode_subtype(
	struct xfs_scrub	*sc,
	unsigned int		scrub_type)
{
	__u32			smtype = sc->sm->sm_type;
	__u32			smflags = sc->sm->sm_flags;
	unsigned int		sick_mask = sc->sick_mask;
	int			error;

	/*
	 * Let's see if the inode needs repair.  We're going to open-code calls
	 * to the scrub and repair functions so that we can hang on to the
	 * resources that we already acquired instead of using the standard
	 * setup/teardown routines.
	 */
	sc->sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;
	sc->sm->sm_type = scrub_type;

	switch (scrub_type) {
	case XFS_SCRUB_TYPE_INODE:
		error = xchk_inode(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTD:
		error = xchk_bmap_data(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTA:
		error = xchk_bmap_attr(sc);
		break;
	default:
		ASSERT(0);
		error = -EFSCORRUPTED;
	}
	if (error)
		goto out;

	if (!xrep_will_attempt(sc))
		goto out;

	/*
	 * Repair some part of the inode.  This will potentially join the inode
	 * to the transaction.
	 */
	switch (scrub_type) {
	case XFS_SCRUB_TYPE_INODE:
		error = xrep_inode(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTD:
		error = xrep_bmap(sc, XFS_DATA_FORK, false);
		break;
	case XFS_SCRUB_TYPE_BMBTA:
		error = xrep_bmap(sc, XFS_ATTR_FORK, false);
		break;
	}
	if (error)
		goto out;

	/*
	 * Finish all deferred intent items and then roll the transaction so
	 * that the inode will not be joined to the transaction when we exit
	 * the function.
	 */
	error = xfs_defer_finish(&sc->tp);
	if (error)
		goto out;
	error = xfs_trans_roll(&sc->tp);
	if (error)
		goto out;

	/*
	 * Clear the corruption flags and re-check the metadata that we just
	 * repaired.
	 */
	sc->sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT;

	switch (scrub_type) {
	case XFS_SCRUB_TYPE_INODE:
		error = xchk_inode(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTD:
		error = xchk_bmap_data(sc);
		break;
	case XFS_SCRUB_TYPE_BMBTA:
		error = xchk_bmap_attr(sc);
		break;
	}
	if (error)
		goto out;

	/* If corruption persists, the repair has failed. */
	if (xchk_needs_repair(sc->sm)) {
		error = -EFSCORRUPTED;
		goto out;
	}
out:
	sc->sick_mask = sick_mask;
	sc->sm->sm_type = smtype;
	sc->sm->sm_flags = smflags;
	return error;
}

/*
 * Repair the ondisk forks of a metadata inode.  The caller must ensure that
 * sc->ip points to the metadata inode and the ILOCK is held on that inode.
 * The inode must not be joined to the transaction before the call, and will
 * not be afterwards.
 */
int
xrep_metadata_inode_forks(
	struct xfs_scrub	*sc)
{
	bool			dirty = false;
	int			error;

	/* Repair the inode record and the data fork. */
	error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_INODE);
	if (error)
		return error;

	error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_BMBTD);
	if (error)
		return error;

	/* Make sure the attr fork looks ok before we delete it. */
	error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_BMBTA);
	if (error)
		return error;

	/* Clear the reflink flag since metadata never shares. */
	if (xfs_is_reflink_inode(sc->ip)) {
		dirty = true;
		xfs_trans_ijoin(sc->tp, sc->ip, 0);
		error = xfs_reflink_clear_inode_flag(sc->ip, &sc->tp);
		if (error)
			return error;
	}

	/*
	 * If we modified the inode, roll the transaction but don't rejoin the
	 * inode to the new transaction because xrep_bmap_data can do that.
	 */
	if (dirty) {
		error = xfs_trans_roll(&sc->tp);
		if (error)
			return error;
		dirty = false;
	}

	return 0;
}
+2 −0
Original line number Diff line number Diff line
@@ -82,6 +82,8 @@ int xrep_ino_dqattach(struct xfs_scrub *sc);
int xrep_ino_ensure_extent_count(struct xfs_scrub *sc, int whichfork,
		xfs_extnum_t nextents);
int xrep_reset_perag_resv(struct xfs_scrub *sc);
int xrep_bmap(struct xfs_scrub *sc, int whichfork, bool allow_unwritten);
int xrep_metadata_inode_forks(struct xfs_scrub *sc);

/* Repair setup functions */
int xrep_setup_ag_allocbt(struct xfs_scrub *sc);