@@ -112,7 +112,8 @@ xfs_scrub_btree_ok(
bool fs_ok,
const char *check,
const char *func,
- int line)
+ int line,
+ bool xref)
{
char bt_ptr[24];
char bt_type[48];
@@ -121,7 +122,7 @@ xfs_scrub_btree_ok(
if (fs_ok)
return fs_ok;
- sc->sm->sm_flags |= XFS_SCRUB_FLAG_CORRUPT;
+ sc->sm->sm_flags |= CORRUPT_FLAG(xref);
xfs_scrub_btree_format(cur, level, bt_type, 48, bt_ptr, 24, &fsbno);
trace_xfs_scrub_btree_error(cur->bc_mp, bt_type, bt_ptr,
@@ -139,7 +140,8 @@ xfs_scrub_btree_op_ok(
int level,
int *error,
const char *func,
- int line)
+ int line,
+ bool xref)
{
char bt_ptr[24];
char bt_type[48];
@@ -153,7 +155,7 @@ xfs_scrub_btree_op_ok(
return xfs_scrub_op_ok(sc,
XFS_FSB_TO_AGNO(cur->bc_mp, fsbno),
XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno),
- bt_type, error, func, line);
+ bt_type, error, func, line, xref);
}
/*
@@ -494,6 +496,84 @@ xfs_scrub_btree_sblock_check_siblings(
return error;
}
+struct check_owner {
+ struct list_head list;
+ xfs_fsblock_t fsb;
+};
+
+/*
+ * Make sure this btree block isn't in the free list and that there's
+ * an rmap record for it.
+ */
+STATIC int
+xfs_scrub_btree_check_block_owner(
+ struct xfs_scrub_btree *bs,
+ xfs_fsblock_t fsb)
+{
+ struct xfs_scrub_ag sa;
+ struct xfs_scrub_ag *psa;
+ xfs_agnumber_t agno;
+ xfs_agblock_t bno;
+ int error = 0;
+
+ agno = XFS_FSB_TO_AGNO(bs->cur->bc_mp, fsb);
+ bno = XFS_FSB_TO_AGBNO(bs->cur->bc_mp, fsb);
+
+ if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+ if (!xfs_scrub_ag_can_lock(bs->sc, agno))
+ return -EDEADLOCK;
+ error = xfs_scrub_ag_init(bs->sc, agno, &sa);
+ if (error)
+ return error;
+ psa = &sa;
+ } else
+ psa = &bs->sc->sa;
+
+ if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
+ xfs_scrub_ag_free(&sa);
+
+ return error;
+}
+
+/* Check the owner of a btree block. */
+STATIC int
+xfs_scrub_btree_check_owner(
+ struct xfs_scrub_btree *bs,
+ struct xfs_buf *bp)
+{
+ struct xfs_btree_cur *cur = bs->cur;
+ struct check_owner *co;
+ xfs_fsblock_t fsbno;
+ xfs_agnumber_t agno;
+
+ if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && bp == NULL)
+ return 0;
+
+ fsbno = XFS_DADDR_TO_FSB(cur->bc_mp, bp->b_bn);
+ agno = XFS_FSB_TO_AGNO(cur->bc_mp, fsbno);
+
+ /* Turn back if we could deadlock. */
+ if ((bs->cur->bc_flags & XFS_BTREE_LONG_PTRS) &&
+ !xfs_scrub_ag_can_lock(bs->sc, agno))
+ return -EDEADLOCK;
+
+ /*
+ * We want to cross-reference each btree block with the bnobt
+ * and the rmapbt. We cannot cross-reference the bnobt or
+ * rmapbt while scanning the bnobt or rmapbt, respectively,
+ * because that would trash the cursor state. Therefore, save
+ * the block numbers for later scanning.
+ */
+ if (cur->bc_btnum == XFS_BTNUM_BNO || cur->bc_btnum == XFS_BTNUM_RMAP) {
+ co = kmem_alloc(sizeof(struct check_owner), KM_SLEEP | KM_NOFS);
+ co->fsb = fsbno;
+ list_add_tail(&co->list, &bs->to_check);
+ return 0;
+ }
+
+ return xfs_scrub_btree_check_block_owner(bs, fsbno);
+}
+
/* Grab and scrub a btree block. */
STATIC int
xfs_scrub_btree_block(
@@ -514,6 +594,10 @@ xfs_scrub_btree_block(
if (error)
return error;
+ error = xfs_scrub_btree_check_owner(bs, *pbp);
+ if (error)
+ return error;
+
return bs->check_siblings_fn(bs, *pblock);
}
@@ -539,6 +623,8 @@ xfs_scrub_btree(
struct xfs_btree_block *block;
int level;
struct xfs_buf *bp;
+ struct check_owner *co;
+ struct check_owner *n;
int i;
int error = 0;
@@ -653,6 +739,14 @@ xfs_scrub_btree(
}
}
+ /* Process deferred owner checks on btree blocks. */
+ list_for_each_entry_safe(co, n, &bs.to_check, list) {
+ if (!error)
+ error = xfs_scrub_btree_check_block_owner(&bs, co->fsb);
+ list_del(&co->list);
+ kmem_free(co);
+ }
+
out_badcursor:
return error;
}
@@ -27,41 +27,72 @@ extern const char * const btree_types[];
/* Check for btree corruption. */
bool xfs_scrub_btree_ok(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur, int level, bool fs_ok,
- const char *check, const char *func, int line);
+ const char *check, const char *func, int line,
+ bool xref);
/* Check for btree operation errors. */
bool xfs_scrub_btree_op_ok(struct xfs_scrub_context *sc,
struct xfs_btree_cur *cur, int level, int *error,
- const char *func, int line);
+ const char *func, int line, bool xref);
#define XFS_SCRUB_BTREC_CHECK(bs, fs_ok) \
xfs_scrub_btree_ok((bs)->sc, (bs)->cur, 0, (fs_ok), #fs_ok, \
- __func__, __LINE__)
+ __func__, __LINE__, false)
#define XFS_SCRUB_BTREC_GOTO(bs, fs_ok, label) \
do { \
if (!xfs_scrub_btree_ok((bs)->sc, (bs)->cur, 0, (fs_ok), \
- #fs_ok, __func__, __LINE__)) \
+ #fs_ok, __func__, __LINE__, false)) \
goto label; \
} while (0)
#define XFS_SCRUB_BTREC_OP_ERROR_GOTO(bs, error, label) \
do { \
if (!xfs_scrub_btree_op_ok((bs)->sc, (bs)->cur, 0, \
- (error), __func__, __LINE__)) \
+ (error), __func__, __LINE__, false)) \
goto label; \
} while (0)
#define XFS_SCRUB_BTKEY_CHECK(bs, level, fs_ok) \
xfs_scrub_btree_ok((bs)->sc, (bs)->cur, (level), (fs_ok), #fs_ok, \
- __func__, __LINE__)
+ __func__, __LINE__, false)
#define XFS_SCRUB_BTKEY_GOTO(bs, level, fs_ok, label) \
do { \
if (!xfs_scrub_btree_ok((bs)->sc, (bs)->cur, (level), (fs_ok), \
- #fs_ok, __func__, __LINE__)) \
+ #fs_ok, __func__, __LINE__, false)) \
goto label; \
} while (0)
#define XFS_SCRUB_BTKEY_OP_ERROR_GOTO(bs, level, error, label) \
do { \
if (!xfs_scrub_btree_op_ok((bs)->sc, (bs)->cur, (level), \
- (error), __func__, __LINE__)) \
+ (error), __func__, __LINE__, false)) \
+ goto label; \
+ } while (0)
+#define XFS_SCRUB_BTREC_XCHECK(bs, fs_ok) \
+ xfs_scrub_btree_ok((bs)->sc, (bs)->cur, 0, (fs_ok), #fs_ok, \
+ __func__, __LINE__, true)
+#define XFS_SCRUB_BTREC_XGOTO(bs, fs_ok, label) \
+ do { \
+ if (!xfs_scrub_btree_ok((bs)->sc, (bs)->cur, 0, (fs_ok), \
+ #fs_ok, __func__, __LINE__, true)) \
+ goto label; \
+ } while (0)
+#define XFS_SCRUB_BTREC_OP_ERROR_XGOTO(bs, error, label) \
+ do { \
+ if (!xfs_scrub_btree_op_ok((bs)->sc, (bs)->cur, 0, \
+ (error), __func__, __LINE__, true)) \
+ goto label; \
+ } while (0)
+#define XFS_SCRUB_BTKEY_XCHECK(bs, level, fs_ok) \
+ xfs_scrub_btree_ok((bs)->sc, (bs)->cur, (level), (fs_ok), #fs_ok, \
+ __func__, __LINE__, true)
+#define XFS_SCRUB_BTKEY_XGOTO(bs, level, fs_ok, label) \
+ do { \
+ if (!xfs_scrub_btree_ok((bs)->sc, (bs)->cur, (level), (fs_ok), \
+ #fs_ok, __func__, __LINE__, true)) \
+ goto label; \
+ } while (0)
+#define XFS_SCRUB_BTKEY_OP_ERROR_XGOTO(bs, level, error, label) \
+ do { \
+ if (!xfs_scrub_btree_op_ok((bs)->sc, (bs)->cur, (level), \
+ (error), __func__, __LINE__, true)) \
goto label; \
} while (0)
@@ -119,7 +119,8 @@ xfs_scrub_op_ok(
const char *type,
int *error,
const char *func,
- int line)
+ int line,
+ bool xref)
{
struct xfs_mount *mp = sc->tp->t_mountp;
@@ -128,7 +129,7 @@ xfs_scrub_op_ok(
trace_xfs_scrub_op_error(mp, agno, bno, type, *error, func, line);
if (*error == -EFSBADCRC || *error == -EFSCORRUPTED) {
- sc->sm->sm_flags |= XFS_SCRUB_FLAG_CORRUPT;
+ sc->sm->sm_flags |= CORRUPT_FLAG(xref);
*error = 0;
}
return false;
@@ -143,7 +144,8 @@ xfs_scrub_file_op_ok(
const char *type,
int *error,
const char *func,
- int line)
+ int line,
+ bool xref)
{
if (*error == 0)
return true;
@@ -151,7 +153,7 @@ xfs_scrub_file_op_ok(
trace_xfs_scrub_file_op_error(sc->ip, whichfork, offset, type, *error,
func, line);
if (*error == -EFSBADCRC || *error == -EFSCORRUPTED) {
- sc->sm->sm_flags |= XFS_SCRUB_FLAG_CORRUPT;
+ sc->sm->sm_flags |= CORRUPT_FLAG(xref);
*error = 0;
}
return false;
@@ -194,7 +196,8 @@ xfs_scrub_block_ok(
bool fs_ok,
const char *check,
const char *func,
- int line)
+ int line,
+ bool xref)
{
struct xfs_mount *mp = sc->tp->t_mountp;
xfs_fsblock_t fsbno;
@@ -208,7 +211,7 @@ xfs_scrub_block_ok(
agno = XFS_FSB_TO_AGNO(mp, fsbno);
bno = XFS_FSB_TO_AGBNO(mp, fsbno);
- sc->sm->sm_flags |= XFS_SCRUB_FLAG_CORRUPT;
+ sc->sm->sm_flags |= CORRUPT_FLAG(xref);
trace_xfs_scrub_block_error(mp, agno, bno, type, check, func, line);
return fs_ok;
}
@@ -223,7 +226,8 @@ xfs_scrub_ino_ok(
bool fs_ok,
const char *check,
const char *func,
- int line)
+ int line,
+ bool xref)
{
struct xfs_inode *ip = sc->ip;
struct xfs_mount *mp = sc->tp->t_mountp;
@@ -243,7 +247,7 @@ xfs_scrub_ino_ok(
bno = XFS_INO_TO_AGINO(mp, ip->i_ino);
}
- sc->sm->sm_flags |= XFS_SCRUB_FLAG_CORRUPT;
+ sc->sm->sm_flags |= CORRUPT_FLAG(xref);
trace_xfs_scrub_ino_error(mp, ino, agno, bno, type, check, func, line);
return fs_ok;
}
@@ -293,12 +297,13 @@ xfs_scrub_data_ok(
bool fs_ok,
const char *check,
const char *func,
- int line)
+ int line,
+ bool xref)
{
if (fs_ok)
return fs_ok;
- sc->sm->sm_flags |= XFS_SCRUB_FLAG_CORRUPT;
+ sc->sm->sm_flags |= CORRUPT_FLAG(xref);
trace_xfs_scrub_data_error(sc->ip, whichfork, offset, type, check,
func, line);
return fs_ok;
@@ -533,6 +538,44 @@ xfs_scrub_ag_lock_all(
return error;
}
+/*
+ * Predicate that decides if we need to evaluate the cross-reference check.
+ * If there was an error accessing the cross-reference btree, just delete
+ * the cursor and skip the check.
+ */
+bool
+__xfs_scrub_should_xref(
+ struct xfs_scrub_context *sc,
+ int error,
+ struct xfs_btree_cur **curpp,
+ const char *func,
+ int line)
+{
+ struct xfs_mount *mp = sc->tp->t_mountp;
+
+ /* If not a btree cross-reference, just check the error code. */
+ if (curpp == NULL) {
+ if (error == 0)
+ return true;
+ trace_xfs_scrub_xref_error(mp, "unknown", error, func, line);
+ return false;
+ }
+
+ ASSERT(*curpp != NULL);
+ /* If no error or we've already given up on xref, just bail out. */
+ if (error == 0 || *curpp == NULL)
+ return true;
+
+ /* xref error, delete cursor and bail out. */
+ sc->sm->sm_flags |= XFS_SCRUB_FLAG_XFAIL;
+ trace_xfs_scrub_xref_error(mp, btree_types[(*curpp)->bc_btnum],
+ error, func, line);
+ xfs_btree_del_cursor(*curpp, XFS_BTREE_ERROR);
+ *curpp = NULL;
+
+ return false;
+}
+
/* Dummy scrubber */
STATIC int
@@ -100,25 +100,44 @@ xfs_scrub_trans_alloc(
return xfs_trans_alloc_empty(mp, tpp);
}
+static inline __u32
+CORRUPT_FLAG(
+ bool xref)
+{
+ return xref ? XFS_SCRUB_FLAG_XCORRUPT : XFS_SCRUB_FLAG_CORRUPT;
+}
+
/* Check for operational errors. */
bool xfs_scrub_op_ok(struct xfs_scrub_context *sc, xfs_agnumber_t agno,
xfs_agblock_t bno, const char *type, int *error,
- const char *func, int line);
+ const char *func, int line, bool xref);
#define XFS_SCRUB_OP_ERROR_GOTO(sc, agno, bno, type, error, label) \
do { \
if (!xfs_scrub_op_ok((sc), (agno), (bno), (type), \
- (error), __func__, __LINE__)) \
+ (error), __func__, __LINE__, false)) \
+ goto label; \
+ } while (0)
+#define XFS_SCRUB_OP_ERROR_XGOTO(sc, agno, bno, type, error, label) \
+ do { \
+ if (!xfs_scrub_op_ok((sc), (agno), (bno), (type), \
+ (error), __func__, __LINE__, true)) \
goto label; \
} while (0)
/* Check for operational errors for a file offset. */
bool xfs_scrub_file_op_ok(struct xfs_scrub_context *sc, int whichfork,
xfs_fileoff_t offset, const char *type,
- int *error, const char *func, int line);
+ int *error, const char *func, int line, bool xref);
#define XFS_SCRUB_FILE_OP_ERROR_GOTO(sc, which, off, type, error, label) \
do { \
if (!xfs_scrub_file_op_ok((sc), (which), (off), (type), \
- (error), __func__, __LINE__)) \
+ (error), __func__, __LINE__, false)) \
+ goto label; \
+ } while (0)
+#define XFS_SCRUB_FILE_OP_ERROR_XGOTO(sc, which, off, type, error, label) \
+ do { \
+ if (!xfs_scrub_file_op_ok((sc), (which), (off), (type), \
+ (error), __func__, __LINE__, true)) \
goto label; \
} while (0)
@@ -141,44 +160,83 @@ bool xfs_scrub_ino_preen(struct xfs_scrub_context *sc, struct xfs_buf *bp,
/* Check for metadata block corruption. */
bool xfs_scrub_block_ok(struct xfs_scrub_context *sc, struct xfs_buf *bp,
const char *type, bool fs_ok, const char *check,
- const char *func, int line);
+ const char *func, int line, bool xref);
#define XFS_SCRUB_CHECK(sc, bp, type, fs_ok) \
xfs_scrub_block_ok((sc), (bp), (type), (fs_ok), #fs_ok, \
- __func__, __LINE__)
+ __func__, __LINE__, false)
#define XFS_SCRUB_GOTO(sc, bp, type, fs_ok, label) \
do { \
if (!xfs_scrub_block_ok((sc), (bp), (type), (fs_ok), \
- #fs_ok, __func__, __LINE__)) \
+ #fs_ok, __func__, __LINE__, false)) \
+ goto label; \
+ } while (0)
+#define XFS_SCRUB_XCHECK(sc, bp, type, fs_ok) \
+ xfs_scrub_block_ok((sc), (bp), (type), (fs_ok), #fs_ok, \
+ __func__, __LINE__, true)
+#define XFS_SCRUB_XGOTO(sc, bp, type, fs_ok, label) \
+ do { \
+ if (!xfs_scrub_block_ok((sc), (bp), (type), (fs_ok), \
+ #fs_ok, __func__, __LINE__, true)) \
goto label; \
} while (0)
/* Check for inode metadata corruption. */
bool xfs_scrub_ino_ok(struct xfs_scrub_context *sc, xfs_ino_t ino,
struct xfs_buf *bp, const char *type, bool fs_ok,
- const char *check, const char *func, int line);
+ const char *check, const char *func, int line,
+ bool xref);
#define XFS_SCRUB_INO_CHECK(sc, ino, bp, type, fs_ok) \
xfs_scrub_ino_ok((sc), (ino), (bp), (type), (fs_ok), #fs_ok, \
- __func__, __LINE__)
+ __func__, __LINE__, false)
#define XFS_SCRUB_INO_GOTO(sc, ino, bp, type, fs_ok, label) \
do { \
if (!xfs_scrub_ino_ok((sc), (ino), (bp), (type), (fs_ok), \
- #fs_ok, __func__, __LINE__)) \
+ #fs_ok, __func__, __LINE__, false)) \
+ goto label; \
+ } while(0)
+#define XFS_SCRUB_INO_XCHECK(sc, ino, bp, type, fs_ok) \
+ xfs_scrub_ino_ok((sc), (ino), (bp), (type), (fs_ok), #fs_ok, \
+ __func__, __LINE__, true)
+#define XFS_SCRUB_INO_XGOTO(sc, ino, bp, type, fs_ok, label) \
+ do { \
+ if (!xfs_scrub_ino_ok((sc), (ino), (bp), (type), (fs_ok), \
+ #fs_ok, __func__, __LINE__, true)) \
goto label; \
} while(0)
/* Check for file data block corruption. */
bool xfs_scrub_data_ok(struct xfs_scrub_context *sc, int whichfork,
xfs_fileoff_t offset, const char *type, bool fs_ok,
- const char *check, const char *func, int line);
+ const char *check, const char *func, int line,
+ bool xref);
#define XFS_SCRUB_DATA_CHECK(sc, whichfork, offset, type, fs_ok) \
xfs_scrub_data_ok((sc), (whichfork), (offset), (type), (fs_ok), \
- #fs_ok, __func__, __LINE__)
+ #fs_ok, __func__, __LINE__, false)
#define XFS_SCRUB_DATA_GOTO(sc, whichfork, offset, type, fs_ok, label) \
do { \
if (!xfs_scrub_data_ok((sc), (whichfork), (offset), \
- (type), (fs_ok), #fs_ok, __func__, __LINE__)) \
+ (type), (fs_ok), #fs_ok, __func__, __LINE__, \
+ false)) \
goto label; \
} while(0)
+#define XFS_SCRUB_DATA_XCHECK(sc, whichfork, offset, type, fs_ok) \
+ xfs_scrub_data_ok((sc), (whichfork), (offset), (type), (fs_ok), \
+ #fs_ok, __func__, __LINE__, true)
+#define XFS_SCRUB_DATA_XGOTO(sc, whichfork, offset, type, fs_ok, label) \
+ do { \
+ if (!xfs_scrub_data_ok((sc), (whichfork), (offset), \
+ (type), (fs_ok), #fs_ok, __func__, __LINE__, \
+ true)) \
+ goto label; \
+ } while(0)
+
+bool __xfs_scrub_should_xref(struct xfs_scrub_context *sc, int error,
+ struct xfs_btree_cur **curpp, const char *func,
+ int line);
+#define xfs_scrub_should_xref(sc, error, curpp) \
+ __xfs_scrub_should_xref((sc), (error), (curpp), __func__, __LINE__)
+#define xfs_scrub_btree_should_xref(bs, error, curpp) \
+ __xfs_scrub_should_xref((bs)->sc, (error), (curpp), __func__, __LINE__)
bool xfs_scrub_ag_can_lock(struct xfs_scrub_context *sc, xfs_agnumber_t agno);
int xfs_scrub_ag_lock_all(struct xfs_scrub_context *sc);
Create some helper functions that we'll use later to cross reference metadata with other metadata. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> --- fs/xfs/scrub/btree.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++-- fs/xfs/scrub/btree.h | 47 +++++++++++++++++++---- fs/xfs/scrub/common.c | 63 +++++++++++++++++++++++++----- fs/xfs/scrub/common.h | 84 ++++++++++++++++++++++++++++++++++------ 4 files changed, 261 insertions(+), 35 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html