From 5e45211a64149b3c659b90ff2de6fa982a5a93ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:17:33 +0200 Subject: Adding upstream version 15.5. Signed-off-by: Daniel Baumann --- src/bin/pg_basebackup/bbstreamer_inject.c | 249 ++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 src/bin/pg_basebackup/bbstreamer_inject.c (limited to 'src/bin/pg_basebackup/bbstreamer_inject.c') diff --git a/src/bin/pg_basebackup/bbstreamer_inject.c b/src/bin/pg_basebackup/bbstreamer_inject.c new file mode 100644 index 0000000..cc804f1 --- /dev/null +++ b/src/bin/pg_basebackup/bbstreamer_inject.c @@ -0,0 +1,249 @@ +/*------------------------------------------------------------------------- + * + * bbstreamer_inject.c + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/bin/pg_basebackup/bbstreamer_inject.c + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include "bbstreamer.h" +#include "common/file_perm.h" +#include "common/logging.h" + +typedef struct bbstreamer_recovery_injector +{ + bbstreamer base; + bool skip_file; + bool is_recovery_guc_supported; + bool is_postgresql_auto_conf; + bool found_postgresql_auto_conf; + PQExpBuffer recoveryconfcontents; + bbstreamer_member member; +} bbstreamer_recovery_injector; + +static void bbstreamer_recovery_injector_content(bbstreamer *streamer, + bbstreamer_member *member, + const char *data, int len, + bbstreamer_archive_context context); +static void bbstreamer_recovery_injector_finalize(bbstreamer *streamer); +static void bbstreamer_recovery_injector_free(bbstreamer *streamer); + +const bbstreamer_ops bbstreamer_recovery_injector_ops = { + .content = bbstreamer_recovery_injector_content, + .finalize = bbstreamer_recovery_injector_finalize, + .free = bbstreamer_recovery_injector_free +}; + +/* + * Create a bbstreamer that can edit recoverydata into an archive stream. + * + * The input should be a series of typed chunks (not BBSTREAMER_UNKNOWN) as + * per the conventions described in bbstreamer.h; the chunks forwarded to + * the next bbstreamer will be similarly typed, but the + * BBSTREAMER_MEMBER_HEADER chunks may be zero-length in cases where we've + * edited the archive stream. + * + * Our goal is to do one of the following three things with the content passed + * via recoveryconfcontents: (1) if is_recovery_guc_supported is false, then + * put the content into recovery.conf, replacing any existing archive member + * by that name; (2) if is_recovery_guc_supported is true and + * postgresql.auto.conf exists in the archive, then append the content + * provided to the existing file; and (3) if is_recovery_guc_supported is + * true but postgresql.auto.conf does not exist in the archive, then create + * it with the specified content. + * + * In addition, if is_recovery_guc_supported is true, then we create a + * zero-length standby.signal file, dropping any file with that name from + * the archive. + */ +extern bbstreamer * +bbstreamer_recovery_injector_new(bbstreamer *next, + bool is_recovery_guc_supported, + PQExpBuffer recoveryconfcontents) +{ + bbstreamer_recovery_injector *streamer; + + streamer = palloc0(sizeof(bbstreamer_recovery_injector)); + *((const bbstreamer_ops **) &streamer->base.bbs_ops) = + &bbstreamer_recovery_injector_ops; + streamer->base.bbs_next = next; + streamer->is_recovery_guc_supported = is_recovery_guc_supported; + streamer->recoveryconfcontents = recoveryconfcontents; + + return &streamer->base; +} + +/* + * Handle each chunk of tar content while injecting recovery configuration. + */ +static void +bbstreamer_recovery_injector_content(bbstreamer *streamer, + bbstreamer_member *member, + const char *data, int len, + bbstreamer_archive_context context) +{ + bbstreamer_recovery_injector *mystreamer; + + mystreamer = (bbstreamer_recovery_injector *) streamer; + Assert(member != NULL || context == BBSTREAMER_ARCHIVE_TRAILER); + + switch (context) + { + case BBSTREAMER_MEMBER_HEADER: + /* Must copy provided data so we have the option to modify it. */ + memcpy(&mystreamer->member, member, sizeof(bbstreamer_member)); + + /* + * On v12+, skip standby.signal and edit postgresql.auto.conf; on + * older versions, skip recovery.conf. + */ + if (mystreamer->is_recovery_guc_supported) + { + mystreamer->skip_file = + (strcmp(member->pathname, "standby.signal") == 0); + mystreamer->is_postgresql_auto_conf = + (strcmp(member->pathname, "postgresql.auto.conf") == 0); + if (mystreamer->is_postgresql_auto_conf) + { + /* Remember we saw it so we don't add it again. */ + mystreamer->found_postgresql_auto_conf = true; + + /* Increment length by data to be injected. */ + mystreamer->member.size += + mystreamer->recoveryconfcontents->len; + + /* + * Zap data and len because the archive header is no + * longer valid; some subsequent bbstreamer must + * regenerate it if it's necessary. + */ + data = NULL; + len = 0; + } + } + else + mystreamer->skip_file = + (strcmp(member->pathname, "recovery.conf") == 0); + + /* Do not forward if the file is to be skipped. */ + if (mystreamer->skip_file) + return; + break; + + case BBSTREAMER_MEMBER_CONTENTS: + /* Do not forward if the file is to be skipped. */ + if (mystreamer->skip_file) + return; + break; + + case BBSTREAMER_MEMBER_TRAILER: + /* Do not forward it the file is to be skipped. */ + if (mystreamer->skip_file) + return; + + /* Append provided content to whatever we already sent. */ + if (mystreamer->is_postgresql_auto_conf) + bbstreamer_content(mystreamer->base.bbs_next, member, + mystreamer->recoveryconfcontents->data, + mystreamer->recoveryconfcontents->len, + BBSTREAMER_MEMBER_CONTENTS); + break; + + case BBSTREAMER_ARCHIVE_TRAILER: + if (mystreamer->is_recovery_guc_supported) + { + /* + * If we didn't already find (and thus modify) + * postgresql.auto.conf, inject it as an additional archive + * member now. + */ + if (!mystreamer->found_postgresql_auto_conf) + bbstreamer_inject_file(mystreamer->base.bbs_next, + "postgresql.auto.conf", + mystreamer->recoveryconfcontents->data, + mystreamer->recoveryconfcontents->len); + + /* Inject empty standby.signal file. */ + bbstreamer_inject_file(mystreamer->base.bbs_next, + "standby.signal", "", 0); + } + else + { + /* Inject recovery.conf file with specified contents. */ + bbstreamer_inject_file(mystreamer->base.bbs_next, + "recovery.conf", + mystreamer->recoveryconfcontents->data, + mystreamer->recoveryconfcontents->len); + } + + /* Nothing to do here. */ + break; + + default: + /* Shouldn't happen. */ + pg_fatal("unexpected state while injecting recovery settings"); + } + + bbstreamer_content(mystreamer->base.bbs_next, &mystreamer->member, + data, len, context); +} + +/* + * End-of-stream processing for this bbstreamer. + */ +static void +bbstreamer_recovery_injector_finalize(bbstreamer *streamer) +{ + bbstreamer_finalize(streamer->bbs_next); +} + +/* + * Free memory associated with this bbstreamer. + */ +static void +bbstreamer_recovery_injector_free(bbstreamer *streamer) +{ + bbstreamer_free(streamer->bbs_next); + pfree(streamer); +} + +/* + * Inject a member into the archive with specified contents. + */ +void +bbstreamer_inject_file(bbstreamer *streamer, char *pathname, char *data, + int len) +{ + bbstreamer_member member; + + strlcpy(member.pathname, pathname, MAXPGPATH); + member.size = len; + member.mode = pg_file_create_mode; + member.is_directory = false; + member.is_link = false; + member.linktarget[0] = '\0'; + + /* + * There seems to be no principled argument for these values, but they are + * what PostgreSQL has historically used. + */ + member.uid = 04000; + member.gid = 02000; + + /* + * We don't know here how to generate valid member headers and trailers + * for the archiving format in use, so if those are needed, some successor + * bbstreamer will have to generate them using the data from 'member'. + */ + bbstreamer_content(streamer, &member, NULL, 0, + BBSTREAMER_MEMBER_HEADER); + bbstreamer_content(streamer, &member, data, len, + BBSTREAMER_MEMBER_CONTENTS); + bbstreamer_content(streamer, &member, NULL, 0, + BBSTREAMER_MEMBER_TRAILER); +} -- cgit v1.2.3