/* scrub teardown will take care of sc->sa for us */
}
/* * Calculate the ondisk superblock size in bytes given the feature set of the * mounted filesystem (aka the primary sb). This is subtlely different from * the logic in xfs_repair, which computes the size of a secondary sb given the * featureset listed in the secondary sb.
*/ STATIC size_t
xchk_superblock_ondisk_size( struct xfs_mount *mp)
{ if (xfs_has_zoned(mp)) return offsetofend(struct xfs_dsb, sb_rtreserved); if (xfs_has_metadir(mp)) return offsetofend(struct xfs_dsb, sb_pad); if (xfs_has_metauuid(mp)) return offsetofend(struct xfs_dsb, sb_meta_uuid); if (xfs_has_crc(mp)) return offsetofend(struct xfs_dsb, sb_lsn); if (xfs_sb_version_hasmorebits(&mp->m_sb)) return offsetofend(struct xfs_dsb, sb_bad_features2); if (xfs_has_logv2(mp)) return offsetofend(struct xfs_dsb, sb_logsunit); if (xfs_has_sector(mp)) return offsetofend(struct xfs_dsb, sb_logsectsize); /* only support dirv2 or more recent */ return offsetofend(struct xfs_dsb, sb_dirblklog);
}
/* * Scrub the filesystem superblock. * * Note: We do /not/ attempt to check AG 0's superblock. Mount is * responsible for validating all the geometry information in sb 0, so * if the filesystem is capable of initiating online scrub, then clearly * sb 0 is ok and we can use its information to check everything else.
*/ int
xchk_superblock( struct xfs_scrub *sc)
{ struct xfs_mount *mp = sc->mp; struct xfs_buf *bp; struct xfs_dsb *sb; struct xfs_perag *pag;
size_t sblen;
xfs_agnumber_t agno;
uint32_t v2_ok;
__be32 features_mask; int error;
__be16 vernum_mask;
agno = sc->sm->sm_agno; if (agno == 0) return 0;
/* * Grab an active reference to the perag structure. If we can't get * it, we're racing with something that's tearing down the AG, so * signal that the AG no longer exists.
*/
pag = xfs_perag_get(mp, agno); if (!pag) return -ENOENT;
error = xfs_sb_read_secondary(mp, sc->tp, agno, &bp); /* * The superblock verifier can return several different error codes * if it thinks the superblock doesn't look right. For a mount these * would all get bounced back to userspace, but if we're here then the * fs mounted successfully, which means that this secondary superblock * is simply incorrect. Treat all these codes the same way we treat * any corruption.
*/ switch (error) { case -EINVAL: /* also -EWRONGFS */ case -ENOSYS: case -EFBIG:
error = -EFSCORRUPTED;
fallthrough; default: break;
} if (!xchk_process_error(sc, agno, XFS_SB_BLOCK(mp), &error)) goto out_pag;
sb = bp->b_addr;
/* * Verify the geometries match. Fields that are permanently * set by mkfs are checked; fields that can be updated later * (and are not propagated to backup superblocks) are preen * checked.
*/ if (sb->sb_blocksize != cpu_to_be32(mp->m_sb.sb_blocksize))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_dblocks != cpu_to_be64(mp->m_sb.sb_dblocks))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_rblocks != cpu_to_be64(mp->m_sb.sb_rblocks))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_rextents != cpu_to_be64(mp->m_sb.sb_rextents))
xchk_block_set_corrupt(sc, bp);
if (!uuid_equal(&sb->sb_uuid, &mp->m_sb.sb_uuid))
xchk_block_set_preen(sc, bp);
if (sb->sb_logstart != cpu_to_be64(mp->m_sb.sb_logstart))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_rootino != cpu_to_be64(mp->m_sb.sb_rootino))
xchk_block_set_preen(sc, bp);
if (xfs_has_metadir(sc->mp)) { if (sb->sb_rbmino != cpu_to_be64(0))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_rsumino != cpu_to_be64(0))
xchk_block_set_corrupt(sc, bp);
} else { if (sb->sb_rbmino != cpu_to_be64(mp->m_sb.sb_rbmino))
xchk_block_set_preen(sc, bp);
if (sb->sb_rsumino != cpu_to_be64(mp->m_sb.sb_rsumino))
xchk_block_set_preen(sc, bp);
}
if (sb->sb_rextsize != cpu_to_be32(mp->m_sb.sb_rextsize))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_agblocks != cpu_to_be32(mp->m_sb.sb_agblocks))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_agcount != cpu_to_be32(mp->m_sb.sb_agcount))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_rbmblocks != cpu_to_be32(mp->m_sb.sb_rbmblocks))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_logblocks != cpu_to_be32(mp->m_sb.sb_logblocks))
xchk_block_set_corrupt(sc, bp);
/* Check sb_versionnum bits that are set at mkfs time. */
vernum_mask = cpu_to_be16(XFS_SB_VERSION_NUMBITS |
XFS_SB_VERSION_ALIGNBIT |
XFS_SB_VERSION_DALIGNBIT |
XFS_SB_VERSION_SHAREDBIT |
XFS_SB_VERSION_LOGV2BIT |
XFS_SB_VERSION_SECTORBIT |
XFS_SB_VERSION_EXTFLGBIT |
XFS_SB_VERSION_DIRV2BIT); if ((sb->sb_versionnum & vernum_mask) !=
(cpu_to_be16(mp->m_sb.sb_versionnum) & vernum_mask))
xchk_block_set_corrupt(sc, bp);
/* Check sb_versionnum bits that can be set after mkfs time. */
vernum_mask = cpu_to_be16(XFS_SB_VERSION_ATTRBIT |
XFS_SB_VERSION_NLINKBIT |
XFS_SB_VERSION_QUOTABIT); if ((sb->sb_versionnum & vernum_mask) !=
(cpu_to_be16(mp->m_sb.sb_versionnum) & vernum_mask))
xchk_block_set_preen(sc, bp);
if (sb->sb_sectsize != cpu_to_be16(mp->m_sb.sb_sectsize))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_inodesize != cpu_to_be16(mp->m_sb.sb_inodesize))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_inopblock != cpu_to_be16(mp->m_sb.sb_inopblock))
xchk_block_set_corrupt(sc, bp);
if (memcmp(sb->sb_fname, mp->m_sb.sb_fname, sizeof(sb->sb_fname)))
xchk_block_set_preen(sc, bp);
if (sb->sb_blocklog != mp->m_sb.sb_blocklog)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_sectlog != mp->m_sb.sb_sectlog)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_inodelog != mp->m_sb.sb_inodelog)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_inopblog != mp->m_sb.sb_inopblog)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_agblklog != mp->m_sb.sb_agblklog)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_rextslog != mp->m_sb.sb_rextslog)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_imax_pct != mp->m_sb.sb_imax_pct)
xchk_block_set_preen(sc, bp);
/* * Skip the summary counters since we track them in memory anyway. * sb_icount, sb_ifree, sb_fdblocks, sb_frexents
*/
if (xfs_has_metadir(mp)) { if (sb->sb_uquotino != cpu_to_be64(0))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_gquotino != cpu_to_be64(0))
xchk_block_set_preen(sc, bp);
} else { if (sb->sb_uquotino != cpu_to_be64(mp->m_sb.sb_uquotino))
xchk_block_set_preen(sc, bp);
if (sb->sb_gquotino != cpu_to_be64(mp->m_sb.sb_gquotino))
xchk_block_set_preen(sc, bp);
}
/* * Skip the quota flags since repair will force quotacheck. * sb_qflags
*/
if (sb->sb_flags != mp->m_sb.sb_flags)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_shared_vn != mp->m_sb.sb_shared_vn)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_inoalignmt != cpu_to_be32(mp->m_sb.sb_inoalignmt))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_unit != cpu_to_be32(mp->m_sb.sb_unit))
xchk_block_set_preen(sc, bp);
if (sb->sb_width != cpu_to_be32(mp->m_sb.sb_width))
xchk_block_set_preen(sc, bp);
if (sb->sb_dirblklog != mp->m_sb.sb_dirblklog)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_logsectlog != mp->m_sb.sb_logsectlog)
xchk_block_set_corrupt(sc, bp);
if (sb->sb_logsectsize != cpu_to_be16(mp->m_sb.sb_logsectsize))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_logsunit != cpu_to_be32(mp->m_sb.sb_logsunit))
xchk_block_set_corrupt(sc, bp);
/* Do we see any invalid bits in sb_features2? */ if (!xfs_sb_version_hasmorebits(&mp->m_sb)) { if (sb->sb_features2 != 0)
xchk_block_set_corrupt(sc, bp);
} else {
v2_ok = XFS_SB_VERSION2_OKBITS; if (xfs_sb_is_v5(&mp->m_sb))
v2_ok |= XFS_SB_VERSION2_CRCBIT;
if (!!(sb->sb_features2 & cpu_to_be32(~v2_ok)))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_features2 != sb->sb_bad_features2)
xchk_block_set_preen(sc, bp);
}
/* Check sb_features2 flags that are set at mkfs time. */
features_mask = cpu_to_be32(XFS_SB_VERSION2_LAZYSBCOUNTBIT |
XFS_SB_VERSION2_PROJID32BIT |
XFS_SB_VERSION2_CRCBIT |
XFS_SB_VERSION2_FTYPE); if ((sb->sb_features2 & features_mask) !=
(cpu_to_be32(mp->m_sb.sb_features2) & features_mask))
xchk_block_set_corrupt(sc, bp);
/* Check sb_features2 flags that can be set after mkfs time. */
features_mask = cpu_to_be32(XFS_SB_VERSION2_ATTR2BIT); if ((sb->sb_features2 & features_mask) !=
(cpu_to_be32(mp->m_sb.sb_features2) & features_mask))
xchk_block_set_preen(sc, bp);
if (!xfs_has_crc(mp)) { /* all v5 fields must be zero */ if (memchr_inv(&sb->sb_features_compat, 0, sizeof(struct xfs_dsb) -
offsetof(struct xfs_dsb, sb_features_compat)))
xchk_block_set_corrupt(sc, bp);
} else { /* compat features must match */ if (sb->sb_features_compat !=
cpu_to_be32(mp->m_sb.sb_features_compat))
xchk_block_set_corrupt(sc, bp);
/* ro compat features must match */ if (sb->sb_features_ro_compat !=
cpu_to_be32(mp->m_sb.sb_features_ro_compat))
xchk_block_set_corrupt(sc, bp);
/* * NEEDSREPAIR is ignored on a secondary super, so we should * clear it when we find it, though it's not a corruption.
*/
features_mask = cpu_to_be32(XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR); if ((cpu_to_be32(mp->m_sb.sb_features_incompat) ^
sb->sb_features_incompat) & features_mask)
xchk_block_set_preen(sc, bp);
/* all other incompat features must match */ if ((cpu_to_be32(mp->m_sb.sb_features_incompat) ^
sb->sb_features_incompat) & ~features_mask)
xchk_block_set_corrupt(sc, bp);
/* * log incompat features protect newer log record types from * older log recovery code. Log recovery doesn't check the * secondary supers, so we can clear these if needed.
*/ if (sb->sb_features_log_incompat)
xchk_block_set_preen(sc, bp);
/* Don't care about sb_crc */
if (sb->sb_spino_align != cpu_to_be32(mp->m_sb.sb_spino_align))
xchk_block_set_corrupt(sc, bp);
if (xfs_has_metadir(mp)) { if (sb->sb_pquotino != cpu_to_be64(0))
xchk_block_set_corrupt(sc, bp);
} else { if (sb->sb_pquotino != cpu_to_be64(mp->m_sb.sb_pquotino))
xchk_block_set_preen(sc, bp);
}
/* Don't care about sb_lsn */
}
if (xfs_has_metauuid(mp)) { /* The metadata UUID must be the same for all supers */ if (!uuid_equal(&sb->sb_meta_uuid, &mp->m_sb.sb_meta_uuid))
xchk_block_set_corrupt(sc, bp);
}
if (xfs_has_metadir(mp)) { if (sb->sb_metadirino != cpu_to_be64(mp->m_sb.sb_metadirino))
xchk_block_set_preen(sc, bp);
if (sb->sb_rgcount != cpu_to_be32(mp->m_sb.sb_rgcount))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_rgextents != cpu_to_be32(mp->m_sb.sb_rgextents))
xchk_block_set_corrupt(sc, bp);
if (sb->sb_rgblklog != mp->m_sb.sb_rgblklog)
xchk_block_set_corrupt(sc, bp);
if (memchr_inv(sb->sb_pad, 0, sizeof(sb->sb_pad)))
xchk_block_set_corrupt(sc, bp);
}
/* Everything else must be zero. */
sblen = xchk_superblock_ondisk_size(mp); if (memchr_inv((char *)sb + sblen, 0, BBTOB(bp->b_length) - sblen))
xchk_block_set_corrupt(sc, bp);
/* Check the btree block counts in the AGF against the btrees. */ STATICvoid
xchk_agf_xref_btreeblks( struct xfs_scrub *sc)
{ struct xfs_agf *agf = sc->sa.agf_bp->b_addr; struct xfs_mount *mp = sc->mp;
xfs_filblks_t blocks;
xfs_agblock_t btreeblks; int error;
/* agf_btreeblks didn't exist before lazysbcount */ if (!xfs_has_lazysbcount(sc->mp)) return;
/* Check agf_rmap_blocks; set up for agf_btreeblks check */ if (sc->sa.rmap_cur) {
error = xfs_btree_count_blocks(sc->sa.rmap_cur, &blocks); if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur)) return;
btreeblks = blocks - 1; if (blocks != be32_to_cpu(agf->agf_rmap_blocks))
xchk_block_xref_set_corrupt(sc, sc->sa.agf_bp);
} else {
btreeblks = 0;
}
/* * No rmap cursor; we can't xref if we have the rmapbt feature. * We also can't do it if we're missing the free space btree cursors.
*/ if ((xfs_has_rmapbt(mp) && !sc->sa.rmap_cur) ||
!sc->sa.bno_cur || !sc->sa.cnt_cur) return;
/* Do the incore counters match? */ if (pag->pagf_freeblks != be32_to_cpu(agf->agf_freeblks))
xchk_block_set_corrupt(sc, sc->sa.agf_bp); if (pag->pagf_flcount != be32_to_cpu(agf->agf_flcount))
xchk_block_set_corrupt(sc, sc->sa.agf_bp); if (xfs_has_lazysbcount(sc->mp) &&
pag->pagf_btreeblks != be32_to_cpu(agf->agf_btreeblks))
xchk_block_set_corrupt(sc, sc->sa.agf_bp);
xchk_agf_xref(sc);
out: return error;
}
/* AGFL */
struct xchk_agfl_info { /* Number of AGFL entries that the AGF claims are in use. */ unsignedint agflcount;
/* Number of AGFL entries that we found. */ unsignedint nr_entries;
/* Buffer to hold AGFL entries for extent checking. */
xfs_agblock_t *entries;
struct xfs_buf *agfl_bp; struct xfs_scrub *sc;
};
/* Cross-reference with the other btrees. */ STATICvoid
xchk_agfl_block_xref( struct xfs_scrub *sc,
xfs_agblock_t agbno)
{ if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) return;
/* * Scrub teardown will take care of sc->sa for us. Leave sc->sa * active so that the agfl block xref can use it too.
*/
}
/* Scrub the AGFL. */ int
xchk_agfl( struct xfs_scrub *sc)
{ struct xchk_agfl_info sai = {
.sc = sc,
}; struct xfs_agf *agf;
xfs_agnumber_t agno = sc->sm->sm_agno; unsignedint i; int error;
/* Lock the AGF and AGI so that nobody can touch this AG. */
error = xchk_ag_read_headers(sc, agno, &sc->sa); if (!xchk_process_error(sc, agno, XFS_AGFL_BLOCK(sc->mp), &error)) return error; if (!sc->sa.agf_bp) return -EFSCORRUPTED;
/* Try to read the AGFL, and verify its structure if we get it. */
error = xfs_alloc_read_agfl(sc->sa.pag, sc->tp, &sai.agfl_bp); if (!xchk_process_error(sc, agno, XFS_AGFL_BLOCK(sc->mp), &error)) return error;
xchk_buffer_recheck(sc, sai.agfl_bp);
xchk_agfl_xref(sc);
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) goto out;
/* Allocate buffer to ensure uniqueness of AGFL entries. */
agf = sc->sa.agf_bp->b_addr;
sai.agflcount = be32_to_cpu(agf->agf_flcount); if (sai.agflcount > xfs_agfl_size(sc->mp)) {
xchk_block_set_corrupt(sc, sc->sa.agf_bp); goto out;
}
sai.entries = kvcalloc(sai.agflcount, sizeof(xfs_agblock_t),
XCHK_GFP_FLAGS); if (!sai.entries) {
error = -ENOMEM; goto out;
}
/* Check the blocks in the AGFL. */
error = xfs_agfl_walk(sc->mp, sc->sa.agf_bp->b_addr, sai.agfl_bp,
xchk_agfl_block, &sai); if (error == -ECANCELED) {
error = 0; goto out_free;
} if (error) goto out_free;
if (sai.agflcount != sai.nr_entries) {
xchk_block_set_corrupt(sc, sc->sa.agf_bp); goto out_free;
}
/* Sort entries, check for duplicates. */
sort(sai.entries, sai.nr_entries, sizeof(sai.entries[0]),
xchk_agblock_cmp, NULL); for (i = 1; i < sai.nr_entries; i++) { if (sai.entries[i] == sai.entries[i - 1]) {
xchk_block_set_corrupt(sc, sc->sa.agf_bp); break;
}
}
/* scrub teardown will take care of sc->sa for us */
}
/* * Check the unlinked buckets for links to bad inodes. We hold the AGI, so * there cannot be any threads updating unlinked list pointers in this AG.
*/ STATICvoid
xchk_iunlink( struct xfs_scrub *sc, struct xfs_agi *agi)
{ unsignedint i; struct xfs_inode *ip;
for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
xfs_agino_t agino = be32_to_cpu(agi->agi_unlinked[i]);
while (agino != NULLAGINO) { if (agino % XFS_AGI_UNLINKED_BUCKETS != i) {
xchk_block_set_corrupt(sc, sc->sa.agi_bp); return;
}
ip = xfs_iunlink_lookup(sc->sa.pag, agino); if (!ip) {
xchk_block_set_corrupt(sc, sc->sa.agi_bp); return;
}
if (!xfs_inode_on_unlinked_list(ip)) {
xchk_block_set_corrupt(sc, sc->sa.agi_bp); return;
}
agino = be32_to_cpu(agi->agi_dirino); if (!xfs_verify_agino_or_null(pag, agino))
xchk_block_set_corrupt(sc, sc->sa.agi_bp);
/* Check unlinked inode buckets */ for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
agino = be32_to_cpu(agi->agi_unlinked[i]); if (!xfs_verify_agino_or_null(pag, agino))
xchk_block_set_corrupt(sc, sc->sa.agi_bp);
}
if (agi->agi_pad32 != cpu_to_be32(0))
xchk_block_set_corrupt(sc, sc->sa.agi_bp);
/* Do the incore counters match? */ if (pag->pagi_count != be32_to_cpu(agi->agi_count))
xchk_block_set_corrupt(sc, sc->sa.agi_bp); if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount))
xchk_block_set_corrupt(sc, sc->sa.agi_bp);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.