Subject: [PATCH 15/17] Make pg_upgrade aware of encryption.

From: Antonin Houska <[email-masked]>
Date: Fri, 5 Jul 2019 16:24:02 +0200

This includes pg_resetwal utility becaue pg_upgrade calls it.
---
 src/bin/pg_resetwal/Makefile      | 10 +++++--
 src/bin/pg_resetwal/pg_resetwal.c | 51 +++++++++++++++++++++++++++++++++--
 src/bin/pg_upgrade/controldata.c  | 39 +++++++++++++++++++++++++++
 src/bin/pg_upgrade/dump.c         |  2 +-
 src/bin/pg_upgrade/exec.c         | 30 +++++++++++++++++++--
 src/bin/pg_upgrade/option.c       | 10 ++++++-
 src/bin/pg_upgrade/parallel.c     |  7 ++---
 src/bin/pg_upgrade/pg_upgrade.c   | 57 +++++++++++++++++++++++++++++++++------
 src/bin/pg_upgrade/pg_upgrade.h   |  9 ++++++-
 src/bin/pg_upgrade/server.c       | 39 +++++++++++++++++++++++++--
 src/fe_utils/encryption.c         | 25 ++++++++++-------
 src/include/fe_utils/encryption.h |  1 +
 12 files changed, 249 insertions(+), 31 deletions(-)

diff --git a/src/bin/pg_resetwal/Makefile b/src/bin/pg_resetwal/Makefile
index 2a3835691f..faebfda27e 100644
--- a/src/bin/pg_resetwal/Makefile
+++ b/src/bin/pg_resetwal/Makefile
@@ -15,13 +15,19 @@ subdir = src/bin/pg_resetwal
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS= pg_resetwal.o $(WIN32RES)
+override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
+LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
+
+OBJS= pg_resetwal.o encryption.o $(WIN32RES)
 
 all: pg_resetwal
 
 pg_resetwal: $(OBJS) | submake-libpgport
 	$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
 
+encryption.c: % : $(top_srcdir)/src/backend/storage/file/%
+	rm -f $@ && $(LN_S) $< .
+
 install: all installdirs
 	$(INSTALL_PROGRAM) pg_resetwal$(X) '$(DESTDIR)$(bindir)/pg_resetwal$(X)'
 
@@ -32,7 +38,7 @@ uninstall:
 	rm -f '$(DESTDIR)$(bindir)/pg_resetwal$(X)'
 
 clean distclean maintainer-clean:
-	rm -f pg_resetwal$(X) $(OBJS)
+	rm -f pg_resetwal$(X) $(OBJS) encryption.c
 	rm -rf tmp_check
 
 check:
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index ff0f8ea5e7..43921132d4 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -54,11 +54,12 @@
 #include "common/file_perm.h"
 #include "common/logging.h"
 #include "common/restricted_token.h"
+#include "fe_utils/encryption.h"
+#include "storage/encryption.h"
 #include "storage/large_object.h"
 #include "pg_getopt.h"
 #include "getopt_long.h"
 
-
 static ControlFileData ControlFile; /* pg_control values */
 static XLogSegNo newXlogSegNo;	/* new XLOG segment # */
 static bool guessed = false;	/* T if we had to guess at any values */
@@ -135,7 +136,7 @@ main(int argc, char *argv[])
 	}

