/*
* Copyright (C) 2017 Oracle. All Rights Reserved.
*
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#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_defer.h"
#include "xfs_btree.h"
#include "xfs_bit.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_inode.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "xfs_rmap.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
/*
* Walk all the blocks in the AGFL. The fn function can return any negative
* error code or XFS_BTREE_QUERY_RANGE_ABORT.
*/
int
xfs_scrub_walk_agfl(
struct xfs_scrub_context *sc,
int (*fn)(struct xfs_scrub_context *,
xfs_agblock_t bno, void *),
void *priv)
{
struct xfs_agf *agf;
__be32 *agfl_bno;
struct xfs_mount *mp = sc->mp;
unsigned int flfirst;
unsigned int fllast;
int i;
int error;
agf = XFS_BUF_TO_AGF(sc->sa.agf_bp);
agfl_bno = XFS_BUF_TO_AGFL_BNO(mp, sc->sa.agfl_bp);
flfirst = be32_to_cpu(agf->agf_flfirst);
fllast = be32_to_cpu(agf->agf_fllast);
/* Nothing to walk in an empty AGFL. */
if (agf->agf_flcount == cpu_to_be32(0))
return 0;
/* first to last is a consecutive list. */
if (fllast >= flfirst) {
for (i = flfirst; i <= fllast; i++) {
error = fn(sc, be32_to_cpu(agfl_bno[i]), priv);
if (error)
return error;
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return error;
}
return 0;
}
/* first to the end */
for (i = flfirst; i < xfs_agfl_size(mp); i++) {
error = fn(sc, be32_to_cpu(agfl_bno[i]), priv);
if (error)
return error;
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return error;
}
/* the start to last. */
for (i = 0; i <= fllast; i++) {
error = fn(sc, be32_to_cpu(agfl_bno[i]), priv);
if (error)
return error;
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return error;
}
return 0;
}
/* Superblock */
/* Cross-reference with the other btrees. */
STATIC void
xfs_scrub_superblock_xref(
struct xfs_scrub_context *sc,
struct xfs_buf *bp)
{
struct xfs_owner_info oinfo;
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 = xfs_scrub_ag_init(sc, agno, &sc->sa);
if (!xfs_scrub_xref_process_error(sc, agno, agbno, &error))
return;
xfs_scrub_xref_is_used_space(sc, agbno, 1);
xfs_scrub_xref_is_not_inode_chunk(sc, agbno, 1);
xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_FS);
xfs_scrub_xref_is_owned_by(sc, agbno, 1, &oinfo);
xfs_scrub_xref_is_not_shared(sc, agbno, 1);
/* scrub teardown will take care of sc->sa for us */
}
/*
* 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
xfs_scrub_superblock(
struct xfs_scrub_context *sc)
{
struct xfs_mount *mp = sc->mp;
struct xfs_buf *bp;
struct xfs_dsb *sb;
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;
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 bo