// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2017-2023 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_btree.h"
#include "xfs_sb.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "xfs_rmap.h"
#include "xfs_ag.h"
#include "xfs_inode.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
int
xchk_setup_agheader(
struct xfs_scrub *sc)
{
if (xchk_need_intent_drain(sc))
xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
return xchk_setup_fs(sc);
}
/* Superblock */
/* Cross-reference with the other btrees. */
STATIC void
xchk_superblock_xref(
struct xfs_scrub *sc,
struct xfs_buf *bp)
{
struct xfs_mount *mp = sc->mp;
xfs_agnumber_t agno = sc->sm->sm_agno;
xfs_agblock_t agbno;
int error;
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return;
agbno = XFS_SB_BLOCK(mp);
error = xchk_ag_init_existing(sc, agno, &sc->sa);
if (!xchk_xref_process_error(sc, agno, agbno, &error))
return;
xchk_xref_is_used_space(sc, agbno, 1);
xchk_xref_is_not_inode_chunk(sc, agbno, 1);
xchk_xref_is_only_owned_by(sc, agbno, 1, &XFS_RMAP_OINFO_FS);
xchk_xref_is_not_shared(sc, agbno, 1);
xchk_xref_is_not_cow_staging(sc, agbno, 1);
/* 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_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 (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