-	while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:O:x:", long_options, NULL)) != -1)
+	while ((c = getopt_long(argc, argv, "c:D:e:fK:l:m:no:O:x:", long_options, NULL)) != -1)
 	{
 		switch (c)
 		{
@@ -277,6 +278,12 @@ main(int argc, char *argv[])
 				}
 				break;
 
+#ifdef	USE_ENCRYPTION
+			case 'K':
+				encryption_key_command = strdup(optarg);
+				break;
+#endif							/* USE_ENCRYPTION */
+
 			case 'l':
 				if (strspn(optarg, "01234567890ABCDEFabcdef") != XLOG_FNAME_LEN)
 				{
@@ -408,6 +415,28 @@ main(int argc, char *argv[])
 		XLogFromFileName(log_fname, &minXlogTli, &minXlogSegNo, WalSegSz);
 
 	/*
+	 * If the data is encrypted, we also might need to encrypt the XLOG record
+	 * below.
+	 */
+	if (ControlFile.data_cipher > PG_CIPHER_NONE && !noupdate)
+	{
+		if (encryption_key_command)
+			run_encryption_key_command(encryption_key, DataDir);
+		else
+		{
+			/*
+			 * If executed by pg_upgrade, we don't want pg_resetwal to run the
+			 * encryption key command (possibly interactive application) w/o
+			 * access to terminal.
+			 */
+			read_encryption_key_fe(stdin);
+		}
+
+		setup_encryption();
+		data_encrypted = true;
+	}
+
+	/*
 	 * Also look at existing segment files to set up newXlogSegNo
 	 */
 	FindEndOfXLOG();
@@ -814,6 +843,12 @@ PrintControlValues(bool guessed)
 		   (ControlFile.float8ByVal ? _("by value") : _("by reference")));
 	printf(_("Data page checksum version:           %u\n"),
 		   ControlFile.data_checksum_version);
+	if (ControlFile.data_cipher > PG_CIPHER_NONE)
+		printf(_("Data encryption fingerprint:          %08X%08X%08X%08X\n"),
+			   htonl(((uint32 *) ControlFile.encryption_verification)[0]),
+			   htonl(((uint32 *) ControlFile.encryption_verification)[1]),
+			   htonl(((uint32 *) ControlFile.encryption_verification)[2]),
+			   htonl(((uint32 *) ControlFile.encryption_verification)[3]));
 }

@@ -1164,6 +1199,14 @@ WriteEmptyXLOG(void)
 	FIN_CRC32C(crc);
 	record->xl_crc = crc;
 
+	if (data_encrypted)
+	{
+		char		tweak[TWEAK_SIZE];
+
+		XLogEncryptionTweak(tweak, page->xlp_tli, newXlogSegNo, 0);
+		encrypt_block(buffer.data, buffer.data, XLOG_BLCKSZ, tweak, true);
+	}
+
 	/* Write the first page */
 	XLogFilePath(path, ControlFile.checkPointCopy.ThisTimeLineID,
 				 newXlogSegNo, WalSegSz);
@@ -1224,6 +1267,10 @@ usage(void)
 	printf(_(" [-D, --pgdata=]DATADIR          data directory\n"));
 	printf(_("  -e, --epoch=XIDEPOCH           set next transaction ID epoch\n"));
 	printf(_("  -f, --force                    force update to be done\n"));
+#ifdef	USE_ENCRYPTION
+	printf(_("  -K, --encryption-key-command\n"
+			 "					 command that returns encryption key\n"));
+#endif							/* USE_ENCRYPTION */
 	printf(_("  -l, --next-wal-file=WALFILE    set minimum starting location for new WAL\n"));
 	printf(_("  -m, --multixact-ids=MXID,MXID  set next and oldest multitransaction ID\n"));
 	printf(_("  -n, --dry-run                  no update, just show what would be done\n"));
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 38236415be..dc15b979bc 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -485,6 +485,33 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 			cluster->controldata.data_checksum_version = str2uint(p);
 			got_data_checksum_version = true;
 		}
+		else if ((p = strstr(bufin, "encryption fingerprint")) != NULL)
+		{
+			int			i;
+
+			p = strchr(p, ':');
+
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+			cluster->controldata.data_encrypted = true;
+
+			/* Skip the colon and any whitespace after it */
+			p = strchr(p, ':');
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+			p = strpbrk(p, "01234567890ABCDEF");
+			if (p == NULL || strlen(p) <= 1)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+			/* Make sure it looks like a valid finerprint */
+			if (strspn(p, "0123456789ABCDEF") != 32)
+				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+			for (i = 0; i < ENCRYPTION_SAMPLE_SIZE; i++)
+				sscanf(p + 2 * i, "%2hhx",
+					   cluster->controldata.encryption_verification + i);
+		}
 	}
 
 	pclose(output);
