From 731c48d21b0adfa74e7ce1e05b2d8f1146b9e3cf Mon Sep 17 00:00:00 2001
From: Michael Banck <mbanck@debian.org>
Date: Wed, 13 Mar 2019 12:05:34 +0100
Subject: [PATCH] Guard against concurrent cluster changes when enabling
 checksums.

Re-read the control file after operating on all files in order to check whether
the instance is still shutdown and the control file still has the same
modification timestamp.

In passing, add a note about syncing the data directory in verbose mode.
---
 src/bin/pg_checksums/pg_checksums.c | 38 ++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 0e464299f3..afca6cf027 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -309,6 +309,7 @@ main(int argc, char *argv[])
 	int			c;
 	int			option_index;
 	bool		crc_ok;
+	pg_time_t	controlfile_last_updated;
 
 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_checksums"));
 
@@ -399,7 +400,7 @@ main(int argc, char *argv[])
 		exit(1);
 	}
 
-	/* Check if cluster is running */
+	/* Get control file data */
 	ControlFile = get_controlfile(DataDir, progname, &crc_ok);
 	if (!crc_ok)
 	{
@@ -414,6 +415,7 @@ main(int argc, char *argv[])
 		exit(1);
 	}
 
+	/* Check if cluster is running */
 	if (ControlFile->state != DB_SHUTDOWNED &&
 		ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
 	{
@@ -440,6 +442,9 @@ main(int argc, char *argv[])
 		exit(1);
 	}
 
+	/* Save time of last control file modification */
+	controlfile_last_updated = ControlFile->time;
+
 	/* Operate on all files if checking or enabling checksums */
 	if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
 	{
@@ -461,17 +466,44 @@ main(int argc, char *argv[])
 	}
 
 	/*
-	 * Finally update the control file, flushing the data directory at the
-	 * end.
+	 * Finally update the control file after checking the cluster is still
+	 * offline and its control file has not changed, flushing the data
+	 * directory at the end.
 	 */
 	if (mode == PG_MODE_ENABLE || mode == PG_MODE_DISABLE)
 	{
+		ControlFile = get_controlfile(DataDir, progname, &crc_ok);
+		if (!crc_ok)
+		{
+			fprintf(stderr, _("%s: pg_control CRC value is incorrect\n"), progname);
+			exit(1);
+		}
+
+		/* Check if cluster is running */
+		if (ControlFile->state != DB_SHUTDOWNED &&
+		ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
+		{
+			fprintf(stderr, _("%s: cluster no longer shut down\n"), progname);
+			exit(1);
+		}
+
+		/* Check if control file has changed */
+		if (controlfile_last_updated != ControlFile->time)
+		{
+			fprintf(stderr, _("%s: control file has changed since startup\n"), progname);
+			exit(1);
+		}
+
 		/* Update control file */
 		ControlFile->data_checksum_version =
 			(mode == PG_MODE_ENABLE) ? PG_DATA_CHECKSUM_VERSION : 0;
 		update_controlfile(DataDir, progname, ControlFile);
 		if (do_sync)
+		{
+			if (verbose && mode == PG_MODE_ENABLE)
+				printf(_("Syncing data directory\n"));
 			fsync_pgdata(DataDir, progname, PG_VERSION_NUM);
+		}
 		if (verbose)
 			printf(_("Data checksum version: %d\n"), ControlFile->data_checksum_version);
 		if (mode == PG_MODE_ENABLE)
-- 
2.11.0

