From: Darrick J. Wong <darrick.wong@xxxxxxxxxx> In process_longform_attr, we enforce that the root block of the attribute index must have both forw or back pointers set to zero. Unfortunately, the code that nulls out the pointers is not aware that the root block could be in da3 node format. This leads to corruption of da3 root node blocks because the functions that convert attr3 leaf headers to and from the ondisk structures perform some interpretation of firstused on what they think is an attr1 leaf block. Found by using xfs/402 to fuzz hdr.info.hdr.forw. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- repair/attr_repair.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/repair/attr_repair.c b/repair/attr_repair.c index 9a44f610..9d2a40f7 100644 --- a/repair/attr_repair.c +++ b/repair/attr_repair.c @@ -952,6 +952,33 @@ _("wrong FS UUID, inode %" PRIu64 " attr block %" PRIu64 "\n"), return 0; } +/* + * Zap the forw/back links in an attribute block. Be careful, because the + * root block could be an attr leaf block or a da node block. + */ +static inline void +clear_attr_forw_back( + struct xfs_buf *bp, + struct xfs_attr3_icleaf_hdr *leafhdr) +{ + struct xfs_mount *mp = bp->b_mount; + + if (leafhdr->magic == XFS_DA_NODE_MAGIC || + leafhdr->magic == XFS_DA3_NODE_MAGIC) { + struct xfs_da3_icnode_hdr da3_hdr; + + xfs_da3_node_hdr_from_disk(mp, &da3_hdr, bp->b_addr); + da3_hdr.forw = 0; + da3_hdr.back = 0; + xfs_da3_node_hdr_to_disk(mp, bp->b_addr, &da3_hdr); + return; + } + + leafhdr->forw = 0; + leafhdr->back = 0; + xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, bp->b_addr, leafhdr); +} + /* * Start processing for a leaf or fuller btree. * A leaf directory is one where the attribute fork is too big for @@ -1028,10 +1055,7 @@ process_longform_attr( _("clearing forw/back pointers in block 0 for attributes in inode %" PRIu64 "\n"), ino); repairlinks = 1; - leafhdr.forw = 0; - leafhdr.back = 0; - xfs_attr3_leaf_hdr_to_disk(mp->m_attr_geo, - leaf, &leafhdr); + clear_attr_forw_back(bp, &leafhdr); } else { do_warn( _("would clear forw/back pointers in block 0 for attributes in inode %" PRIu64 "\n"), ino);