diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_ialloc.c')
| -rw-r--r-- | fs/xfs/libxfs/xfs_ialloc.c | 55 | 
1 files changed, 55 insertions, 0 deletions
| diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index 57d9cb632983..aaf8805a82df 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -2928,3 +2928,58 @@ xfs_ialloc_calc_rootino(  	return XFS_AGINO_TO_INO(mp, 0, XFS_AGB_TO_AGINO(mp, first_bno));  } + +/* + * Ensure there are not sparse inode clusters that cross the new EOAG. + * + * This is a no-op for non-spinode filesystems since clusters are always fully + * allocated and checking the bnobt suffices.  However, a spinode filesystem + * could have a record where the upper inodes are free blocks.  If those blocks + * were removed from the filesystem, the inode record would extend beyond EOAG, + * which will be flagged as corruption. + */ +int +xfs_ialloc_check_shrink( +	struct xfs_trans	*tp, +	xfs_agnumber_t		agno, +	struct xfs_buf		*agibp, +	xfs_agblock_t		new_length) +{ +	struct xfs_inobt_rec_incore rec; +	struct xfs_btree_cur	*cur; +	struct xfs_mount	*mp = tp->t_mountp; +	struct xfs_perag	*pag; +	xfs_agino_t		agino = XFS_AGB_TO_AGINO(mp, new_length); +	int			has; +	int			error; + +	if (!xfs_sb_version_hassparseinodes(&mp->m_sb)) +		return 0; + +	pag = xfs_perag_get(mp, agno); +	cur = xfs_inobt_init_cursor(mp, tp, agibp, pag, XFS_BTNUM_INO); + +	/* Look up the inobt record that would correspond to the new EOFS. */ +	error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &has); +	if (error || !has) +		goto out; + +	error = xfs_inobt_get_rec(cur, &rec, &has); +	if (error) +		goto out; + +	if (!has) { +		error = -EFSCORRUPTED; +		goto out; +	} + +	/* If the record covers inodes that would be beyond EOFS, bail out. */ +	if (rec.ir_startino + XFS_INODES_PER_CHUNK > agino) { +		error = -ENOSPC; +		goto out; +	} +out: +	xfs_btree_del_cursor(cur, error); +	xfs_perag_put(pag); +	return error; +} |