diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_ialloc.c')
| -rw-r--r-- | fs/xfs/libxfs/xfs_ialloc.c | 162 | 
1 files changed, 42 insertions, 120 deletions
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index de627fa19168..0d968e8143aa 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0  /*   * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.   * All Rights Reserved. - * - * 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. - * - * 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" @@ -133,22 +121,51 @@ xfs_inobt_get_rec(  	struct xfs_inobt_rec_incore	*irec,  	int				*stat)  { +	struct xfs_mount		*mp = cur->bc_mp; +	xfs_agnumber_t			agno = cur->bc_private.a.agno;  	union xfs_btree_rec		*rec;  	int				error; +	uint64_t			realfree;  	error = xfs_btree_get_rec(cur, &rec, stat);  	if (error || *stat == 0)  		return error; -	xfs_inobt_btrec_to_irec(cur->bc_mp, rec, irec); +	xfs_inobt_btrec_to_irec(mp, rec, irec); + +	if (!xfs_verify_agino(mp, agno, irec->ir_startino)) +		goto out_bad_rec; +	if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT || +	    irec->ir_count > XFS_INODES_PER_CHUNK) +		goto out_bad_rec; +	if (irec->ir_freecount > XFS_INODES_PER_CHUNK) +		goto out_bad_rec; + +	/* if there are no holes, return the first available offset */ +	if (!xfs_inobt_issparse(irec->ir_holemask)) +		realfree = irec->ir_free; +	else +		realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec); +	if (hweight64(realfree) != irec->ir_freecount) +		goto out_bad_rec;  	return 0; + +out_bad_rec: +	xfs_warn(mp, +		"%s Inode BTree record corruption in AG %d detected!", +		cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free", agno); +	xfs_warn(mp, +"start inode 0x%x, count 0x%x, free 0x%x freemask 0x%llx, holemask 0x%x", +		irec->ir_startino, irec->ir_count, irec->ir_freecount, +		irec->ir_free, irec->ir_holemask); +	return -EFSCORRUPTED;  }  /*   * Insert a single inobt record. Cursor must already point to desired location.   */ -STATIC int +int  xfs_inobt_insert_rec(  	struct xfs_btree_cur	*cur,  	uint16_t		holemask, @@ -880,6 +897,7 @@ sparse_alloc:  	be32_add_cpu(&agi->agi_freecount, newlen);  	pag = xfs_perag_get(args.mp, agno);  	pag->pagi_freecount += newlen; +	pag->pagi_count += newlen;  	xfs_perag_put(pag);  	agi->agi_newino = cpu_to_be32(newino); @@ -1974,6 +1992,7 @@ xfs_difree_inobt(  		xfs_ialloc_log_agi(tp, agbp, XFS_AGI_COUNT | XFS_AGI_FREECOUNT);  		pag = xfs_perag_get(mp, agno);  		pag->pagi_freecount -= ilen - 1; +		pag->pagi_count -= ilen;  		xfs_perag_put(pag);  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, -ilen);  		xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -(ilen - 1)); @@ -2477,26 +2496,13 @@ xfs_ialloc_log_agi(  	}  } -#ifdef DEBUG -STATIC void -xfs_check_agi_unlinked( -	struct xfs_agi		*agi) -{ -	int			i; - -	for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) -		ASSERT(agi->agi_unlinked[i]); -} -#else -#define xfs_check_agi_unlinked(agi) -#endif -  static xfs_failaddr_t  xfs_agi_verify(  	struct xfs_buf	*bp)  {  	struct xfs_mount *mp = bp->b_target->bt_mount;  	struct xfs_agi	*agi = XFS_BUF_TO_AGI(bp); +	int		i;  	if (xfs_sb_version_hascrc(&mp->m_sb)) {  		if (!uuid_equal(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid)) @@ -2532,7 +2538,13 @@ xfs_agi_verify(  	if (bp->b_pag && be32_to_cpu(agi->agi_seqno) != bp->b_pag->pag_agno)  		return __this_address; -	xfs_check_agi_unlinked(agi); +	for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) { +		if (agi->agi_unlinked[i] == NULLAGINO) +			continue; +		if (!xfs_verify_ino(mp, be32_to_cpu(agi->agi_unlinked[i]))) +			return __this_address; +	} +  	return NULL;  } @@ -2664,96 +2676,6 @@ xfs_ialloc_pagi_init(  	return 0;  } -/* Calculate the first and last possible inode number in an AG. */ -void -xfs_ialloc_agino_range( -	struct xfs_mount	*mp, -	xfs_agnumber_t		agno, -	xfs_agino_t		*first, -	xfs_agino_t		*last) -{ -	xfs_agblock_t		bno; -	xfs_agblock_t		eoag; - -	eoag = xfs_ag_block_count(mp, agno); - -	/* -	 * Calculate the first inode, which will be in the first -	 * cluster-aligned block after the AGFL. -	 */ -	bno = round_up(XFS_AGFL_BLOCK(mp) + 1, -			xfs_ialloc_cluster_alignment(mp)); -	*first = XFS_OFFBNO_TO_AGINO(mp, bno, 0); - -	/* -	 * Calculate the last inode, which will be at the end of the -	 * last (aligned) cluster that can be allocated in the AG. -	 */ -	bno = round_down(eoag, xfs_ialloc_cluster_alignment(mp)); -	*last = XFS_OFFBNO_TO_AGINO(mp, bno, 0) - 1; -} - -/* - * Verify that an AG inode number pointer neither points outside the AG - * nor points at static metadata. - */ -bool -xfs_verify_agino( -	struct xfs_mount	*mp, -	xfs_agnumber_t		agno, -	xfs_agino_t		agino) -{ -	xfs_agino_t		first; -	xfs_agino_t		last; - -	xfs_ialloc_agino_range(mp, agno, &first, &last); -	return agino >= first && agino <= last; -} - -/* - * Verify that an FS inode number pointer neither points outside the - * filesystem nor points at static AG metadata. - */ -bool -xfs_verify_ino( -	struct xfs_mount	*mp, -	xfs_ino_t		ino) -{ -	xfs_agnumber_t		agno = XFS_INO_TO_AGNO(mp, ino); -	xfs_agino_t		agino = XFS_INO_TO_AGINO(mp, ino); - -	if (agno >= mp->m_sb.sb_agcount) -		return false; -	if (XFS_AGINO_TO_INO(mp, agno, agino) != ino) -		return false; -	return xfs_verify_agino(mp, agno, agino); -} - -/* Is this an internal inode number? */ -bool -xfs_internal_inum( -	struct xfs_mount	*mp, -	xfs_ino_t		ino) -{ -	return ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino || -		(xfs_sb_version_hasquota(&mp->m_sb) && -		 xfs_is_quota_inode(&mp->m_sb, ino)); -} - -/* - * Verify that a directory entry's inode number doesn't point at an internal - * inode, empty space, or static AG metadata. - */ -bool -xfs_verify_dir_ino( -	struct xfs_mount	*mp, -	xfs_ino_t		ino) -{ -	if (xfs_internal_inum(mp, ino)) -		return false; -	return xfs_verify_ino(mp, ino); -} -  /* Is there an inode record covering a given range of inode numbers? */  int  xfs_ialloc_has_inode_record(  |