From 36e4456d78b6a27cdaf82280b98998fc3bf3e9c7 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Fri, 21 Dec 2012 15:29:49 +0200 Subject: [PATCH] Fix race condition if a file is removed while pg_basebackup is running. If a relation file was removed when the server-side counterpart of pg_basebackup was just about to open it to send it to the client, you'd get a "could not open file" error. Fix that. Backpatch to 9.1, this goes back to when pg_basebackup was introduced. --- src/backend/replication/basebackup.c | 41 +++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index 65200c129a..dcc242773d 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -44,8 +44,8 @@ typedef struct static int64 sendDir(char *path, int basepathlen, bool sizeonly); -static void sendFile(char *readfilename, char *tarfilename, - struct stat * statbuf); +static bool sendFile(char *readfilename, char *tarfilename, + struct stat * statbuf, bool missing_ok); static void sendFileWithContent(const char *filename, const char *content); static void _tarWriteHeader(const char *filename, const char *linktarget, struct stat * statbuf); @@ -199,7 +199,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) XLOG_CONTROL_FILE))); } - sendFile(XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf); + sendFile(XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf, false); } /* @@ -712,11 +712,18 @@ sendDir(char *path, int basepathlen, bool sizeonly) } else if (S_ISREG(statbuf.st_mode)) { - /* Add size, rounded up to 512byte block */ - size += ((statbuf.st_size + 511) & ~511); + bool sent = false; + if (!sizeonly) - sendFile(pathbuf, pathbuf + basepathlen + 1, &statbuf); - size += 512; /* Size of the header of the file */ + sent = sendFile(pathbuf, pathbuf + basepathlen + 1, &statbuf, + true); + + if (sent || sizeonly) + { + /* Add size, rounded up to 512byte block */ + size += ((statbuf.st_size + 511) & ~511); + size += 512; /* Size of the header of the file */ + } } else ereport(WARNING, @@ -776,9 +783,17 @@ _tarChecksum(char *header) return sum; } -/* Given the member, write the TAR header & send the file */ -static void -sendFile(char *readfilename, char *tarfilename, struct stat * statbuf) +/* + * Given the member, write the TAR header & send the file. + * + * If 'missing_ok' is true, will not throw an error if the file is not found. + * + * Returns true if the file was successfully sent, false if 'missing_ok', + * and the file did not exist. + */ +static bool +sendFile(char *readfilename, char *tarfilename, struct stat *statbuf, + bool missing_ok) { FILE *fp; char buf[TAR_SEND_SIZE]; @@ -788,9 +803,13 @@ sendFile(char *readfilename, char *tarfilename, struct stat * statbuf) fp = AllocateFile(readfilename, "rb"); if (fp == NULL) + { + if (errno == ENOENT && missing_ok) + return false; ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", readfilename))); + } /* * Some compilers will throw a warning knowing this test can never be true @@ -844,6 +863,8 @@ sendFile(char *readfilename, char *tarfilename, struct stat * statbuf) } FreeFile(fp); + + return true; }