From 15ea2196d7d9457e0cefe626c410fe88a835bbba Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Tue, 6 Apr 2021 14:23:41 -0400 Subject: [PATCH] cfe-07-bin_over_cfe-06-backend squash commit --- doc/src/sgml/ref/initdb.sgml | 47 +++++++++ doc/src/sgml/ref/pg_ctl-ref.sgml | 14 +++ doc/src/sgml/ref/pgupgrade.sgml | 20 +++- doc/src/sgml/ref/postgres-ref.sgml | 13 +++ src/bin/initdb/initdb.c | 126 ++++++++++++++++++++++-- src/bin/pg_controldata/pg_controldata.c | 3 + src/bin/pg_ctl/pg_ctl.c | 59 +++++++++-- src/bin/pg_resetwal/pg_resetwal.c | 3 + src/bin/pg_rewind/filemap.c | 8 ++ src/bin/pg_upgrade/check.c | 34 +++++++ src/bin/pg_upgrade/controldata.c | 41 +++++++- src/bin/pg_upgrade/file.c | 2 + src/bin/pg_upgrade/option.c | 7 +- src/bin/pg_upgrade/pg_upgrade.h | 3 + src/bin/pg_upgrade/server.c | 5 +- 15 files changed, 362 insertions(+), 23 deletions(-) diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml index afd344b4c0..236d213f14 100644 --- a/doc/src/sgml/ref/initdb.sgml +++ b/doc/src/sgml/ref/initdb.sgml @@ -163,6 +163,18 @@ PostgreSQL documentation + + + + + + This option specifies an external command to obtain the cluster-level + key for cluster file encryption during server initialization and + server start; see for details. + + + + @@ -224,6 +236,18 @@ PostgreSQL documentation + + + + + + Specifies the cluster file encryption method. The + value values are AES128 (the default), AES192, and AES256. + + + + @@ -299,6 +323,17 @@ PostgreSQL documentation + + + + + + Allows the command + to prompt for a passphrase or PIN. + + + + @@ -321,6 +356,18 @@ PostgreSQL documentation + + + + + + Copies cluster file encryption keys from another cluster; required + when using pg_upgrade on a cluster + with cluster file encryption enabled. + + + + diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml index 3946fa52ea..0662ae051a 100644 --- a/doc/src/sgml/ref/pg_ctl-ref.sgml +++ b/doc/src/sgml/ref/pg_ctl-ref.sgml @@ -38,6 +38,7 @@ PostgreSQL documentation options path + @@ -72,6 +73,7 @@ PostgreSQL documentation seconds options + @@ -373,6 +375,18 @@ PostgreSQL documentation + + + + + + Allows or + to prompt for a passphrase + or PIN. + + + + diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml index 92e1d09a55..9b49e8da59 100644 --- a/doc/src/sgml/ref/pgupgrade.sgml +++ b/doc/src/sgml/ref/pgupgrade.sgml @@ -167,6 +167,15 @@ PostgreSQL documentation + + + + allows or + to prompt for a passphrase + or PIN. + + + dir dir @@ -309,7 +318,9 @@ make prefix=/usr/local/pgsql.new install Again, use compatible initdb flags that match the old cluster. Many prebuilt installers do this step automatically. There is no need to - start the new cluster. + start the new cluster. If upgrading a cluster that uses + cluster file encryption, the initdb option + must be specified. @@ -838,6 +849,13 @@ psql --username=postgres --file=script.sql postgres is down. + + If the old cluster uses file encryption, the new cluster must use + the same keys, so pg_upgrade copies them to the + new cluster. It is necessary to initialize the new cluster with + the same cluster_key_command and the same + file encryption method. + diff --git a/doc/src/sgml/ref/postgres-ref.sgml b/doc/src/sgml/ref/postgres-ref.sgml index 4aaa7abe1a..805da81e07 100644 --- a/doc/src/sgml/ref/postgres-ref.sgml +++ b/doc/src/sgml/ref/postgres-ref.sgml @@ -297,6 +297,19 @@ PostgreSQL documentation + + + + + Makes postgres prompt for a passphrase or PIN + from the specified open numeric file descriptor. The descriptor + is closed after the key is read. The file descriptor number + -1 duplicates standard error for the terminal; + this is useful for single-user mode. + + + + diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 4500df6fc8..cb8a843f2b 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -65,6 +65,7 @@ #include "catalog/pg_collation_d.h" #include "common/file_perm.h" #include "common/file_utils.h" +#include "common/kmgr_utils.h" #include "common/logging.h" #include "common/restricted_token.h" #include "common/string.h" @@ -142,11 +143,16 @@ static bool noclean = false; static bool noinstructions = false; static bool do_sync = true; static bool sync_only = false; +static bool pass_terminal_fd = false; +static char *term_fd_opt = NULL; +static int file_encryption_method = DISABLED_ENCRYPTION_METHOD; static bool show_setting = false; static bool data_checksums = false; static char *xlog_dir = NULL; static char *str_wal_segment_size_mb = NULL; static int wal_segment_size_mb; +static char *cluster_key_cmd = NULL; +static char *old_key_datadir = NULL; /* internal vars */ @@ -205,6 +211,7 @@ static const char *const subdirs[] = { "global", "pg_wal/archive_status", "pg_commit_ts", + "pg_cryptokeys", "pg_dynshmem", "pg_notify", "pg_serial", @@ -961,12 +968,13 @@ test_config_settings(void) test_buffs = MIN_BUFS_FOR_CONNS(test_conns); snprintf(cmd, sizeof(cmd), - "\"%s\" --boot -x0 %s " + "\"%s\" --boot -x0 %s %s " "-c max_connections=%d " "-c shared_buffers=%d " "-c dynamic_shared_memory_type=%s " "< \"%s\" > \"%s\" 2>&1", backend_exec, boot_options, + term_fd_opt ? term_fd_opt : "", test_conns, test_buffs, dynamic_shared_memory_type, DEVNULL, DEVNULL); @@ -997,12 +1005,13 @@ test_config_settings(void) } snprintf(cmd, sizeof(cmd), - "\"%s\" --boot -x0 %s " + "\"%s\" --boot -x0 %s %s " "-c max_connections=%d " "-c shared_buffers=%d " "-c dynamic_shared_memory_type=%s " "< \"%s\" > \"%s\" 2>&1", backend_exec, boot_options, + term_fd_opt ? term_fd_opt : "", n_connections, test_buffs, dynamic_shared_memory_type, DEVNULL, DEVNULL); @@ -1192,6 +1201,13 @@ setup_config(void) "password_encryption = md5"); } + if (cluster_key_cmd) + { + snprintf(repltok, sizeof(repltok), "cluster_key_command = '%s'", + escape_quotes(cluster_key_cmd)); + conflines = replace_token(conflines, "#cluster_key_command = ''", repltok); + } + /* * If group access has been enabled for the cluster then it makes sense to * ensure that the log files also allow group access. Otherwise a backup @@ -1402,12 +1418,17 @@ bootstrap_template1(void) unsetenv("PGCLIENTENCODING"); snprintf(cmd, sizeof(cmd), - "\"%s\" --boot -x1 -X %u %s %s %s", + "\"%s\" --boot -x1 -X %u %s %s %s %s %s %s %s %s", backend_exec, wal_segment_size_mb * (1024 * 1024), data_checksums ? "-k" : "", + cluster_key_cmd ? "-K" : "", + cluster_key_cmd ? encryption_methods[file_encryption_method].name : "", + old_key_datadir ? "-u" : "", + old_key_datadir ? old_key_datadir : "", boot_options, - debug ? "-d 5" : ""); + debug ? "-d 5" : "", + term_fd_opt ? term_fd_opt : ""); PG_CMD_OPEN; @@ -2254,21 +2275,29 @@ usage(const char *progname) " set default locale in the respective category for\n" " new databases (default taken from environment)\n")); printf(_(" --no-locale equivalent to --locale=C\n")); - printf(_(" --pwfile=FILE read password for the new superuser from file\n")); + printf(_(" --pwfile=FILE read the new superuser password from file\n")); printf(_(" -T, --text-search-config=CFG\n" " default text search configuration\n")); printf(_(" -U, --username=NAME database superuser name\n")); - printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); + printf(_(" -W, --pwprompt prompt for the new superuser password\n")); printf(_(" -X, --waldir=WALDIR location for the write-ahead log directory\n")); printf(_(" --wal-segsize=SIZE size of WAL segments, in megabytes\n")); printf(_("\nLess commonly used options:\n")); + printf(_(" -c, --cluster-key-command=COMMAND\n" + " enable cluster file encryption and set command\n" + " to obtain the cluster key\n")); printf(_(" -d, --debug generate lots of debugging output\n")); + printf(_(" -K, --file-encryption-method=METHOD\n" + " cluster file encryption method\n")); printf(_(" -L DIRECTORY where to find the input files\n")); printf(_(" -n, --no-clean do not clean up after errors\n")); printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n")); + printf(_(" -R, --authprompt prompt for a passphrase or PIN\n")); printf(_(" --no-instructions do not print instructions for next steps\n")); printf(_(" -s, --show show internal settings\n")); printf(_(" -S, --sync-only only sync data directory\n")); + printf(_(" -u, --copy-encryption-keys=DATADIR\n" + " copy the file encryption key from another cluster\n")); printf(_("\nOther options:\n")); printf(_(" -V, --version output version information, then exit\n")); printf(_(" -?, --help show this help, then exit\n")); @@ -2836,6 +2865,23 @@ initialize_data_directory(void) /* Top level PG_VERSION is checked by bootstrapper, so make it first */ write_version_file(NULL); + if (pass_terminal_fd) + { +#ifndef WIN32 + int terminal_fd = open("/dev/tty", O_RDWR, 0); +#else + int terminal_fd = open("CONOUT$", O_RDWR, 0); +#endif + + if (terminal_fd < 0) + { + pg_log_error(_("%s: could not open terminal: %s\n"), + progname, strerror(errno)); + exit(1); + } + term_fd_opt = psprintf("-R %d", terminal_fd); + } + /* Select suitable configuration settings */ set_null_conf(); test_config_settings(); @@ -2859,8 +2905,9 @@ initialize_data_directory(void) fflush(stdout); snprintf(cmd, sizeof(cmd), - "\"%s\" %s template1 >%s", + "\"%s\" %s %s template1 >%s", backend_exec, backend_options, + term_fd_opt ? term_fd_opt : "", DEVNULL); PG_CMD_OPEN; @@ -2936,7 +2983,11 @@ main(int argc, char *argv[]) {"waldir", required_argument, NULL, 'X'}, {"wal-segsize", required_argument, NULL, 12}, {"data-checksums", no_argument, NULL, 'k'}, + {"authprompt", no_argument, NULL, 'R'}, + {"file-encryption-method", required_argument, NULL, 'K'}, {"allow-group-access", no_argument, NULL, 'g'}, + {"cluster-key-command", required_argument, NULL, 'c'}, + {"copy-encryption-keys", required_argument, NULL, 'u'}, {NULL, 0, NULL, 0} }; @@ -2978,7 +3029,7 @@ main(int argc, char *argv[]) /* process command-line options */ - while ((c = getopt_long(argc, argv, "A:dD:E:gkL:nNsST:U:WX:", long_options, &option_index)) != -1) + while ((c = getopt_long(argc, argv, "A:c:dD:E:gkK:L:nNRsST:u:U:WX:", long_options, &option_index)) != -1) { switch (c) { @@ -3024,6 +3075,28 @@ main(int argc, char *argv[]) case 'N': do_sync = false; break; + case 'R': + pass_terminal_fd = true; + break; + case 'K': + { + int i; + + /* method 0/disabled cannot be specified */ + for (i = DISABLED_ENCRYPTION_METHOD + 1; + i < NUM_ENCRYPTION_METHODS; i++) + if (pg_strcasecmp(optarg, encryption_methods[i].name) == 0) + { + file_encryption_method = i; + break; + } + if (i == NUM_ENCRYPTION_METHODS) + { + fprintf(stderr, _("invalid cluster encryption method\n")); + exit(1); + } + } + break; case 'S': sync_only = true; break; @@ -3060,6 +3133,12 @@ main(int argc, char *argv[]) case 9: pwfilename = pg_strdup(optarg); break; + case 'c': + cluster_key_cmd = pg_strdup(optarg); + break; + case 'u': + old_key_datadir = pg_strdup(optarg); + break; case 's': show_setting = true; break; @@ -3133,6 +3212,32 @@ main(int argc, char *argv[]) exit(1); } +#ifndef USE_OPENSSL + if (cluster_key_cmd) + { + pg_log_error("cluster file encryption is not supported because OpenSSL is not supported by this build"); + exit(1); + } +#endif + + if (old_key_datadir != NULL && cluster_key_cmd == NULL) + { + pg_log_error("copying encryption keys requires the cluster key command to be specified"); + exit(1); + } + + if (file_encryption_method != DISABLED_ENCRYPTION_METHOD && + cluster_key_cmd == NULL) + { + pg_log_error("a file encryption method requires the cluster key command to be specified"); + exit(1); + } + + /* set the default */ + if (file_encryption_method == DISABLED_ENCRYPTION_METHOD && + cluster_key_cmd != NULL) + file_encryption_method = DEFAULT_ENABLED_ENCRYPTION_METHOD; + check_authmethod_unspecified(&authmethodlocal); check_authmethod_unspecified(&authmethodhost); @@ -3200,6 +3305,11 @@ main(int argc, char *argv[]) else printf(_("Data page checksums are disabled.\n")); + if (cluster_key_cmd) + printf(_("Cluster file encryption is enabled.\n")); + else + printf(_("Cluster file encryption is disabled.\n")); + if (pwprompt || pwfilename) get_su_pwd(); diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index f911f98d94..e95eaae5e5 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -25,6 +25,7 @@ #include "access/xlog_internal.h" #include "catalog/pg_control.h" #include "common/controldata_utils.h" +#include "common/kmgr_utils.h" #include "common/logging.h" #include "getopt_long.h" #include "pg_getopt.h" @@ -328,5 +329,7 @@ main(int argc, char *argv[]) ControlFile->data_checksum_version); printf(_("Mock authentication nonce: %s\n"), mock_auth_nonce_str); + printf(_("File encryption method: %s\n"), + encryption_methods[ControlFile->file_encryption_method].name); return 0; } diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 7985da0a94..8fd60cc78c 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -79,6 +79,7 @@ typedef enum static bool do_wait = true; static int wait_seconds = DEFAULT_WAIT; static bool wait_seconds_arg = false; +static bool pass_terminal_fd = false; static bool silent_mode = false; static ShutdownMode shutdown_mode = FAST_MODE; static int sig = SIGINT; /* default */ @@ -442,7 +443,7 @@ free_readfile(char **optlines) static pgpid_t start_postmaster(void) { - char cmd[MAXPGPATH]; + char cmd[MAXPGPATH], *term_fd_opt = NULL; #ifndef WIN32 pgpid_t pm_pid; @@ -467,6 +468,19 @@ start_postmaster(void) /* fork succeeded, in child */ + if (pass_terminal_fd) + { + int terminal_fd = open("/dev/tty", O_RDWR, 0); + + if (terminal_fd < 0) + { + write_stderr(_("%s: could not open terminal: %s\n"), + progname, strerror(errno)); + exit(1); + } + term_fd_opt = psprintf(" -R %d", terminal_fd); + } + /* * If possible, detach the postmaster process from the launching process * group and make it a group leader, so that it doesn't get signaled along @@ -487,12 +501,14 @@ start_postmaster(void) * has the same PID as the current child process. */ if (log_file != NULL) - snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s < \"%s\" >> \"%s\" 2>&1", + snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s%s < \"%s\" >> \"%s\" 2>&1", exec_path, pgdata_opt, post_opts, + term_fd_opt ? term_fd_opt : "", DEVNULL, log_file); else - snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s < \"%s\" 2>&1", - exec_path, pgdata_opt, post_opts, DEVNULL); + snprintf(cmd, MAXPGPATH, "exec \"%s\" %s%s%s < \"%s\" 2>&1", + exec_path, pgdata_opt, post_opts, + term_fd_opt ? term_fd_opt : "", DEVNULL); (void) execl("/bin/sh", "/bin/sh", "-c", cmd, (char *) NULL); @@ -513,6 +529,21 @@ start_postmaster(void) PROCESS_INFORMATION pi; const char *comspec; + if (pass_terminal_fd) + { + /* Hopefully we can read and write CONOUT, see simple_prompt() XXX */ + /* Do CreateRestrictedProcess() children even inherit open file descriptors? XXX */ + int terminal_fd = open("CONOUT$", O_RDWR, 0); + + if (terminal_fd < 0) + { + write_stderr(_("%s: could not open terminal: %s\n"), + progname, strerror(errno)); + exit(1); + } + term_fd_opt = psprintf(" -R %d", terminal_fd); + } + /* Find CMD.EXE location using COMSPEC, if it's set */ comspec = getenv("COMSPEC"); if (comspec == NULL) @@ -553,12 +584,14 @@ start_postmaster(void) else close(fd); - snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"", - comspec, exec_path, pgdata_opt, post_opts, DEVNULL, log_file); + snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s%s < \"%s\" >> \"%s\" 2>&1\"", + comspec, exec_path, pgdata_opt, post_opts, + term_fd_opt ? term_fd_opt : "", DEVNULL, log_file); } else - snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s < \"%s\" 2>&1\"", - comspec, exec_path, pgdata_opt, post_opts, DEVNULL); + snprintf(cmd, MAXPGPATH, "\"%s\" /C \"\"%s\" %s%s%s < \"%s\" 2>&1\"", + comspec, exec_path, pgdata_opt, post_opts, + term_fd_opt ? term_fd_opt : "", DEVNULL); if (!CreateRestrictedProcess(cmd, &pi, false)) { @@ -689,7 +722,8 @@ wait_for_postmaster(pgpid_t pm_pid, bool do_checkpoint) } else #endif - print_msg("."); + if (!pass_terminal_fd) + print_msg("."); } pg_usleep(USEC_PER_SEC / WAITS_PER_SEC); @@ -2065,6 +2099,7 @@ do_help(void) printf(_(" -o, --options=OPTIONS command line options to pass to postgres\n" " (PostgreSQL server executable) or initdb\n")); printf(_(" -p PATH-TO-POSTGRES normally not necessary\n")); + printf(_(" -R, --authprompt prompt for a passphrase or PIN\n")); printf(_("\nOptions for stop or restart:\n")); printf(_(" -m, --mode=MODE MODE can be \"smart\", \"fast\", or \"immediate\"\n")); @@ -2259,6 +2294,7 @@ main(int argc, char **argv) {"mode", required_argument, NULL, 'm'}, {"pgdata", required_argument, NULL, 'D'}, {"options", required_argument, NULL, 'o'}, + {"authprompt", no_argument, NULL, 'R'}, {"silent", no_argument, NULL, 's'}, {"timeout", required_argument, NULL, 't'}, {"core-files", no_argument, NULL, 'c'}, @@ -2331,7 +2367,7 @@ main(int argc, char **argv) /* process command-line options */ while (optind < argc) { - while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:sS:t:U:wW", + while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:RsS:t:U:wW", long_options, &option_index)) != -1) { switch (c) @@ -2383,6 +2419,9 @@ main(int argc, char **argv) case 'P': register_password = pg_strdup(optarg); break; + case 'R': + pass_terminal_fd = true; + break; case 's': silent_mode = true; break; diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index 805dafef07..682c0e26c3 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -52,6 +52,7 @@ #include "common/controldata_utils.h" #include "common/fe_memutils.h" #include "common/file_perm.h" +#include "common/kmgr_utils.h" #include "common/logging.h" #include "common/restricted_token.h" #include "common/string.h" @@ -804,6 +805,8 @@ PrintControlValues(bool guessed) (ControlFile.float8ByVal ? _("by value") : _("by reference"))); printf(_("Data page checksum version: %u\n"), ControlFile.data_checksum_version); + printf(_("File encryption method: %s\n"), + encryption_methods[ControlFile.file_encryption_method].name); } diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c index 2618b4c957..e52a914361 100644 --- a/src/bin/pg_rewind/filemap.c +++ b/src/bin/pg_rewind/filemap.c @@ -28,6 +28,7 @@ #include "catalog/pg_tablespace_d.h" #include "common/hashfn.h" +#include "common/kmgr_utils.h" #include "common/string.h" #include "datapagemap.h" #include "filemap.h" @@ -107,6 +108,13 @@ static const char *excludeDirContents[] = /* Contents removed on startup, see AsyncShmemInit(). */ "pg_notify", + /* + * Skip cryptographic keys. It's generally not a good idea to copy the + * cryptographic keys from source database because these might use + * different cluster key. + */ + KMGR_DIR, + /* * Old contents are loaded for possible debugging but are not required for * normal operation, see SerialInit(). diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c index d77183b8d1..b816fb952f 100644 --- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -10,6 +10,7 @@ #include "postgres_fe.h" #include "catalog/pg_authid_d.h" +#include "common/kmgr_utils.h" #include "fe_utils/string_utils.h" #include "mb/pg_wchar.h" #include "pg_upgrade.h" @@ -27,6 +28,7 @@ static void check_for_tables_with_oids(ClusterInfo *cluster); static void check_for_reg_data_type_usage(ClusterInfo *cluster); static void check_for_jsonb_9_4_usage(ClusterInfo *cluster); static void check_for_pg_role_prefix(ClusterInfo *cluster); +static void check_for_cluster_key_failure(ClusterInfo *cluster); static void check_for_new_tablespace_dir(ClusterInfo *new_cluster); static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster); static char *get_canonical_locale_name(int category, const char *locale); @@ -149,6 +151,9 @@ check_and_dump_old_cluster(bool live_check) if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905) check_for_pg_role_prefix(&old_cluster); + if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1400) + check_for_cluster_key_failure(&old_cluster); + if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 && old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER) check_for_jsonb_9_4_usage(&old_cluster); @@ -183,6 +188,9 @@ check_new_cluster(void) check_loadable_libraries(); + if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1400) + check_for_cluster_key_failure(&new_cluster); + switch (user_opts.transfer_mode) { case TRANSFER_MODE_CLONE: @@ -1364,6 +1372,32 @@ check_for_user_defined_encoding_conversions(ClusterInfo *cluster) } +/* + * check_for_cluster_key_failure() + * + * Make sure there was no unrepaired pg_alterckey failure + */ +static void +check_for_cluster_key_failure(ClusterInfo *cluster) +{ + struct stat buffer; + + if (stat (KMGR_DIR_PID, &buffer) == 0) + { + if (cluster == &old_cluster) + pg_fatal("The source cluster had a pg_alterckey failure that needs repair or\n" + "pg_alterckey is running. Run pg_alterckey --repair or wait for it\n" + "to complete.\n"); + else + pg_fatal("The target cluster had a pg_alterckey failure that needs repair or\n" + "pg_alterckey is running. Run pg_alterckey --repair or wait for it\n" + "to complete.\n"); + } + + check_ok(); +} + + /* * get_canonical_locale_name * diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c index 4f647cdf33..7deb040777 100644 --- a/src/bin/pg_upgrade/controldata.c +++ b/src/bin/pg_upgrade/controldata.c @@ -9,10 +9,16 @@ #include "postgres_fe.h" +#include #include #include "pg_upgrade.h" +#include "access/xlog_internal.h" +#include "common/controldata_utils.h" +#include "common/file_utils.h" +#include "common/kmgr_utils.h" + /* * get_control_data() * @@ -59,6 +65,7 @@ get_control_data(ClusterInfo *cluster, bool live_check) bool got_date_is_int = false; bool got_data_checksum_version = false; bool got_cluster_state = false; + int got_file_encryption_method = false; char *lc_collate = NULL; char *lc_ctype = NULL; char *lc_monetary = NULL; @@ -202,6 +209,13 @@ get_control_data(ClusterInfo *cluster, bool live_check) got_data_checksum_version = true; } + /* Only in <= 14 */ + if (GET_MAJOR_VERSION(cluster->major_version) <= 1400) + { + cluster->controldata.file_encryption_method = DISABLED_ENCRYPTION_METHOD; + got_file_encryption_method = true; + } + /* we have the result of cmd in "output". so parse it line by line now */ while (fgets(bufin, sizeof(bufin), output)) { @@ -485,6 +499,18 @@ 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, "Cluster file encryption method:")) != NULL) + { + p = strchr(p, ':'); + + if (p == NULL || strlen(p) <= 1) + pg_fatal("%d: controldata retrieval problem\n", __LINE__); + + p++; /* remove ':' char */ + /* used later for contrib check */ + cluster->controldata.file_encryption_method = atoi(p); + got_file_encryption_method = true; + } } pclose(output); @@ -553,7 +579,8 @@ get_control_data(ClusterInfo *cluster, bool live_check) !got_index || !got_toast || (!got_large_object && cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) || - !got_date_is_int || !got_data_checksum_version) + !got_date_is_int || !got_data_checksum_version || + !got_file_encryption_method) { if (cluster == &old_cluster) pg_log(PG_REPORT, @@ -619,6 +646,10 @@ get_control_data(ClusterInfo *cluster, bool live_check) if (!got_data_checksum_version) pg_log(PG_REPORT, " data checksum version\n"); + /* value added in Postgres 14 */ + if (!got_file_encryption_method) + pg_log(PG_REPORT, " file encryption method\n"); + pg_fatal("Cannot continue without required control information, terminating\n"); } } @@ -683,6 +714,14 @@ 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"); + + /* + * We cannot upgrade if the old cluster file encryption method + * doesn't match the new one. + */ + if (oldctrl->file_encryption_method != newctrl->file_encryption_method) + pg_fatal("old and new clusters use different file encryption methods or\n" + "one cluster uses encryption and the other does not"); } diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c index 9b0cc16e45..ffa3017ad3 100644 --- a/src/bin/pg_upgrade/file.c +++ b/src/bin/pg_upgrade/file.c @@ -11,6 +11,7 @@ #include #include +#include #ifdef HAVE_COPYFILE_H #include #endif @@ -21,6 +22,7 @@ #include "access/visibilitymap.h" #include "common/file_perm.h" +#include "common/file_utils.h" #include "pg_upgrade.h" #include "storage/bufpage.h" #include "storage/checksum.h" diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c index 9c9b313e0c..db76534e68 100644 --- a/src/bin/pg_upgrade/option.c +++ b/src/bin/pg_upgrade/option.c @@ -52,6 +52,7 @@ parseCommandLine(int argc, char *argv[]) {"check", no_argument, NULL, 'c'}, {"link", no_argument, NULL, 'k'}, {"retain", no_argument, NULL, 'r'}, + {"authprompt", no_argument, NULL, 'R'}, {"jobs", required_argument, NULL, 'j'}, {"socketdir", required_argument, NULL, 's'}, {"verbose", no_argument, NULL, 'v'}, @@ -102,7 +103,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:ko:O:p:P:rRs:U:v", long_options, &optindex)) != -1) { switch (option) @@ -180,6 +181,10 @@ parseCommandLine(int argc, char *argv[]) log_opts.retain = true; break; + case 'R': + user_opts.pass_terminal_fd = true; + break; + case 's': user_opts.socketdir = pg_strdup(optarg); break; diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h index 919a7849fd..3fc8e299ad 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -11,6 +11,7 @@ #include #include "libpq-fe.h" +#include "common/kmgr_utils.h" /* Use port in the private/dynamic port number range */ #define DEF_PGUPORT 50432 @@ -219,6 +220,7 @@ typedef struct bool date_is_int; bool float8_pass_by_value; bool data_checksum_version; + int file_encryption_method; } ControlData; /* @@ -293,6 +295,7 @@ typedef struct int jobs; /* number of processes/threads to use */ char *socketdir; /* directory to use for Unix sockets */ bool ind_coll_unknown; /* mark unknown index collation versions */ + bool pass_terminal_fd; /* pass -R to pg_ctl? */ } UserOpts; typedef struct diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c index 7fed0ae108..f77ad24db2 100644 --- a/src/bin/pg_upgrade/server.c +++ b/src/bin/pg_upgrade/server.c @@ -245,8 +245,9 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error) * vacuumdb --freeze actually freezes the tuples. */ snprintf(cmd, sizeof(cmd), - "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start", - cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port, + "\"%s/pg_ctl\" -w%s -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start", + cluster->bindir, user_opts.pass_terminal_fd ? " -R" : "", + SERVER_LOG_FILE, cluster->pgconfig, cluster->port, (cluster->controldata.cat_ver >= BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" : " -c autovacuum=off -c autovacuum_freeze_max_age=2000000000", -- 2.20.1