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 */