/* Break all our leases, we're going to mess with things. */ if (is_repair) {
error = xfs_break_layouts(VFS_I(sc->ip),
&sc->ilock_flags, BREAK_WRITE); if (error) goto out;
}
/* Drop the page cache if we're repairing block mappings. */ if (is_repair) {
error = invalidate_inode_pages2(
VFS_I(sc->ip)->i_mapping); if (error) goto out;
}
}
/* Got the inode, lock it and we're ready to go. */
error = xchk_trans_alloc(sc, 0); if (error) goto out;
error = xchk_ino_dqattach(sc); if (error) goto out;
xchk_ilock(sc, XFS_ILOCK_EXCL);
out: /* scrub teardown will unlock and release the inode */ return error;
}
if (!has_rmap)
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); return has_rmap;
}
/* Make sure that we have rmapbt records for this data/attr fork extent. */ STATICvoid
xchk_bmap_xref_rmap( struct xchk_bmap_info *info, struct xfs_bmbt_irec *irec,
xfs_agblock_t bno)
{ struct xfs_rmap_irec rmap; unsignedlonglong rmap_end;
uint64_t owner = info->sc->ip->i_ino;
if (xchk_skip_xref(info->sc->sm)) return;
/* Find the rmap record for this irec. */ if (!xchk_bmap_get_rmap(info, irec, bno, owner, &rmap)) return;
if (!!(info->whichfork == XFS_ATTR_FORK) !=
!!(rmap.rm_flags & XFS_RMAP_ATTR_FORK))
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
}
/* Make sure that we have rmapbt records for this COW fork extent. */ STATICvoid
xchk_bmap_xref_rmap_cow( struct xchk_bmap_info *info, struct xfs_bmbt_irec *irec,
xfs_agblock_t bno)
{ struct xfs_rmap_irec rmap; unsignedlonglong rmap_end;
uint64_t owner = XFS_RMAP_OWN_COW;
if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm)) return;
/* Find the rmap record for this irec. */ if (!xchk_bmap_get_rmap(info, irec, bno, owner, &rmap)) return;
if (!xfs_verify_fileext(mp, irec->br_startoff, irec->br_blockcount))
xchk_fblock_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
xchk_bmap_dirattr_extent(ip, info, irec);
/* Make sure the extent points to a valid place. */ if (info->is_rt &&
!xfs_verify_rtbext(mp, irec->br_startblock, irec->br_blockcount))
xchk_fblock_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); if (!info->is_rt &&
!xfs_verify_fsbext(mp, irec->br_startblock, irec->br_blockcount))
xchk_fblock_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
/* We don't allow unwritten extents on attr forks. */ if (irec->br_state == XFS_EXT_UNWRITTEN &&
info->whichfork == XFS_ATTR_FORK)
xchk_fblock_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) return;
if (info->is_rt)
xchk_bmap_rt_iextent_xref(ip, info, irec); else
xchk_bmap_iextent_xref(ip, info, irec);
}
/* Can we find bmaps that fit this rmap? */ STATICint
xchk_bmap_check_rmap( struct xfs_btree_cur *cur, conststruct xfs_rmap_irec *rec, void *priv)
{ struct xfs_bmbt_irec irec; struct xfs_rmap_irec check_rec; struct xchk_bmap_check_rmap_info *sbcri = priv; struct xfs_ifork *ifp; struct xfs_scrub *sc = sbcri->sc; bool have_map;
/* Is this even the right fork? */ if (rec->rm_owner != sc->ip->i_ino) return0; if ((sbcri->whichfork == XFS_ATTR_FORK) ^
!!(rec->rm_flags & XFS_RMAP_ATTR_FORK)) return0; if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK) return0;
/* Now look up the bmbt record. */
ifp = xfs_ifork_ptr(sc->ip, sbcri->whichfork); if (!ifp) {
xchk_fblock_set_corrupt(sc, sbcri->whichfork,
rec->rm_offset); goto out;
}
have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
&sbcri->icur, &irec); if (!have_map)
xchk_fblock_set_corrupt(sc, sbcri->whichfork,
rec->rm_offset); /* *bmapextentrecordlengthsareconstrainedto2^21blocksinlength *becauseofspaceconstraintsintheon-diskmetadatastructure. *However,rmapextentrecordlengthsareconstrainedonlybyAG *length,sowehavetoloopthroughthebmbttomakesurethatthe *entirermapiscoveredbybmbtrecords.
*/
check_rec = *rec; while (have_map) { if (irec.br_startoff != check_rec.rm_offset)
xchk_fblock_set_corrupt(sc, sbcri->whichfork,
check_rec.rm_offset); if (irec.br_startblock !=
xfs_gbno_to_fsb(cur->bc_group, check_rec.rm_startblock))
xchk_fblock_set_corrupt(sc, sbcri->whichfork,
check_rec.rm_offset); if (irec.br_blockcount > check_rec.rm_blockcount)
xchk_fblock_set_corrupt(sc, sbcri->whichfork,
check_rec.rm_offset); if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) break;
check_rec.rm_startblock += irec.br_blockcount;
check_rec.rm_offset += irec.br_blockcount;
check_rec.rm_blockcount -= irec.br_blockcount; if (check_rec.rm_blockcount == 0) break;
have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec); if (!have_map)
xchk_fblock_set_corrupt(sc, sbcri->whichfork,
check_rec.rm_offset);
}
out: if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) return -ECANCELED; return0;
}
/* Make sure each rmap has a corresponding bmbt entry. */ STATICint
xchk_bmap_check_ag_rmaps( struct xfs_scrub *sc, int whichfork, struct xfs_perag *pag)
{ struct xchk_bmap_check_rmap_info sbcri; struct xfs_btree_cur *cur; struct xfs_buf *agf; int error;
error = xfs_alloc_read_agf(pag, sc->tp, 0, &agf); if (error) return error;
cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, pag);
if (!xfs_has_rmapbt(sc->mp)) returnfalse; if (info->whichfork == XFS_COW_FORK) returnfalse; if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) returnfalse;
if (info->whichfork == XFS_ATTR_FORK) return xchk_bmap_check_empty_attrfork(sc->ip);
return xchk_bmap_check_empty_datafork(sc->ip);
}
/* Make sure each rmap has a corresponding bmbt entry. */ STATICint
xchk_bmap_check_rmaps( struct xfs_scrub *sc, int whichfork)
{ struct xfs_perag *pag = NULL; int error;
if (xfs_ifork_is_realtime(sc->ip, whichfork)) { struct xfs_rtgroup *rtg = NULL;
if (!xfs_verify_fileext(mp, irec->br_startoff, irec->br_blockcount))
xchk_fblock_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
/* Make sure the extent points to a valid place. */ if (irec->br_blockcount > XFS_MAX_BMBT_EXTLEN)
xchk_fblock_set_corrupt(info->sc, info->whichfork,
irec->br_startoff);
}
/* Decide if this individual fork mapping is ok. */ staticbool
xchk_bmap_iext_mapping( struct xchk_bmap_info *info, conststruct xfs_bmbt_irec *irec)
{ /* There should never be a "hole" extent in either extent list. */ if (irec->br_startblock == HOLESTARTBLOCK) returnfalse; if (irec->br_blockcount > XFS_MAX_BMBT_EXTLEN) returnfalse; returntrue;
}
/* Are these two mappings contiguous with each other? */ staticinlinebool
xchk_are_bmaps_contiguous( conststruct xchk_bmap_info *info, conststruct xfs_bmbt_irec *b1, conststruct xfs_bmbt_irec *b2)
{ struct xfs_mount *mp = info->sc->mp;
/* Don't try to combine unallocated mappings. */ if (!xfs_bmap_is_real_extent(b1)) returnfalse; if (!xfs_bmap_is_real_extent(b2)) returnfalse;
/* Does b2 come right after b1 in the logical and physical range? */ if (b1->br_startoff + b1->br_blockcount != b2->br_startoff) returnfalse; if (b1->br_startblock + b1->br_blockcount != b2->br_startblock) returnfalse; if (b1->br_state != b2->br_state) returnfalse;
/* *Don'tcombinebmapsthatwouldcrossrtgroupboundaries.Thisisa *validstate,butifcombinedtheywillfailrtbextentchecks.
*/ if (info->is_rt && xfs_has_rtgroups(mp)) { if (xfs_rtb_to_rgno(mp, b1->br_startblock) !=
xfs_rtb_to_rgno(mp, b2->br_startblock)) returnfalse;
}
/* Advance to the next iextent record and check the mapping. */
xfs_iext_next(ifp, &info->icur); if (!xfs_iext_get_extent(ifp, &info->icur, irec)) returnfalse;
if (!xchk_bmap_iext_mapping(info, irec)) {
xchk_fblock_set_corrupt(info->sc, info->whichfork,
irec->br_startoff); returnfalse;
}
nr++;
/* *Iteratesubsequentiextentrecordsandmergethemwiththeone *thatwejustread,ifpossible.
*/ while (xfs_iext_peek_next_extent(ifp, &info->icur, &got)) { if (!xchk_are_bmaps_contiguous(info, irec, &got)) break;
if (!xchk_bmap_iext_mapping(info, &got)) {
xchk_fblock_set_corrupt(info->sc, info->whichfork,
got.br_startoff); returnfalse;
}
nr++;
switch (whichfork) { case XFS_COW_FORK: /* No CoW forks filesystem doesn't support out of place writes */ if (!xfs_has_reflink(mp) && !xfs_has_zoned(mp)) {
xchk_ino_set_corrupt(sc, sc->ip->i_ino); return0;
} break; case XFS_ATTR_FORK: /* *"attr"meansthatanattrforkwascreatedatsomepointin *thelifeofthisfilesystem."attr2"meansthatinodeshave *variable-sizeddata/attrforkareas.Henceweonlycheck *attrhere.
*/ if (!xfs_has_attr(mp))
xchk_ino_set_corrupt(sc, sc->ip->i_ino); break; default:
ASSERT(whichfork == XFS_DATA_FORK); break;
}
/* Check the fork values */ switch (ifp->if_format) { case XFS_DINODE_FMT_UUID: case XFS_DINODE_FMT_DEV: case XFS_DINODE_FMT_LOCAL: case XFS_DINODE_FMT_META_BTREE: /* No mappings to check. */ if (whichfork == XFS_COW_FORK)
xchk_fblock_set_corrupt(sc, whichfork, 0); return0; case XFS_DINODE_FMT_EXTENTS: break; case XFS_DINODE_FMT_BTREE: if (whichfork == XFS_COW_FORK) {
xchk_fblock_set_corrupt(sc, whichfork, 0); return0;
}
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) return0;
/* Find the offset of the last extent in the mapping. */
error = xfs_bmap_last_offset(ip, &endoff, whichfork); if (!xchk_fblock_process_error(sc, whichfork, 0, &error)) return error;
/* *Scrubextentrecords.Weuseaspecialiteratorfunctionherethat *combinesadjacentmappingsiftheyarelogicallyandphysically *contiguous.Forlargeallocationsthatrequiremultiplebmbt *records,thisreducesthenumberofcross-referencingcalls,which *reducesruntime.Crossreferencingwiththermapissimplerbecause *thermapmustmatchthecombinedmappingexactly.
*/ while (xchk_bmap_iext_iter(&info, &irec)) { if (xchk_should_terminate(sc, &error) ||
(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) return0;
if (irec.br_startoff >= endoff) {
xchk_fblock_set_corrupt(sc, whichfork,
irec.br_startoff); return0;
}
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.