From 2a14aafc618a9878d45240c4f0c1f0c7dc8f3d6b Mon Sep 17 00:00:00 2001
From: "Darrick J. Wong" <djwong@kernel.org>
Date: Fri, 29 Sep 2023 09:53:40 +0000
Subject: [PATCH 05/11] xfs_repair: check the force-align flag

Make sure the flag isn't set incorrectly.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Signed-off-by: John Garry <john.g.garry@oracle.com>

---
 repair/dinode.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/repair/dinode.c b/repair/dinode.c
index f39ab2d..7747070 100644
--- a/repair/dinode.c
+++ b/repair/dinode.c
@@ -2229,6 +2229,69 @@ _("Bad extent size hint %u on inode %" PRIu64 ", "),
 	}
 }
 
+static void
+validate_forcealign(
+	struct xfs_mount	*mp,
+	struct xfs_dinode	*dino,
+	xfs_ino_t		lino,
+	int			*dirty)
+{
+	uint16_t		mode;
+	uint16_t		flags;
+	uint64_t		flags2;
+	
+	mode = be16_to_cpu(dino->di_mode);
+	flags = be16_to_cpu(dino->di_flags);
+	flags2 = be64_to_cpu(dino->di_flags2);
+
+	if (!(flags2 & XFS_DIFLAG2_FORCEALIGN))
+		return;
+
+	if (!xfs_sb_version_hasforcealign(&mp->m_sb)) {
+		do_warn(
+ _("Filesystem does not support forcealign flag set on inode %" PRIu64 ", "),
+					lino);
+		goto zap;
+	}
+
+	if (!S_ISDIR(mode) && !S_ISREG(mode)) {
+		do_warn(
+ _("Cannot have forcealign inode flag set on non-dir non-regular file inode %" PRIu64 "\n"),
+					lino);
+		goto zap;
+	}
+
+	if (flags & XFS_DIFLAG_REALTIME) {
+		do_warn(
+ _("Cannot have forcealign inode flag set on realtime inode %" PRIu64 "\n"),
+					lino);
+		goto zap;
+	}
+
+	if (dino->di_extsize == 0) {
+		do_warn(
+ _("Cannot have forcealign inode flag set without an extent size hint on inode %" PRIu64 "\n"),
+					lino);
+		goto zap;
+	}
+
+	if (dino->di_cowextsize != 0) {
+		do_warn(
+ _("Cannot have forcealign inode flag set with nonzero CoW extent size hint on inode %" PRIu64 "\n"),
+					lino);
+		goto zap;
+	}
+
+	return;
+zap:
+	if (!no_modify) {
+		do_warn(_("clearing flag\n"));
+		dino->di_flags2 &= ~cpu_to_be64(XFS_DIFLAG2_FORCEALIGN);
+		*dirty = 1;
+	} else
+		do_warn(_("would clear flag\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
@@ -2764,6 +2827,9 @@ _("Bad CoW extent size %u on inode %" PRIu64 ", "),
 			do_warn(_("would reset to zero\n"));
 	}
 
+	if (dino->di_version >= 3)
+		validate_forcealign(mp, dino, lino, dirty);
+
 	/* nsec fields cannot be larger than 1 billion */
 	check_nsec("atime", lino, dino, &dino->di_atime, dirty);
 	check_nsec("mtime", lino, dino, &dino->di_mtime, dirty);
-- 
2.33.0

