Skip to content

Commit da062d1

Browse files
author
Darrick J. Wong
committed
xfs: check for sparse inode clusters that cross new EOAG when shrinking
While running xfs/168, I noticed occasional write verifier shutdowns involving inodes at the very end of the filesystem. Existing inode btree validation code checks that all inode clusters are fully contained within the filesystem. However, due to inadequate checking in the fs shrink code, it's possible that there could be a sparse inode cluster at the end of the filesystem where the upper inodes of the cluster are marked as holes and the corresponding blocks are free. In this case, the last blocks in the AG are listed in the bnobt. This enables the shrink to proceed but results in a filesystem that trips the inode verifiers. Fix this by disallowing the shrink. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Gao Xiang <[email protected]>
1 parent e73f0f0 commit da062d1

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

fs/xfs/libxfs/xfs_ag.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,14 @@ xfs_ag_shrink_space(
803803

804804
args.fsbno = XFS_AGB_TO_FSB(mp, agno, aglen - delta);
805805

806+
/*
807+
* Make sure that the last inode cluster cannot overlap with the new
808+
* end of the AG, even if it's sparse.
809+
*/
810+
error = xfs_ialloc_check_shrink(*tpp, agno, agibp, aglen - delta);
811+
if (error)
812+
return error;
813+
806814
/*
807815
* Disable perag reservations so it doesn't cause the allocation request
808816
* to fail. We'll reestablish reservation before we return.

fs/xfs/libxfs/xfs_ialloc.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2928,3 +2928,58 @@ xfs_ialloc_calc_rootino(
29282928

29292929
return XFS_AGINO_TO_INO(mp, 0, XFS_AGB_TO_AGINO(mp, first_bno));
29302930
}
2931+
2932+
/*
2933+
* Ensure there are not sparse inode clusters that cross the new EOAG.
2934+
*
2935+
* This is a no-op for non-spinode filesystems since clusters are always fully
2936+
* allocated and checking the bnobt suffices. However, a spinode filesystem
2937+
* could have a record where the upper inodes are free blocks. If those blocks
2938+
* were removed from the filesystem, the inode record would extend beyond EOAG,
2939+
* which will be flagged as corruption.
2940+
*/
2941+
int
2942+
xfs_ialloc_check_shrink(
2943+
struct xfs_trans *tp,
2944+
xfs_agnumber_t agno,
2945+
struct xfs_buf *agibp,
2946+
xfs_agblock_t new_length)
2947+
{
2948+
struct xfs_inobt_rec_incore rec;
2949+
struct xfs_btree_cur *cur;
2950+
struct xfs_mount *mp = tp->t_mountp;
2951+
struct xfs_perag *pag;
2952+
xfs_agino_t agino = XFS_AGB_TO_AGINO(mp, new_length);
2953+
int has;
2954+
int error;
2955+
2956+
if (!xfs_sb_version_hassparseinodes(&mp->m_sb))
2957+
return 0;
2958+
2959+
pag = xfs_perag_get(mp, agno);
2960+
cur = xfs_inobt_init_cursor(mp, tp, agibp, pag, XFS_BTNUM_INO);
2961+
2962+
/* Look up the inobt record that would correspond to the new EOFS. */
2963+
error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &has);
2964+
if (error || !has)
2965+
goto out;
2966+
2967+
error = xfs_inobt_get_rec(cur, &rec, &has);
2968+
if (error)
2969+
goto out;
2970+
2971+
if (!has) {
2972+
error = -EFSCORRUPTED;
2973+
goto out;
2974+
}
2975+
2976+
/* If the record covers inodes that would be beyond EOFS, bail out. */
2977+
if (rec.ir_startino + XFS_INODES_PER_CHUNK > agino) {
2978+
error = -ENOSPC;
2979+
goto out;
2980+
}
2981+
out:
2982+
xfs_btree_del_cursor(cur, error);
2983+
xfs_perag_put(pag);
2984+
return error;
2985+
}

fs/xfs/libxfs/xfs_ialloc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,7 @@ int xfs_ialloc_cluster_alignment(struct xfs_mount *mp);
122122
void xfs_ialloc_setup_geometry(struct xfs_mount *mp);
123123
xfs_ino_t xfs_ialloc_calc_rootino(struct xfs_mount *mp, int sunit);
124124

125+
int xfs_ialloc_check_shrink(struct xfs_trans *tp, xfs_agnumber_t agno,
126+
struct xfs_buf *agibp, xfs_agblock_t new_length);
127+
125128
#endif /* __XFS_IALLOC_H__ */

0 commit comments

Comments
 (0)