From e60810874bb68af7a84cebdb8ba1674e2ce2239f Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 25 Jun 2021 16:56:56 -0400 Subject: [PATCH 07/12] 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 | 125 ++++++++++++++++++++++-- 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 | 4 + src/bin/pg_upgrade/server.c | 2 +- 15 files changed, 361 insertions(+), 21 deletions(-) diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml index 8158896298..06b10fbaaf 100644 --- a/doc/src/sgml/ref/initdb.sgml +++ b/doc/src/sgml/ref/initdb.sgml @@ -186,6 +186,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. + + + + @@ -257,6 +269,18 @@ PostgreSQL documentation + + + + + + Specifies the cluster file encryption method. The + value values are AES128 (the default), AES192, and AES256. + + + + @@ -344,6 +368,17 @@ PostgreSQL documentation + + + + + + Allows the command + to prompt for a passphrase or PIN. + + + + @@ -369,6 +404,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 46906966eb..e3705c724e 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 8f7a3025c3..4362e551c9 100644 --- a/doc/src/sgml/ref/pgupgrade.sgml +++ b/doc/src/sgml/ref/pgupgrade.sgml @@ -182,6 +182,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. @@ -842,6 +853,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 55a3f6c69d..f0fa52ebdc 100644 --- a/doc/src/sgml/ref/postgres-ref.sgml +++ b/doc/src/sgml/ref/postgres-ref.sgml @@ -305,6 +305,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 f61a043055..8c7e1e39c5 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -68,6 +68,7 @@ #include "catalog/pg_database_d.h" /* pgrminclude ignore */ #include "common/file_perm.h" #include "common/file_utils.h" +#include "common/kmgr_utils.h" #include "common/logging.h" #include "common/pg_prng.h" #include "common/restricted_token.h" @@ -147,11 +148,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 */ @@ -214,6 +220,7 @@ static const char *const subdirs[] = { "global", "pg_wal/archive_status", "pg_commit_ts", + "pg_cryptokeys", "pg_dynshmem", "pg_notify", "pg_serial", @@ -904,12 +911,13 @@ test_config_settings(void) test_buffs = MIN_BUFS_FOR_CONNS(test_conns); snprintf(cmd, sizeof(cmd), - "\"%s\" --check %s %s " + "\"%s\" --check -x0 %s %s %s " "-c max_connections=%d " "-c shared_buffers=%d " "-c dynamic_shared_memory_type=%s " "< \"%s\" > \"%s\" 2>&1", backend_exec, boot_options, extra_options, + term_fd_opt ? term_fd_opt : "", test_conns, test_buffs, dynamic_shared_memory_type, DEVNULL, DEVNULL); @@ -941,12 +949,13 @@ test_config_settings(void) } snprintf(cmd, sizeof(cmd), - "\"%s\" --check %s %s " + "\"%s\" --check %s %s %s " "-c max_connections=%d " "-c shared_buffers=%d " "-c dynamic_shared_memory_type=%s " "< \"%s\" > \"%s\" 2>&1", backend_exec, boot_options, extra_options, + term_fd_opt ? term_fd_opt : "", n_connections, test_buffs, dynamic_shared_memory_type, DEVNULL, DEVNULL); @@ -1133,6 +1142,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 @@ -1321,11 +1337,17 @@ bootstrap_template1(void) unsetenv("PGCLIENTENCODING"); snprintf(cmd, sizeof(cmd), - "\"%s\" --boot -X %d %s %s %s %s", + "\"%s\" --boot -X %d %s %s %s %s %s %s %s %s %s", backend_exec, wal_segment_size_mb * (1024 * 1024), data_checksums ? "-k" : "", - boot_options, extra_options, + 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, + extra_options, + term_fd_opt ? term_fd_opt : "", debug ? "-d 5" : ""); @@ -2159,18 +2181,26 @@ usage(const char *progname) 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(_(" --discard-caches set debug_discard_caches=1\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 database files to disk, then exit\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")); @@ -2700,6 +2730,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(); @@ -2723,8 +2770,9 @@ initialize_data_directory(void) fflush(stdout); snprintf(cmd, sizeof(cmd), - "\"%s\" %s %s template1 >%s", + "\"%s\" %s %s %s template1 >%s", backend_exec, backend_options, extra_options, + term_fd_opt ? term_fd_opt : "", DEVNULL); PG_CMD_OPEN; @@ -2802,10 +2850,14 @@ 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'}, {"discard-caches", no_argument, NULL, 14}, {"locale-provider", required_argument, NULL, 15}, {"icu-locale", required_argument, NULL, 16}, + {"cluster-key-command", required_argument, NULL, 'c'}, + {"copy-encryption-keys", required_argument, NULL, 'u'}, {NULL, 0, NULL, 0} }; @@ -2847,7 +2899,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) { @@ -2893,6 +2945,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; @@ -2929,6 +3003,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; @@ -3014,6 +3094,32 @@ main(int argc, char *argv[]) if (pwprompt && pwfilename) pg_fatal("password prompt and password file cannot be specified together"); +#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); @@ -3072,6 +3178,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 c390ec51ce..d25d13c992 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 a509fc9109..d90c07db10 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -74,6 +74,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 */ @@ -439,7 +440,7 @@ free_readfile(char **optlines) static pid_t start_postmaster(void) { - char *cmd; + char *cmd, *term_fd_opt = NULL; #ifndef WIN32 pid_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) - cmd = psprintf("exec \"%s\" %s%s < \"%s\" >> \"%s\" 2>&1", + cmd = psprintf("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 - cmd = psprintf("exec \"%s\" %s%s < \"%s\" 2>&1", - exec_path, pgdata_opt, post_opts, DEVNULL); + cmd = psprintf("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); - cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"", - comspec, exec_path, pgdata_opt, post_opts, DEVNULL, log_file); + cmd = psprintf("\"%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 - cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" 2>&1\"", - comspec, exec_path, pgdata_opt, post_opts, DEVNULL); + cmd = psprintf("\"%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_start(pid_t pm_pid, bool do_checkpoint) } else #endif - print_msg("."); + if (!pass_terminal_fd) + print_msg("."); } pg_usleep(USEC_PER_SEC / WAITS_PER_SEC); @@ -2005,6 +2039,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")); @@ -2200,6 +2235,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'}, @@ -2272,7 +2308,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) @@ -2324,6 +2360,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 089063f471..73cb048b3e 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" @@ -772,6 +773,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 269ed6446e..d0b71f972a 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" @@ -106,6 +107,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 f1bc1e6886..ae7aa601a7 100644 --- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -11,6 +11,7 @@ #include "catalog/pg_authid_d.h" #include "catalog/pg_collation.h" +#include "common/kmgr_utils.h" #include "fe_utils/string_utils.h" #include "mb/pg_wchar.h" #include "pg_upgrade.h" @@ -30,6 +31,7 @@ static void check_for_composite_data_type_usage(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); @@ -160,6 +162,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); @@ -190,6 +195,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: @@ -1466,6 +1474,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 018cd310f7..ee6a48438b 100644 --- a/src/bin/pg_upgrade/controldata.c +++ b/src/bin/pg_upgrade/controldata.c @@ -9,12 +9,18 @@ #include "postgres_fe.h" +#include #include #include "pg_upgrade.h" #include "common/string.h" +#include "access/xlog_internal.h" +#include "common/controldata_utils.h" +#include "common/file_utils.h" +#include "common/kmgr_utils.h" + /* * get_control_data() * @@ -62,6 +68,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; @@ -203,6 +210,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)) { @@ -498,6 +512,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); @@ -566,7 +592,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, @@ -635,6 +662,10 @@ get_control_data(ClusterInfo *cluster, bool live_check) if (!got_data_checksum_version) pg_log(PG_REPORT, " data checksum version"); + /* 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"); } } @@ -699,6 +730,14 @@ check_control_data(ControlData *oldctrl, pg_fatal("old cluster uses data checksums but the new one does not"); else if (oldctrl->data_checksum_version != newctrl->data_checksum_version) pg_fatal("old and new cluster pg_controldata checksum versions do not match"); + + /* + * 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 079fbda838..29fd003318 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/visibilitymapdefs.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 3f719f1762..b6285d68ed 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'}, @@ -99,7 +100,7 @@ parseCommandLine(int argc, char *argv[]) if (os_user_effective_id == 0) pg_fatal("%s: cannot be run as root", os_info.progname); - while ((option = getopt_long(argc, argv, "d:D:b:B:cj:kNo:O:p:P:rs:U:v", + while ((option = getopt_long(argc, argv, "d:D:b:B:cj:kNo:O:p:P:rRs:U:v", long_options, &optindex)) != -1) { switch (option) @@ -176,6 +177,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 31589b0fdc..878f8e8032 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -12,6 +12,7 @@ #include "common/relpath.h" #include "libpq-fe.h" +#include "common/kmgr_utils.h" /* For now, pg_upgrade does not use common/logging.c; use our own pg_fatal */ #undef pg_fatal @@ -218,6 +219,7 @@ typedef struct bool date_is_int; bool float8_pass_by_value; uint32 data_checksum_version; + int file_encryption_method; } ControlData; /* @@ -296,6 +298,8 @@ typedef struct transferMode transfer_mode; /* copy files or link them? */ 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 9a1a20fb27..0775010379 100644 --- a/src/bin/pg_upgrade/server.c +++ b/src/bin/pg_upgrade/server.c @@ -239,7 +239,7 @@ 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/%s\" -D \"%s\" -o \"-p %d -b%s %s%s\" start", + "\"%s/pg_ctl\" -w -l \"%s/%s\" -D \"%s\" -o \"-p %d%s -b %s%s\" start", cluster->bindir, log_opts.logdir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port, -- 2.37.0 (Apple Git-136)