diff options
author | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2012-12-21 15:29:49 +0200 |
---|---|---|
committer | Heikki Linnakangas <heikki.linnakangas@iki.fi> | 2012-12-21 15:33:38 +0200 |
commit | 14aa55df29ea69e453be4c46f8546d49365fc06b (patch) | |
tree | ba79ae07a8fb41bce131d010be8ba1533f10d176 | |
parent | 17a71067d03edfc7a0eddb5bff42f48fb1855d82 (diff) | |
download | postgresql-14aa55df29ea69e453be4c46f8546d49365fc06b.tar.gz postgresql-14aa55df29ea69e453be4c46f8546d49365fc06b.zip |
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.
-rw-r--r-- | src/backend/replication/basebackup.c | 39 |
1 files changed, 30 insertions, 9 deletions
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index 41cab5b0ffa..610a799400c 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -43,8 +43,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); @@ -692,11 +692,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, @@ -756,9 +763,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]; @@ -768,9 +783,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 @@ -824,6 +843,8 @@ sendFile(char *readfilename, char *tarfilename, struct stat * statbuf) } FreeFile(fp); + + return true; } |