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. See1d4a0ab1
and606e0f98
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:
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user