diff options
author | Darrick J. Wong <djwong@kernel.org> | 2021-09-01 10:45:55 -0700 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2021-09-17 18:54:52 -0700 |
commit | 3c18a9d69da0ca621e0b56f55a1ddf7d350d6c65 (patch) | |
tree | 6033060c949e56def50cbe78663b033083aac492 | |
parent | c0d680ffb0b1f0f29f30bf986db407c7b310915b (diff) |
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>
-rw-r--r-- | fs/xfs/scrub/bmap_repair.c | 16 | ||||
-rw-r--r-- | fs/xfs/scrub/common.h | 8 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.c | 80 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.h | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/scrub.c | 5 |
5 files changed, 104 insertions, 7 deletions
diff --git a/fs/xfs/scrub/bmap_repair.c b/fs/xfs/scrub/bmap_repair.c index 2d4dd484e942..29342e6b79d8 100644 --- a/fs/xfs/scrub/bmap_repair.c +++ b/fs/xfs/scrub/bmap_repair.c @@ -68,6 +68,9 @@ struct xrep_bmap { /* Which fork are we fixing? */ int whichfork; + + /* Do we allow unwritten extents? */ + bool allow_unwritten; }; /* Remember this reverse-mapping as a series of bmap records. */ @@ -133,6 +136,9 @@ xrep_bmap_check_fork_rmap( rec->rm_blockcount)) return -EFSCORRUPTED; + if ((rec->rm_flags & XFS_RMAP_UNWRITTEN) && !rb->allow_unwritten) + return -EFSCORRUPTED; + /* Make sure this isn't free space. */ error = xfs_alloc_has_record(sc->sa.bno_cur, rec->rm_startblock, rec->rm_blockcount, &is_freesp); @@ -568,10 +574,11 @@ xrep_bmap_check_inputs( } /* Repair an inode fork. */ -STATIC int +int xrep_bmap( struct xfs_scrub *sc, - int whichfork) + int whichfork, + bool allow_unwritten) { struct xrep_bmap *rb; int error = 0; @@ -585,6 +592,7 @@ xrep_bmap( return -ENOMEM; rb->sc = sc; rb->whichfork = whichfork; + rb->allow_unwritten = allow_unwritten; /* Set up some storage */ rb->bmap_records = xfbma_init("bmap records", @@ -627,7 +635,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. */ @@ -635,5 +643,5 @@ int xrep_bmap_attr( struct xfs_scrub *sc) { - return xrep_bmap(sc, XFS_ATTR_FORK); + return xrep_bmap(sc, XFS_ATTR_FORK, false); } diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h index bc0e78c91f38..669ce29f2ecf 100644 --- a/fs/xfs/scrub/common.h +++ b/fs/xfs/scrub/common.h @@ -158,4 +158,12 @@ int xchk_ilock_inverted(struct xfs_inode *ip, uint lock_mode); void xchk_stop_reaping(struct xfs_scrub *sc); void xchk_start_reaping(struct xfs_scrub *sc); +/* Do we need to invoke the repair tool? */ +static inline bool xfs_scrub_needs_repair(struct xfs_scrub_metadata *sm) +{ + return sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT | + XFS_SCRUB_OFLAG_XCORRUPT | + XFS_SCRUB_OFLAG_PREEN); +} + #endif /* __XFS_SCRUB_COMMON_H__ */ diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index 091efeffd300..585b6cceaf3b 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -34,6 +34,7 @@ #include "xfs_attr_remote.h" #include "xfs_defer.h" #include "xfs_extfree_item.h" +#include "xfs_reflink.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" @@ -1605,3 +1606,82 @@ xrep_reset_perag_resv( out: 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) +{ + __u32 smtype; + __u32 smflags; + bool dirty = false; + int 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; + } + + /* + * Let's see if the forks need repair. We're going to open-code calls + * to the bmapbtd 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. + */ + smtype = sc->sm->sm_type; + smflags = sc->sm->sm_flags; + sc->sm->sm_type = XFS_SCRUB_TYPE_BMBTD; + sc->sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT; + + error = xchk_metadata_inode_forks(sc); + if (error || !xfs_scrub_needs_repair(sc->sm)) + goto out; + + /* + * Repair the data fork. This will potentially join the inode to the + * transaction. We do not allow unwritten extents in metadata files. + */ + error = xrep_bmap(sc, XFS_DATA_FORK, false); + if (error) + goto out; + + /* + * Roll the transaction but don't rejoin the inode to the new + * transaction because we're done making changes to the inode. + */ + error = xfs_trans_roll(&sc->tp); + if (error) + goto out; + + /* Bail out if we still need repairs. */ + sc->sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT; + error = xchk_metadata_inode_forks(sc); + if (error) + goto out; + if (xfs_scrub_needs_repair(sc->sm)) + error = -EFSCORRUPTED; +out: + sc->sm->sm_type = smtype; + sc->sm->sm_flags = smflags; + return error; +} diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h index fff462b1351e..15d3a1379f0e 100644 --- a/fs/xfs/scrub/repair.h +++ b/fs/xfs/scrub/repair.h @@ -59,6 +59,8 @@ int xrep_find_ag_btree_roots(struct xfs_scrub *sc, struct xfs_buf *agf_bp, void xrep_force_quotacheck(struct xfs_scrub *sc, xfs_dqtype_t type); int xrep_ino_dqattach(struct xfs_scrub *sc); 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); void xrep_ag_btcur_init(struct xfs_scrub *sc, struct xchk_ag *sa); int xrep_ag_init(struct xfs_scrub *sc, struct xfs_perag *pag, diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index df092ccb28e6..092603b04484 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -543,9 +543,8 @@ retry_op: if (XFS_TEST_ERROR(false, mp, XFS_ERRTAG_FORCE_SCRUB_REPAIR)) sc.sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; - needs_fix = (sc.sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT | - XFS_SCRUB_OFLAG_XCORRUPT | - XFS_SCRUB_OFLAG_PREEN)); + needs_fix = xfs_scrub_needs_repair(sc.sm); + /* * If userspace asked for a repair but it wasn't necessary, * report that back to userspace. |