diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index c0e4ca50899..e4a4478af75 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -74,6 +74,7 @@ #include "pg_trace.h" #include "pgstat.h" #include "port/atomics.h" +#include "port/pg_crc32c.h" #include "port/pg_iovec.h" #include "postmaster/bgwriter.h" #include "postmaster/startup.h" @@ -8691,6 +8692,21 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive) "and should not be used. " "Try taking another online backup."))); + /* + * Create a copy of control data to be stored in the backup label. + * Recalculate the CRC so recovery can validate the contents but do not + * bother with the timestamp since that will be applied before it is + * written by recovery. + */ + LWLockAcquire(ControlFileLock, LW_SHARED); + state->controlFile = *ControlFile; + LWLockRelease(ControlFileLock); + + INIT_CRC32C(state->controlFile.crc); + COMP_CRC32C(state->controlFile.crc, (char *)&state->controlFile, + offsetof(ControlFileData, crc)); + FIN_CRC32C(state->controlFile.crc); + /* * During recovery, we don't write an end-of-backup record. We assume that * pg_control was backed up last and its minimum recovery point can be @@ -8741,11 +8757,8 @@ do_pg_backup_stop(BackupState *state, bool waitforarchive) "Enable full_page_writes and run CHECKPOINT on the primary, " "and then try an online backup again."))); - - LWLockAcquire(ControlFileLock, LW_SHARED); - state->stoppoint = ControlFile->minRecoveryPoint; - state->stoptli = ControlFile->minRecoveryPointTLI; - LWLockRelease(ControlFileLock); + state->stoppoint = state->controlFile.minRecoveryPoint; + state->stoptli = state->controlFile.minRecoveryPointTLI; } else { diff --git a/src/backend/access/transam/xlogbackup.c b/src/backend/access/transam/xlogbackup.c index 21d68133ae1..79559d78acb 100644 --- a/src/backend/access/transam/xlogbackup.c +++ b/src/backend/access/transam/xlogbackup.c @@ -16,6 +16,7 @@ #include "access/xlog.h" #include "access/xlog_internal.h" #include "access/xlogbackup.h" +#include "common/base64.h" /* * Build contents for backup_label or backup history file. @@ -76,6 +77,17 @@ build_backup_content(BackupState *state, bool ishistoryfile) appendStringInfo(result, "STOP TIME: %s\n", stopstrfbuf); appendStringInfo(result, "STOP TIMELINE: %u\n", state->stoptli); } + /* Include a copy of control data */ + else + { + char controlbuf[((sizeof(ControlFileData) + 2) / 3 * 4) + 1]; + + pg_b64_encode((char *)&state->controlFile, sizeof(ControlFileData), + controlbuf, sizeof(controlbuf) - 1); + controlbuf[sizeof(controlbuf) - 1] = '\0'; + + appendStringInfo(result, "CONTROL DATA: %s\n", controlbuf); + } data = result->data; pfree(result); diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c index becc2bda62e..1abd9494bf0 100644 --- a/src/backend/access/transam/xlogrecovery.c +++ b/src/backend/access/transam/xlogrecovery.c @@ -43,6 +43,7 @@ #include "backup/basebackup.h" #include "catalog/pg_control.h" #include "commands/tablespace.h" +#include "common/base64.h" #include "common/file_utils.h" #include "miscadmin.h" #include "pgstat.h" @@ -390,7 +391,8 @@ static void readRecoverySignalFile(void); static void validateRecoveryParameters(void); static bool read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI, - bool *backupEndRequired, bool *backupFromStandby); + bool *backupEndRequired, bool *backupFromStandby, + ControlFileData *ControlFile); static bool read_tablespace_map(List **tablespaces); static void xlogrecovery_redo(XLogReaderState *record, TimeLineID replayTLI); @@ -610,7 +612,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr, primary_image_masked = (char *) palloc(BLCKSZ); if (read_backup_label(&CheckPointLoc, &CheckPointTLI, &backupEndRequired, - &backupFromStandby)) + &backupFromStandby, ControlFile)) { List *tablespaces = NIL; @@ -1167,7 +1169,8 @@ validateRecoveryParameters(void) */ static bool read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI, - bool *backupEndRequired, bool *backupFromStandby) + bool *backupEndRequired, bool *backupFromStandby, + ControlFileData *ControlFile) { char startxlogfilename[MAXFNAMELEN]; TimeLineID tli_from_walseg, @@ -1180,6 +1183,7 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI, char backuptime[128]; uint32 hi, lo; + char controlB64Buf[684 + 1]; /* suppress possible uninitialized-variable warnings */ *checkPointLoc = InvalidXLogRecPtr; @@ -1285,6 +1289,51 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI, tli_from_file, BACKUP_LABEL_FILE))); } + /* + * Read control data to be used to create pg_control. Control data may not + * exist if the backup was made with an older version, in which case the + * control file read from disk will be used. + */ + if (fscanf(lfp, "CONTROL DATA: %684s\n", controlB64Buf) == 1) + { + ControlFileData controlBuf; + pg_crc32c crc; + + /* Check that the base64 data is the correct length */ + if (strlen(controlB64Buf) != (sizeof(ControlFileData) + 2) / 3 * 4) + ereport(FATAL, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE), + errdetail("Control data expected %zu base64 characters.", + (sizeof(ControlFileData) + 2) / 3 * 4))); + + /* Decode control file */ + if (pg_b64_decode(controlB64Buf, strlen(controlB64Buf), (char *)&controlBuf, + sizeof(ControlFileData)) == -1) + ereport(FATAL, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE), + errdetail("Control data contains invalid base64 encoding."))); + + /* CRC check on stored control file */ + INIT_CRC32C(crc); + COMP_CRC32C(crc, (char *)&controlBuf, offsetof(ControlFileData, crc)); + FIN_CRC32C(crc); + + if (!EQ_CRC32C(crc, controlBuf.crc)) + ereport(FATAL, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE), + errdetail("Control data contains invalid CRC."))); + + ereport(DEBUG1, + (errmsg_internal("backup control data in file \"%s\"", + BACKUP_LABEL_FILE))); + + /* Copy control data */ + memcpy(ControlFile, &controlBuf, sizeof(ControlFileData)); + } + if (ferror(lfp) || FreeFile(lfp)) ereport(FATAL, (errcode_for_file_access(), diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c index 7d025bcf382..f49dabf63ef 100644 --- a/src/backend/backup/basebackup.c +++ b/src/backend/backup/basebackup.c @@ -330,13 +330,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink) bbsink_begin_archive(sink, "base.tar"); - /* In the main tar, include the backup_label first... */ - backup_label = build_backup_content(backup_state, false); - sendFileWithContent(sink, BACKUP_LABEL_FILE, - backup_label, &manifest); - pfree(backup_label); - - /* Then the tablespace_map file, if required... */ + /* Send the tablespace_map file, if required... */ if (opt->sendtblspcmapfile) { sendFileWithContent(sink, TABLESPACE_MAP, @@ -348,7 +342,7 @@ perform_base_backup(basebackup_options *opt, bbsink *sink) sendDir(sink, ".", 1, false, state.tablespaces, sendtblspclinks, &manifest, NULL); - /* ... and pg_control after everything else. */ + /* ... and pg_control after everything but backup_label */ if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0) ereport(ERROR, (errcode_for_file_access(), @@ -356,6 +350,16 @@ perform_base_backup(basebackup_options *opt, bbsink *sink) XLOG_CONTROL_FILE))); sendFile(sink, XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf, false, InvalidOid, &manifest, NULL); + + /* End the backup before sending backup_label */ + basebackup_progress_wait_wal_archive(&state); + do_pg_backup_stop(backup_state, !opt->nowait); + + /* Last in the main tar, include backup_label */ + backup_label = build_backup_content(backup_state, false); + sendFileWithContent(sink, BACKUP_LABEL_FILE, + backup_label, &manifest); + pfree(backup_label); } else { @@ -389,9 +393,6 @@ perform_base_backup(basebackup_options *opt, bbsink *sink) } } - basebackup_progress_wait_wal_archive(&state); - do_pg_backup_stop(backup_state, !opt->nowait); - endptr = backup_state->stoppoint; endtli = backup_state->stoptli; diff --git a/src/include/access/xlogbackup.h b/src/include/access/xlogbackup.h index 1611358137b..bd53405670e 100644 --- a/src/include/access/xlogbackup.h +++ b/src/include/access/xlogbackup.h @@ -15,6 +15,7 @@ #define XLOG_BACKUP_H #include "access/xlogdefs.h" +#include "catalog/pg_control.h" #include "pgtime.h" /* Structure to hold backup state. */ @@ -33,6 +34,7 @@ typedef struct BackupState XLogRecPtr stoppoint; /* backup stop WAL location */ TimeLineID stoptli; /* backup stop TLI */ pg_time_t stoptime; /* backup stop time */ + ControlFileData controlFile; /* Copy of control data */ } BackupState; extern char *build_backup_content(BackupState *state,