diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2018-08-11 03:55:57 +0300 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2018-08-14 18:17:02 +0300 |
commit | 1fc25f51d7c18e07e8cf935cbdd4603adfc7b3ad (patch) | |
tree | 504a881c0fe9c4eaba3f114855245f6d46b11a99 /fs | |
parent | 00d22a1c367d03f469f8e1e924b062b09b0545e8 (diff) | |
download | linux-1fc25f51d7c18e07e8cf935cbdd4603adfc7b3ad.tar.xz |
xfs: sanity check ag header values in xrep_calc_ag_resblks
Check the values we read in from the AG headers when calculating the
block reservations for a repair transaction. If they're obviously
wrong, substitute worst case assumptions (rather than ENOSPC on a bogus
reservation request).
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/scrub/repair.c | 46 |
1 files changed, 27 insertions, 19 deletions
diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index 17cf48564390..9f08dd9bf1d5 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -195,8 +195,8 @@ xrep_calc_ag_resblks( struct xfs_scrub_metadata *sm = sc->sm; struct xfs_perag *pag; struct xfs_buf *bp; - xfs_agino_t icount = 0; - xfs_extlen_t aglen = 0; + xfs_agino_t icount = NULLAGINO; + xfs_extlen_t aglen = NULLAGBLOCK; xfs_extlen_t usedlen; xfs_extlen_t freelen; xfs_extlen_t bnobt_sz; @@ -208,20 +208,14 @@ xrep_calc_ag_resblks( if (!(sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR)) return 0; - /* Use in-core counters if possible. */ pag = xfs_perag_get(mp, sm->sm_agno); - if (pag->pagi_init) + if (pag->pagi_init) { + /* Use in-core icount if possible. */ icount = pag->pagi_count; - - /* - * Otherwise try to get the actual counters from disk; if not, make - * some worst case assumptions. - */ - if (icount == 0) { + } else { + /* Try to get the actual counters from disk. */ error = xfs_ialloc_read_agi(mp, NULL, sm->sm_agno, &bp); - if (error) { - icount = mp->m_sb.sb_agblocks / mp->m_sb.sb_inopblock; - } else { + if (!error) { icount = pag->pagi_count; xfs_buf_relse(bp); } @@ -229,18 +223,32 @@ xrep_calc_ag_resblks( /* Now grab the block counters from the AGF. */ error = xfs_alloc_read_agf(mp, NULL, sm->sm_agno, 0, &bp); - if (error) { - aglen = mp->m_sb.sb_agblocks; - freelen = aglen; - usedlen = aglen; - } else { + if (!error) { aglen = be32_to_cpu(XFS_BUF_TO_AGF(bp)->agf_length); - freelen = pag->pagf_freeblks; + freelen = be32_to_cpu(XFS_BUF_TO_AGF(bp)->agf_freeblks); usedlen = aglen - freelen; xfs_buf_relse(bp); } xfs_perag_put(pag); + /* If the icount is impossible, make some worst-case assumptions. */ + if (icount == NULLAGINO || + !xfs_verify_agino(mp, sm->sm_agno, icount)) { + xfs_agino_t first, last; + + xfs_agino_range(mp, sm->sm_agno, &first, &last); + icount = last - first + 1; + } + + /* If the block counts are impossible, make worst-case assumptions. */ + if (aglen == NULLAGBLOCK || + aglen != xfs_ag_block_count(mp, sm->sm_agno) || + freelen >= aglen) { + aglen = xfs_ag_block_count(mp, sm->sm_agno); + freelen = aglen; + usedlen = aglen; + } + trace_xrep_calc_ag_resblks(mp, sm->sm_agno, icount, aglen, freelen, usedlen); |