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
This commit is contained in:
Michael Paquier 2019-03-12 10:03:33 +09:00
parent 1a83a80a2f
commit ce6afc6823
3 changed files with 101 additions and 42 deletions

View File

@ -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.
*

View File

@ -24,8 +24,10 @@
#include <sys/stat.h>
#include <fcntl.h>
#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
}

View File

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