From 256d9dd3d44c71c5b729dd28be5c1b5e7126f118 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 24 Feb 2025 10:22:06 -0800 Subject: [PATCH] xfs_repair: validate CoW extent size hint on rtinherit directories XFS allows a sysadmin to change the rt extent size when adding a rt section to a filesystem after formatting. If there are any directories with both a cowextsize hint and rtinherit set, the hint could become misaligned with the new rextsize. Offer to fix the problem if we're in modify mode and the verifier didn't trip. If we're in dry run mode, we let the kernel fix it. Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig --- repair/dinode.c | 64 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/repair/dinode.c b/repair/dinode.c index 5a1c8e8c..8696a838 100644 --- a/repair/dinode.c +++ b/repair/dinode.c @@ -2961,6 +2961,47 @@ should_have_metadir_iflag( return false; } +static void +validate_cowextsize( + struct xfs_mount *mp, + struct xfs_dinode *dino, + xfs_ino_t lino, + int *dirty) +{ + uint16_t flags = be16_to_cpu(dino->di_flags); + uint64_t flags2 = be64_to_cpu(dino->di_flags2); + unsigned int value = be32_to_cpu(dino->di_cowextsize); + bool misaligned = false; + bool bad; + + /* + * XFS allows a sysadmin to change the rt extent size when adding a + * rt section to a filesystem after formatting. If there are any + * directories with both a cowextsize hint and rtinherit set, the + * hint could become misaligned with the new rextsize. + */ + if ((flags2 & XFS_DIFLAG2_COWEXTSIZE) && + (flags & XFS_DIFLAG_RTINHERIT) && + value % mp->m_sb.sb_rextsize > 0) + misaligned = true; + + /* Complain if the verifier fails. */ + bad = libxfs_inode_validate_cowextsize(mp, value, + be16_to_cpu(dino->di_mode), flags, flags2) != NULL; + if (bad || misaligned) { + do_warn( +_("Bad CoW extent size hint %u on inode %" PRIu64 ", "), + be32_to_cpu(dino->di_cowextsize), lino); + if (!no_modify) { + do_warn(_("resetting to zero\n")); + dino->di_flags2 &= ~cpu_to_be64(XFS_DIFLAG2_COWEXTSIZE); + dino->di_cowextsize = 0; + *dirty = 1; + } else + do_warn(_("would reset to zero\n")); + } +} + /* * returns 0 if the inode is ok, 1 if the inode is corrupt * check_dups can be set to 1 *only* when called by the @@ -3544,27 +3585,8 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"), validate_extsize(mp, dino, lino, dirty); - /* - * Only (regular files and directories) with COWEXTSIZE flags - * set can have extsize set. - */ - if (dino->di_version >= 3 && - libxfs_inode_validate_cowextsize(mp, - be32_to_cpu(dino->di_cowextsize), - be16_to_cpu(dino->di_mode), - be16_to_cpu(dino->di_flags), - be64_to_cpu(dino->di_flags2)) != NULL) { - do_warn( -_("Bad CoW extent size %u on inode %" PRIu64 ", "), - be32_to_cpu(dino->di_cowextsize), lino); - if (!no_modify) { - do_warn(_("resetting to zero\n")); - dino->di_flags2 &= ~cpu_to_be64(XFS_DIFLAG2_COWEXTSIZE); - dino->di_cowextsize = 0; - *dirty = 1; - } else - do_warn(_("would reset to zero\n")); - } + if (dino->di_version >= 3) + validate_cowextsize(mp, dino, lino, dirty); /* nsec fields cannot be larger than 1 billion */ check_nsec("atime", lino, dino, &dino->di_atime, dirty); -- 2.47.2