From ce6afc6823fbe2d83a5a77061b05864612b6bab2 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Tue, 12 Mar 2019 10:03:33 +0900 Subject: [PATCH] Add routine able to update the control file to src/common/ This adds a new routine to src/common/ which is compatible with both the frontend and backend code, able to update the control file's contents. This is now getting used only by pg_rewind, but some upcoming patches which add more control on checksums for offline instances will make use of it. This could also get used more by the backend as xlog.c has its own flavor of the same logic with some wait events and an additional flush phase before closing the opened file descriptor, but this is let as separate work. Author: Michael Banck, Michael Paquier Reviewed-by: Fabien Coelho, Sergei Kornilov Discussion: https://postgr.es/m/20181221201616.GD4974@nighthawk.caipicrew.dd-dns.de --- src/bin/pg_rewind/pg_rewind.c | 43 +----------- src/common/controldata_utils.c | 94 ++++++++++++++++++++++++++ src/include/common/controldata_utils.h | 6 +- 3 files changed, 101 insertions(+), 42 deletions(-) diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c index aa753bb315..7f1d6bf48a 100644 --- a/src/bin/pg_rewind/pg_rewind.c +++ b/src/bin/pg_rewind/pg_rewind.c @@ -24,6 +24,7 @@ #include "access/xlog_internal.h" #include "catalog/catversion.h" #include "catalog/pg_control.h" +#include "common/controldata_utils.h" #include "common/file_perm.h" #include "common/file_utils.h" #include "common/restricted_token.h" @@ -37,7 +38,6 @@ static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, static void digestControlFile(ControlFileData *ControlFile, char *source, size_t size); -static void updateControlFile(ControlFileData *ControlFile); static void syncTargetDirectory(void); static void sanityChecks(void); static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex); @@ -377,7 +377,7 @@ main(int argc, char **argv) ControlFile_new.minRecoveryPoint = endrec; ControlFile_new.minRecoveryPointTLI = endtli; ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY; - updateControlFile(&ControlFile_new); + update_controlfile(datadir_target, progname, &ControlFile_new); pg_log(PG_PROGRESS, "syncing target data directory\n"); syncTargetDirectory(); @@ -666,45 +666,6 @@ digestControlFile(ControlFileData *ControlFile, char *src, size_t size) checkControlFile(ControlFile); } -/* - * Update the target's control file. - */ -static void -updateControlFile(ControlFileData *ControlFile) -{ - char buffer[PG_CONTROL_FILE_SIZE]; - - /* - * For good luck, apply the same static assertions as in backend's - * WriteControlFile(). - */ - StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_MAX_SAFE_SIZE, - "pg_control is too large for atomic disk writes"); - StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_FILE_SIZE, - "sizeof(ControlFileData) exceeds PG_CONTROL_FILE_SIZE"); - - /* Recalculate CRC of control file */ - INIT_CRC32C(ControlFile->crc); - COMP_CRC32C(ControlFile->crc, - (char *) ControlFile, - offsetof(ControlFileData, crc)); - FIN_CRC32C(ControlFile->crc); - - /* - * Write out PG_CONTROL_FILE_SIZE bytes into pg_control by zero-padding - * the excess over sizeof(ControlFileData), to avoid premature EOF related - * errors when reading it. - */ - memset(buffer, 0, PG_CONTROL_FILE_SIZE); - memcpy(buffer, ControlFile, sizeof(ControlFileData)); - - open_target_file("global/pg_control", false); - - write_target_range(buffer, 0, PG_CONTROL_FILE_SIZE); - - close_target_file(); -} - /* * Sync target data directory to ensure that modifications are safely on disk. * diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c index 6289a4343a..71e67a2eda 100644 --- a/src/common/controldata_utils.c +++ b/src/common/controldata_utils.c @@ -24,8 +24,10 @@ #include #include +#include "access/xlog_internal.h" #include "catalog/pg_control.h" #include "common/controldata_utils.h" +#include "common/file_perm.h" #include "port/pg_crc32c.h" #ifndef FRONTEND #include "storage/fd.h" @@ -137,3 +139,95 @@ get_controlfile(const char *DataDir, const char *progname, bool *crc_ok_p) return ControlFile; } + +/* + * update_controlfile() + * + * Update controlfile values with the contents given by caller. The + * contents to write are included in "ControlFile". Note that it is up + * to the caller to fsync the updated file, and to properly lock + * ControlFileLock when calling this routine in the backend. + */ +void +update_controlfile(const char *DataDir, const char *progname, + ControlFileData *ControlFile) +{ + int fd; + char buffer[PG_CONTROL_FILE_SIZE]; + char ControlFilePath[MAXPGPATH]; + + /* + * Apply the same static assertions as in backend's WriteControlFile(). + */ + StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_MAX_SAFE_SIZE, + "pg_control is too large for atomic disk writes"); + StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_FILE_SIZE, + "sizeof(ControlFileData) exceeds PG_CONTROL_FILE_SIZE"); + + /* Recalculate CRC of control file */ + INIT_CRC32C(ControlFile->crc); + COMP_CRC32C(ControlFile->crc, + (char *) ControlFile, + offsetof(ControlFileData, crc)); + FIN_CRC32C(ControlFile->crc); + + /* + * Write out PG_CONTROL_FILE_SIZE bytes into pg_control by zero-padding + * the excess over sizeof(ControlFileData), to avoid premature EOF related + * errors when reading it. + */ + memset(buffer, 0, PG_CONTROL_FILE_SIZE); + memcpy(buffer, ControlFile, sizeof(ControlFileData)); + + snprintf(ControlFilePath, sizeof(ControlFilePath), "%s/%s", DataDir, XLOG_CONTROL_FILE); + +#ifndef FRONTEND + if ((fd = OpenTransientFile(ControlFilePath, O_WRONLY | PG_BINARY)) == -1) + ereport(PANIC, + (errcode_for_file_access(), + errmsg("could not open file \"%s\": %m", + ControlFilePath))); +#else + if ((fd = open(ControlFilePath, O_WRONLY | PG_BINARY, + pg_file_create_mode)) == -1) + { + fprintf(stderr, _("%s: could not open file \"%s\": %s\n"), + progname, ControlFilePath, strerror(errno)); + exit(EXIT_FAILURE); + } +#endif + + errno = 0; + if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE) + { + /* if write didn't set errno, assume problem is no disk space */ + if (errno == 0) + errno = ENOSPC; + +#ifndef FRONTEND + ereport(PANIC, + (errcode_for_file_access(), + errmsg("could not write file \"%s\": %m", + ControlFilePath))); +#else + fprintf(stderr, _("%s: could not write \"%s\": %s\n"), + progname, ControlFilePath, strerror(errno)); + exit(EXIT_FAILURE); +#endif + } + +#ifndef FRONTEND + if (CloseTransientFile(fd)) + ereport(PANIC, + (errcode_for_file_access(), + errmsg("could not close file \"%s\": %m", + ControlFilePath))); +#else + if (close(fd) < 0) + { + fprintf(stderr, _("%s: could not close file \"%s\": %s\n"), + progname, ControlFilePath, strerror(errno)); + exit(EXIT_FAILURE); + } +#endif +} diff --git a/src/include/common/controldata_utils.h b/src/include/common/controldata_utils.h index 0ffa2000fc..95317ebacf 100644 --- a/src/include/common/controldata_utils.h +++ b/src/include/common/controldata_utils.h @@ -12,6 +12,10 @@ #include "catalog/pg_control.h" -extern ControlFileData *get_controlfile(const char *DataDir, const char *progname, bool *crc_ok_p); +extern ControlFileData *get_controlfile(const char *DataDir, + const char *progname, + bool *crc_ok_p); +extern void update_controlfile(const char *DataDir, const char *progname, + ControlFileData *ControlFile); #endif /* COMMON_CONTROLDATA_UTILS_H */ -- 2.40.0