/* Cross reference the inode fields with the forks. */ STATIC void xchk_inode_xref_bmap( struct xfs_scrub *sc, struct xfs_dinode *dip) { xfs_extnum_t nextents; xfs_filblks_t count; xfs_filblks_t acount; int error; if (xchk_skip_xref(sc->sm)) return; /* Walk all the extents to check nextents/naextents/nblocks. */ error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK, &nextents, &count); if (!xchk_should_check_xref(sc, &error, NULL)) return; if (nextents < be32_to_cpu(dip->di_nextents)) xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino); error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK, &nextents, &acount); if (!xchk_should_check_xref(sc, &error, NULL)) return; if (nextents != be16_to_cpu(dip->di_anextents)) xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino); /* Check nblocks against the inode. */ if (count + acount != be64_to_cpu(dip->di_nblocks)) xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino); }
/* Cross-reference a rmap against the refcount btree. */ STATIC void xchk_rmapbt_xref_refc( struct xfs_scrub *sc, struct xfs_rmap_irec *irec) { xfs_agblock_t fbno; xfs_extlen_t flen; bool non_inode; bool is_bmbt; bool is_attr; bool is_unwritten; int error; if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm)) return; non_inode = XFS_RMAP_NON_INODE_OWNER(irec->rm_owner); is_bmbt = irec->rm_flags & XFS_RMAP_BMBT_BLOCK; is_attr = irec->rm_flags & XFS_RMAP_ATTR_FORK; is_unwritten = irec->rm_flags & XFS_RMAP_UNWRITTEN; /* If this is shared, must be a data fork extent. */ error = xfs_refcount_find_shared(sc->sa.refc_cur, irec->rm_startblock, irec->rm_blockcount, &fbno, &flen, false); if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur)) return; if (flen != 0 && (non_inode || is_attr || is_bmbt || is_unwritten)) xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0); }
/* * Make sure the finobt doesn't think this inode is free. * We don't have to check the inobt ourselves because we got the inode via * IGET_UNTRUSTED, which checks the inobt for us. */ static void xchk_inode_xref_finobt( struct xfs_scrub *sc, xfs_ino_t ino) { struct xfs_inobt_rec_incore rec; xfs_agino_t agino; int has_record; int error; if (!sc->sa.fino_cur || xchk_skip_xref(sc->sm)) return; agino = XFS_INO_TO_AGINO(sc->mp, ino); /* * Try to get the finobt record. If we can't get it, then we're * in good shape. */ error = xfs_inobt_lookup(sc->sa.fino_cur, agino, XFS_LOOKUP_LE, &has_record); if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) || !has_record) return; error = xfs_inobt_get_rec(sc->sa.fino_cur, &rec, &has_record); if (!xchk_should_check_xref(sc, &error, &sc->sa.fino_cur) || !has_record) return; /* * Otherwise, make sure this record either doesn't cover this inode, * or that it does but it's marked present. */ if (rec.ir_startino > agino || rec.ir_startino + XFS_INODES_PER_CHUNK <= agino) return; if (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino)) xchk_btree_xref_set_corrupt(sc, sc->sa.fino_cur, 0); }
/* xref check that the extent has no reverse mapping at all */ void xchk_xref_has_no_owner( struct xfs_scrub *sc, xfs_agblock_t bno, xfs_extlen_t len) { bool has_rmap; int error; if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm)) return; error = xfs_rmap_has_record(sc->sa.rmap_cur, bno, len, &has_rmap); if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur)) return; if (has_rmap) xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); }
/* xref check that the extent is owned by a given owner */ static inline void xchk_xref_check_owner( struct xfs_scrub *sc, xfs_agblock_t bno, xfs_extlen_t len, const struct xfs_owner_info *oinfo, bool should_have_rmap) { bool has_rmap; int error; if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm)) return; error = xfs_rmap_record_exists(sc->sa.rmap_cur, bno, len, oinfo, &has_rmap); if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur)) return; if (has_rmap != should_have_rmap) xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); }
/* * If we're checking the finobt, cross-reference with the inobt. * Otherwise we're checking the inobt; if there is an finobt, make sure * we have a record or not depending on freecount. */ static inline void xchk_iallocbt_chunk_xref_other( struct xfs_scrub *sc, struct xfs_inobt_rec_incore *irec, xfs_agino_t agino) { struct xfs_btree_cur **pcur; bool has_irec; int error; if (sc->sm->sm_type == XFS_SCRUB_TYPE_FINOBT) pcur = &sc->sa.ino_cur; else pcur = &sc->sa.fino_cur; if (!(*pcur)) return; error = xfs_ialloc_has_inode_record(*pcur, agino, agino, &has_irec); if (!xchk_should_check_xref(sc, &error, pcur)) return; if (((irec->ir_freecount > 0 && !has_irec) || (irec->ir_freecount == 0 && has_irec))) xchk_btree_xref_set_corrupt(sc, *pcur, 0); }