@@ -669,6 +696,18 @@ check_control_data(ControlData *oldctrl,
 		pg_fatal("old cluster uses data checksums but the new one does not\n");
 	else if (oldctrl->data_checksum_version != newctrl->data_checksum_version)
 		pg_fatal("old and new cluster pg_controldata checksum versions do not match\n");
+
+	if (oldctrl->data_encrypted && !newctrl->data_encrypted)
+		pg_fatal("old cluster is encrypted, but the new one is not\n");
+	else if (!oldctrl->data_encrypted && newctrl->data_encrypted)
+		pg_fatal("old cluster is not encrypted, but the new one is\n");
+	else if (oldctrl->data_encrypted)
+	{
+		if (memcmp(oldctrl->encryption_verification,
+				   newctrl->encryption_verification,
+				   ENCRYPTION_SAMPLE_SIZE) != 0)
+			pg_fatal("encryption of the new cluster is not compatible with encryption of the old one\n");
+	}
 }

diff --git a/src/bin/pg_upgrade/dump.c b/src/bin/pg_upgrade/dump.c
index c1429fe4bf..6fcf9126a4 100644
--- a/src/bin/pg_upgrade/dump.c
+++ b/src/bin/pg_upgrade/dump.c
@@ -22,7 +22,7 @@ generate_old_dump(void)
 	prep_status("Creating dump of global objects");
 
 	/* run new pg_dumpall binary for globals */
