1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-21 10:42:50 +03:00

pg_basebackup pg_receivexlog: Issue fsync more carefully

Several places weren't careful about fsyncing in the way.  See 1d4a0ab1
and 606e0f98 for details about required fsyncs.

This adds a couple of functions in src/common/ that have an equivalent
in the backend: durable_rename(), fsync_parent_path()

From: Michael Paquier <michael.paquier@gmail.com>
This commit is contained in:
Peter Eisentraut
2016-09-29 12:00:00 -04:00
parent bf5bb2e85b
commit bc34223bc1
4 changed files with 164 additions and 31 deletions

View File

@@ -34,7 +34,7 @@ static void pre_sync_fname(const char *fname, bool isdir,
const char *progname);
#endif
static void walkdir(const char *path,
void (*action) (const char *fname, bool isdir, const char *progname),
int (*action) (const char *fname, bool isdir, const char *progname),
bool process_symlinks, const char *progname);
/*
@@ -120,7 +120,7 @@ fsync_pgdata(const char *pg_data, const char *progname)
*/
static void
walkdir(const char *path,
void (*action) (const char *fname, bool isdir, const char *progname),
int (*action) (const char *fname, bool isdir, const char *progname),
bool process_symlinks, const char *progname)
{
DIR *dir;
@@ -228,7 +228,7 @@ pre_sync_fname(const char *fname, bool isdir, const char *progname)
* directories on systems where that isn't allowed/required. Reports
* other errors non-fatally.
*/
void
int
fsync_fname(const char *fname, bool isdir, const char *progname)
{
int fd;
@@ -256,10 +256,10 @@ fsync_fname(const char *fname, bool isdir, const char *progname)
if (fd < 0)
{
if (errno == EACCES || (isdir && errno == EISDIR))
return;
return 0;
fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
progname, fname, strerror(errno));
return;
return -1;
}
returncode = fsync(fd);
@@ -269,8 +269,103 @@ fsync_fname(const char *fname, bool isdir, const char *progname)
* those errors. Anything else needs to be reported.
*/
if (returncode != 0 && !(isdir && errno == EBADF))
{
fprintf(stderr, _("%s: could not fsync file \"%s\": %s\n"),
progname, fname, strerror(errno));
return -1;
}
(void) close(fd);
return 0;
}
/*
* fsync_parent_path -- fsync the parent path of a file or directory
*
* This is aimed at making file operations persistent on disk in case of
* an OS crash or power failure.
*/
int
fsync_parent_path(const char *fname, const char *progname)
{
char parentpath[MAXPGPATH];
strlcpy(parentpath, fname, MAXPGPATH);
get_parent_directory(parentpath);
/*
* get_parent_directory() returns an empty string if the input argument is
* just a file name (see comments in path.c), so handle that as being the
* current directory.
*/
if (strlen(parentpath) == 0)
strlcpy(parentpath, ".", MAXPGPATH);
if (fsync_fname(parentpath, true, progname) != 0)
return -1;
return 0;
}
/*
* durable_rename -- rename(2) wrapper, issuing fsyncs required for durability
*
* Wrapper around rename, similar to the backend version.
*/
int
durable_rename(const char *oldfile, const char *newfile, const char *progname)
{
int fd;
/*
* First fsync the old and target path (if it exists), to ensure that they
* are properly persistent on disk. Syncing the target file is not
* strictly necessary, but it makes it easier to reason about crashes;
* because it's then guaranteed that either source or target file exists
* after a crash.
*/
if (fsync_fname(oldfile, false, progname) != 0)
return -1;
fd = open(newfile, PG_BINARY | O_RDWR, 0);
if (fd < 0)
{
if (errno != ENOENT)
{
fprintf(stderr, _("%s: could not open file \"%s\": %s\n"),
progname, newfile, strerror(errno));
return -1;
}
}
else
{
if (fsync(fd) != 0)
{
fprintf(stderr, _("%s: could not fsync file \"%s\": %s\n"),
progname, newfile, strerror(errno));
close(fd);
return -1;
}
close(fd);
}
/* Time to do the real deal... */
if (rename(oldfile, newfile) != 0)
{
fprintf(stderr, _("%s: could not rename file \"%s\" to \"%s\": %s\n"),
progname, oldfile, newfile, strerror(errno));
return -1;
}
/*
* To guarantee renaming the file is persistent, fsync the file with its
* new name, and its containing directory.
*/
if (fsync_fname(newfile, false, progname) != 0)
return -1;
if (fsync_parent_path(newfile, progname) != 0)
return -1;
return 0;
}