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: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
20cc0d398e
commit
5a8e07e799
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user