summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@kernel.org>2021-09-01 10:45:55 -0700
committerDarrick J. Wong <djwong@kernel.org>2021-09-17 18:54:52 -0700
commit3c18a9d69da0ca621e0b56f55a1ddf7d350d6c65 (patch)
tree6033060c949e56def50cbe78663b033083aac492
parentc0d680ffb0b1f0f29f30bf986db407c7b310915b (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.c16
-rw-r--r--fs/xfs/scrub/common.h8
-rw-r--r--fs/xfs/scrub/repair.c80
-rw-r--r--fs/xfs/scrub/repair.h2
-rw-r--r--fs/xfs/scrub/scrub.c5
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.