]> granicus.if.org Git - postgresql/commitdiff
Fix race condition if a file is removed while pg_basebackup is running.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 21 Dec 2012 13:29:49 +0000 (15:29 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 21 Dec 2012 13:29:49 +0000 (15:29 +0200)
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

index 1b234c64c9af3ad9013df86160e50552061744e4..bc95215457ff9182fdc653e6228aaf203a8a0780 100644 (file)
@@ -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);
@@ -194,7 +194,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);
                        }
 
                        /*
@@ -715,11 +715,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,
@@ -779,9 +786,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];
@@ -791,9 +806,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
@@ -847,6 +866,8 @@ sendFile(char *readfilename, char *tarfilename, struct stat * statbuf)
        }
 
        FreeFile(fp);
+
+       return true;
 }