-	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+	exec_prog(UTILITY_LOG_FILE, NULL, true, true, NULL,
 			  "\"%s/pg_dumpall\" %s --globals-only --quote-all-identifiers "
 			  "--binary-upgrade %s -f %s",
 			  new_cluster.bindir, cluster_conn_opts(&old_cluster),
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 0363309328..cd6c68bdd9 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -74,11 +74,17 @@ get_bin_version(ClusterInfo *cluster)
  * If the command fails, an error message is optionally written to the specified
  * log_file, and the program optionally exits.
  *
+ * If encryption_key is passed, popen() is used and the key is sent to stdin
+ * of the command.
+ *
  * The code requires it be called first from the primary thread on Windows.
+ *
+ * TODO Consolidate the use of popen() with Windows. Or use only popen()?
  */
 bool
 exec_prog(const char *log_file, const char *opt_log_file,
-		  bool report_error, bool exit_on_error, const char *fmt,...)
+		  bool report_error, bool exit_on_error, unsigned char *encryption_key,
+		  const char *fmt,...)
 {
 	int			result = 0;
 	int			written;
@@ -170,7 +176,27 @@ exec_prog(const char *log_file, const char *opt_log_file,
 	/* see comment above */
 	if (mainThreadId == GetCurrentThreadId())
 #endif
-		result = system(cmd);
+	{
+		if (!encryption_key)
+			result = system(cmd);
+		else
+		{
+			FILE	*fp;
+			int	i;
+
+			fp = popen(cmd, "w");
+			if (fp == NULL)
+				pg_fatal("Failed to execute \"%s\"\n", cmd);
+
+			/* Send the key. */
+			for (i = 0; i < ENCRYPTION_KEY_LENGTH; i++)
+				fprintf(fp, "%.2x", encryption_key[i]);
+			fputc('\n', fp);
+
+			if (pclose(fp))
+				pg_fatal("\"%s\" returned non-zero code\n", cmd);
+		}
+	}
 
 	if (result != 0 && report_error)
 	{
diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c
index 73f395f2a3..e2bb0e7c8e 100644
--- a/src/bin/pg_upgrade/option.c
+++ b/src/bin/pg_upgrade/option.c
@@ -56,6 +56,7 @@ parseCommandLine(int argc, char *argv[])
 		{"socketdir", required_argument, NULL, 's'},
 		{"verbose", no_argument, NULL, 'v'},
 		{"clone", no_argument, NULL, 1},
+		{"encryption-key-command", required_argument, NULL, 'K'},
 
 		{NULL, 0, NULL, 0}
 	};
@@ -101,7 +102,7 @@ parseCommandLine(int argc, char *argv[])
 	if (os_user_effective_id == 0)
 		pg_fatal("%s: cannot be run as root\n", os_info.progname);
 
-	while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rs:U:v",
+	while ((option = getopt_long(argc, argv, "d:D:b:B:cj:K:ko:O:p:P:rs:U:v",
 								 long_options, &optindex)) != -1)
 	{
 		switch (option)
@@ -209,6 +210,10 @@ parseCommandLine(int argc, char *argv[])
 				user_opts.transfer_mode = TRANSFER_MODE_CLONE;
 				break;
 
+			case 'K':
+				encryption_key_command = pg_strdup(optarg);
+				break;
+
 			default:
 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
 						os_info.progname);
@@ -309,6 +314,9 @@ usage(void)
 	printf(_("  -v, --verbose                 enable verbose internal logging\n"));
 	printf(_("  -V, --version                 display version information, then exit\n"));
 	printf(_("  --clone                       clone instead of copying files to new cluster\n"));
+#ifdef	USE_ENCRYPTION
+	printf(_("  -K, --encryption-key-command  command that returns encryption key\n\n"));
+#endif							/* USE_ENCRYPTION */
 	printf(_("  -?, --help                    show this help, then exit\n"));
 	printf(_("\n"
 			 "Before running pg_upgrade you must:\n"
diff --git a/src/bin/pg_upgrade/parallel.c b/src/bin/pg_upgrade/parallel.c
index 80ab1b8609..be3fd51f15 100644
--- a/src/bin/pg_upgrade/parallel.c
+++ b/src/bin/pg_upgrade/parallel.c
@@ -79,7 +79,7 @@ parallel_exec_prog(const char *log_file, const char *opt_log_file,
 
 	if (user_opts.jobs <= 1)
 		/* exit_on_error must be true to allow jobs */
-		exec_prog(log_file, opt_log_file, true, true, "%s", cmd);
+		exec_prog(log_file, opt_log_file, true, true, NULL, "%s", cmd);
 	else
 	{
 		/* parallel */
@@ -122,7 +122,8 @@ parallel_exec_prog(const char *log_file, const char *opt_log_file,
 		child = fork();
 		if (child == 0)
 			/* use _exit to skip atexit() functions */
-			_exit(!exec_prog(log_file, opt_log_file, true, true, "%s", cmd));
+			_exit(!exec_prog(log_file, opt_log_file, true, true, NULL, "%s",
+							 cmd));
 		else if (child < 0)
 			/* fork failed */
 			pg_fatal("could not create worker process: %s\n", strerror(errno));
@@ -160,7 +161,7 @@ win32_exec_prog(exec_thread_arg *args)
 {
 	int			ret;
 
-	ret = !exec_prog(args->log_file, args->opt_log_file, true, true, "%s", args->cmd);
+	ret = !exec_prog(args->log_file, args->opt_log_file, true, true, NULL, "%s", args->cmd);
 
 	/* terminates thread */
 	return ret;
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 336df7378f..c464f90cf9 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -73,6 +73,12 @@ char	   *output_files[] = {
 	NULL
 };
 
+/*
+ * Declare these locally so we don't have to link storage/file/encryption.c
+ * here.
+ */
+bool		encryption_setup_done = false;
+unsigned char encryption_key[ENCRYPTION_KEY_LENGTH];
 
 int
 main(int argc, char **argv)
@@ -94,6 +100,20 @@ main(int argc, char **argv)
 	adjust_data_dir(&old_cluster);
 	adjust_data_dir(&new_cluster);
 
+	/*
+	 * The encryption key is needed to start the clusters.
+	 */
+	if (encryption_key_command)
+	{
+		/*
+		 * Both clusters should have the same KDF parameters, so we can pass
+		 * pgdata of any one.
+		 */
+		run_encryption_key_command(encryption_key, old_cluster.pgdata);
+
+		encryption_setup_done = true;
+	}
+
 	setup(argv[0], &live_check);
 
 	output_check_banner(live_check);
@@ -139,6 +159,16 @@ main(int argc, char **argv)
 
 	copy_xact_xlog_xid();
 
+	if (encryption_setup_done)
+	{
+		/*
+		 * Copy KDF file so that the old cluster encryption password works for
+		 * the new cluster.
+		 */
+		read_kdf_file(old_cluster.pgdata);
+		write_kdf_file(new_cluster.pgdata);
+	}
+
 	/* New now using xids of the old system */
 
 	/* -- NEW -- */
@@ -170,13 +200,15 @@ main(int argc, char **argv)
 	 */
 	prep_status("Setting next OID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+			  encryption_setup_done ? encryption_key : NULL,
 			  "\"%s/pg_resetwal\" -o %u \"%s\"",
-			  new_cluster.bindir, old_cluster.controldata.chkpnt_nxtoid,
+			  new_cluster.bindir,
+			  old_cluster.controldata.chkpnt_nxtoid,
 			  new_cluster.pgdata);
 	check_ok();
 
 	prep_status("Sync data directory to disk");
-	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+	exec_prog(UTILITY_LOG_FILE, NULL, true, true, NULL,
 			  "\"%s/initdb\" --sync-only \"%s\"", new_cluster.bindir,
 			  new_cluster.pgdata);
 	check_ok();
@@ -270,7 +302,7 @@ prepare_new_cluster(void)
 	 * --analyze so autovacuum doesn't update statistics later
 	 */
 	prep_status("Analyzing all rows in the new cluster");
-	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+	exec_prog(UTILITY_LOG_FILE, NULL, true, true, NULL,
 			  "\"%s/vacuumdb\" %s --all --analyze %s",
 			  new_cluster.bindir, cluster_conn_opts(&new_cluster),
 			  log_opts.verbose ? "--verbose" : "");
@@ -283,7 +315,7 @@ prepare_new_cluster(void)
 	 * counter later.
 	 */
 	prep_status("Freezing all rows in the new cluster");
-	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+	exec_prog(UTILITY_LOG_FILE, NULL, true, true, NULL,
 			  "\"%s/vacuumdb\" %s --all --freeze %s",
 			  new_cluster.bindir, cluster_conn_opts(&new_cluster),
 			  log_opts.verbose ? "--verbose" : "");
@@ -304,7 +336,7 @@ prepare_new_globals(void)
 	 */
 	prep_status("Restoring global objects in the new cluster");
 
-	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+	exec_prog(UTILITY_LOG_FILE, NULL, true, true, NULL,
 			  "\"%s/psql\" " EXEC_PSQL_ARGS " %s -f \"%s\"",
 			  new_cluster.bindir, cluster_conn_opts(&new_cluster),
 			  GLOBALS_DUMP_FILE);
@@ -351,6 +383,7 @@ create_new_objects(void)
 				  NULL,
 				  true,
 				  true,
+				  NULL,
 				  "\"%s/pg_restore\" %s %s --exit-on-error --verbose "
 				  "--dbname postgres \"%s\"",
 				  new_cluster.bindir,
@@ -448,7 +481,7 @@ copy_subdir_files(const char *old_subdir, const char *new_subdir)
 
 	prep_status("Copying old %s to new server", old_subdir);
 
-	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+	exec_prog(UTILITY_LOG_FILE, NULL, true, true, NULL,
 #ifndef WIN32
 			  "cp -Rf \"%s\" \"%s\"",
 #else
@@ -475,15 +508,20 @@ copy_xact_xlog_xid(void)
 	/* set the next transaction id and epoch of the new cluster */
 	prep_status("Setting next transaction ID and epoch for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+			  encryption_setup_done ? encryption_key : NULL,
 			  "\"%s/pg_resetwal\" -f -x %u \"%s\"",
-			  new_cluster.bindir, old_cluster.controldata.chkpnt_nxtxid,
+			  new_cluster.bindir,
+			  old_cluster.controldata.chkpnt_nxtxid,
 			  new_cluster.pgdata);
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+			  encryption_setup_done ? encryption_key : NULL,
 			  "\"%s/pg_resetwal\" -f -e %u \"%s\"",
-			  new_cluster.bindir, old_cluster.controldata.chkpnt_nxtepoch,
+			  new_cluster.bindir,
+			  old_cluster.controldata.chkpnt_nxtepoch,
 			  new_cluster.pgdata);
 	/* must reset commit timestamp limits also */
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+			  encryption_setup_done ? encryption_key : NULL,
 			  "\"%s/pg_resetwal\" -f -c %u,%u \"%s\"",
 			  new_cluster.bindir,
 			  old_cluster.controldata.chkpnt_nxtxid,
@@ -510,6 +548,7 @@ copy_xact_xlog_xid(void)
 		 * counters here and the oldest multi present on system.
 		 */
 		exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+				  encryption_setup_done ? encryption_key : NULL,
 				  "\"%s/pg_resetwal\" -O %u -m %u,%u \"%s\"",
 				  new_cluster.bindir,
 				  old_cluster.controldata.chkpnt_nxtmxoff,
@@ -538,6 +577,7 @@ copy_xact_xlog_xid(void)
 		 * next=MaxMultiXactId, but multixact.c can cope with that just fine.
 		 */
 		exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+				  encryption_setup_done ? encryption_key : NULL,
 				  "\"%s/pg_resetwal\" -m %u,%u \"%s\"",
 				  new_cluster.bindir,
 				  old_cluster.controldata.chkpnt_nxtmulti + 1,
@@ -549,6 +589,7 @@ copy_xact_xlog_xid(void)
 	/* now reset the wal archives in the new cluster */
 	prep_status("Resetting WAL archives");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
+			  encryption_setup_done ? encryption_key : NULL,
 	/* use timeline 1 to match controldata and no WAL history file */
 			  "\"%s/pg_resetwal\" -l 00000001%s \"%s\"", new_cluster.bindir,
 			  old_cluster.controldata.nextxlogfile + 8,
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 5d31750d86..e89f6c08a0 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -10,7 +10,10 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 
+#include "fe_utils/encryption.h"
 #include "libpq-fe.h"
+#include "storage/relfilenode.h"
+#include "storage/encryption.h"
 
 /* Use port in the private/dynamic port number range */
 #define DEF_PGUPORT			50432
@@ -227,6 +230,8 @@ typedef struct
 	bool		date_is_int;
 	bool		float8_pass_by_value;
 	bool		data_checksum_version;
+	bool		data_encrypted;
+	uint8		encryption_verification[ENCRYPTION_SAMPLE_SIZE];
 } ControlData;
 
 /*
@@ -367,7 +372,9 @@ void		generate_old_dump(void);
 #define EXEC_PSQL_ARGS "--echo-queries --set ON_ERROR_STOP=on --no-psqlrc --dbname=template1"
 
 bool		exec_prog(const char *log_file, const char *opt_log_file,
-					  bool report_error, bool exit_on_error, const char *fmt,...) pg_attribute_printf(5, 6);
+					  bool report_error, bool exit_on_error,
+					  unsigned char *encryption_key, const char *fmt,...)
+	pg_attribute_printf(6, 7);
 void		verify_directories(void);
 bool		pid_lock_file_exists(const char *datadir);
 
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 6ad4b14e16..93cb518cc5 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -252,6 +252,41 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 			 cluster->pgopts ? cluster->pgopts : "", socket_string);
 
 	/*
+	 * If encryption key needs to be sent, run a separate process now and let
+	 * it send the password to the postmaster.  We cannot send the key later
+	 * in the current process because the exec_prog call below blocks until
+	 * the postmaster succeeds or fails to start (and it will definitely fail
+	 * if it receives no key).
+	 */
+	if (encryption_setup_done)
+	{
+#ifndef WIN32
+		pid_t sender;
+
+		sender = fork();
+		if (sender == 0)
+		{
+			char	port_str[6];
+
+			snprintf(port_str, sizeof(port_str), "%d", cluster->port);
+
+			/* in child process */
+			send_key_to_postmaster(cluster->sockdir, port_str,
+								   encryption_key);
+			exit(EXIT_SUCCESS);
+		}
+		else if (sender < 0)
+		{
+			pg_fatal("could not create key sender process");
+			exit(EXIT_FAILURE);
+		}
+#else
+		/* TODO  */
+		#error "W32 not implemented yet"
+#endif
+	}
+
+	/*
 	 * Don't throw an error right away, let connecting throw the error because
 	 * it might supply a reason for the failure.
 	 */
@@ -260,7 +295,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 							  (strcmp(SERVER_LOG_FILE,
 									  SERVER_START_LOG_FILE) != 0) ?
 							  SERVER_LOG_FILE : NULL,
-							  report_and_exit_on_error, false,
+							  report_and_exit_on_error, false, NULL,
 							  "%s", cmd);
 
 	/* Did it fail and we are just testing if the server could be started? */
@@ -336,7 +371,7 @@ stop_postmaster(bool in_atexit)
 	else
 		return;					/* no cluster running */
 
-	exec_prog(SERVER_STOP_LOG_FILE, NULL, !in_atexit, !in_atexit,
+	exec_prog(SERVER_STOP_LOG_FILE, NULL, !in_atexit, !in_atexit, NULL,
 			  "\"%s/pg_ctl\" -w -D \"%s\" -o \"%s\" %s stop",
 			  cluster->bindir, cluster->pgconfig,
 			  cluster->pgopts ? cluster->pgopts : "",
diff --git a/src/fe_utils/encryption.c b/src/fe_utils/encryption.c
index 134b3bde9b..2b4a6fadad 100644
--- a/src/fe_utils/encryption.c
+++ b/src/fe_utils/encryption.c
@@ -259,8 +259,6 @@ run_encryption_key_command(unsigned char *encryption_key, char *data_dir)
 	FILE	   *fp;
 	char	cmd[MAXPGPATH];
 	char	*sp, *dp, *endp;
-	char	   *buf;
-	int		read_len, i, c;
 
 	Assert(encryption_key_command != NULL &&
 		   strlen(encryption_key_command) > 0);
@@ -312,15 +310,25 @@ run_encryption_key_command(unsigned char *encryption_key, char *data_dir)
 		exit(EXIT_FAILURE);
 	}
 
+	/* Read the key. */
+	read_encryption_key_fe(fp);
+
+	pclose(fp);
+}
+
+/*
+ * Frontend counterpart of read_encryption_key().
+ */
+void
+read_encryption_key_fe(FILE *f)
+{
+	char	   *buf;
+	int		read_len, i, c;
+
 	buf = (char *) palloc(ENCRYPTION_KEY_CHARS);
 
-	/*
-	 * Read the key. This is very similar to backend's read_encryption_key()
-	 * but there seems to be no straightforward way to call the function from
-	 * here.
-	 */
 	read_len = 0;
-	while ((c = fgetc(fp)) != EOF && c != '\n')
+	while ((c = fgetc(f)) != EOF && c != '\n')
 	{
 		if (read_len >= ENCRYPTION_KEY_CHARS)
 		{
@@ -349,7 +357,6 @@ run_encryption_key_command(unsigned char *encryption_key, char *data_dir)
 	}
 
 	pfree(buf);
-	pclose(fp);
 }
 
 /*
diff --git a/src/include/fe_utils/encryption.h b/src/include/fe_utils/encryption.h
index da302fe494..2c6032df07 100644
--- a/src/include/fe_utils/encryption.h
+++ b/src/include/fe_utils/encryption.h
@@ -22,5 +22,6 @@ extern void derive_key_from_password(unsigned char *encryption_key,
 									 const char *password, int len);
 extern void run_encryption_key_command(unsigned char *encryption_key,
 									   char *data_dir);
+extern void read_encryption_key_fe(FILE *f);
 extern bool send_key_to_postmaster(const char *host, const char *port,
 								   const unsigned char *encryption_Key);
-- 
2.13.7

--=-=-=
Content-Type: text/x-diff
Content-Disposition: attachment;
 filename=v04-0016-Teach-the-remaining-frontend-application-about-encry.patch